diff --git a/backend/server/apps/kea/statisticscounter.go b/backend/server/apps/kea/statisticscounter.go index 5d49a85f1..4396c77ff 100644 --- a/backend/server/apps/kea/statisticscounter.go +++ b/backend/server/apps/kea/statisticscounter.go @@ -50,26 +50,26 @@ func newGlobalStats() *globalStats { // Add the IPv4 subnet statistics to the global state. func (g *globalStats) addIPv4Subnet(subnet *subnetIPv4Stats) { - g.totalIPv4Addresses.AddUint64(subnet.totalAddresses) - g.totalIPv4AddressesInPools.AddUint64(subnet.totalAddressesInPools) - g.totalAssignedIPv4Addresses.AddUint64(subnet.totalAssignedAddresses) - g.totalAssignedIPv4AddressesInPools.AddUint64(subnet.totalAssignedAddressesInPools) - g.totalDeclinedIPv4Addresses.AddUint64(subnet.totalDeclinedAddresses) - g.totalDeclinedIPv4AddressesInPools.AddUint64(subnet.totalDeclinedAddressesInPools) + g.totalIPv4Addresses.AddUint64(g.totalIPv4Addresses, subnet.totalAddresses) + g.totalIPv4AddressesInPools.AddUint64(g.totalIPv4AddressesInPools, subnet.totalAddressesInPools) + g.totalAssignedIPv4Addresses.AddUint64(g.totalAssignedIPv4Addresses, subnet.totalAssignedAddresses) + g.totalAssignedIPv4AddressesInPools.AddUint64(g.totalAssignedIPv4AddressesInPools, subnet.totalAssignedAddressesInPools) + g.totalDeclinedIPv4Addresses.AddUint64(g.totalDeclinedIPv4Addresses, subnet.totalDeclinedAddresses) + g.totalDeclinedIPv4AddressesInPools.AddUint64(g.totalDeclinedIPv4AddressesInPools, subnet.totalDeclinedAddressesInPools) } // Add the IPv6 subnet statistics to the global state. func (g *globalStats) addIPv6Subnet(subnet *subnetIPv6Stats) { - g.totalIPv6Addresses.Add(subnet.totalAddresses) - g.totalIPv6AddressesInPools.Add(subnet.totalAddressesInPools) - g.totalAssignedIPv6Addresses.Add(subnet.totalAssignedAddresses) - g.totalAssignedIPv6AddressesInPools.Add(subnet.totalAssignedAddressesInPools) - g.totalDeclinedIPv6Addresses.Add(subnet.totalDeclinedAddresses) - g.totalDeclinedIPv6AddressesInPools.Add(subnet.totalDeclinedAddressesInPools) - g.totalDelegatedPrefixes.Add(subnet.totalDelegatedPrefixes) - g.totalDelegatedPrefixesInPools.Add(subnet.totalDelegatedPrefixesInPools) - g.totalAssignedDelegatedPrefixes.Add(subnet.totalAssignedDelegatedPrefixes) - g.totalAssignedDelegatedPrefixesInPools.Add(subnet.totalAssignedDelegatedPrefixesInPools) + g.totalIPv6Addresses.Add(g.totalIPv6Addresses, subnet.totalAddresses) + g.totalIPv6AddressesInPools.Add(g.totalIPv6AddressesInPools, subnet.totalAddressesInPools) + g.totalAssignedIPv6Addresses.Add(g.totalAssignedIPv6Addresses, subnet.totalAssignedAddresses) + g.totalAssignedIPv6AddressesInPools.Add(g.totalAssignedIPv6AddressesInPools, subnet.totalAssignedAddressesInPools) + g.totalDeclinedIPv6Addresses.Add(g.totalDeclinedIPv6Addresses, subnet.totalDeclinedAddresses) + g.totalDeclinedIPv6AddressesInPools.Add(g.totalDeclinedIPv6AddressesInPools, subnet.totalDeclinedAddressesInPools) + g.totalDelegatedPrefixes.Add(g.totalDelegatedPrefixes, subnet.totalDelegatedPrefixes) + g.totalDelegatedPrefixesInPools.Add(g.totalDelegatedPrefixesInPools, subnet.totalDelegatedPrefixesInPools) + g.totalAssignedDelegatedPrefixes.Add(g.totalAssignedDelegatedPrefixes, subnet.totalAssignedDelegatedPrefixes) + g.totalAssignedDelegatedPrefixesInPools.Add(g.totalAssignedDelegatedPrefixesInPools, subnet.totalAssignedDelegatedPrefixesInPools) } // General subnet lease statistics. @@ -117,9 +117,9 @@ func (s *sharedNetworkStats) GetAddressUtilization() float64 { // Out-of-pool address utilization of the shared network. func (s *sharedNetworkStats) GetOutOfPoolAddressUtilization() float64 { - return s.totalAssignedAddresses.Subtract(s.totalAssignedAddressesInPools). + return storkutil.NewBigCounter(0).Subtract(s.totalAssignedAddresses, s.totalAssignedAddressesInPools). DivideSafeBy( - s.totalAddresses.Subtract(s.totalAddressesInPools), + storkutil.NewBigCounter(0).Subtract(s.totalAddresses, s.totalAddressesInPools), ) } @@ -130,9 +130,9 @@ func (s *sharedNetworkStats) GetDelegatedPrefixUtilization() float64 { // Out-of-pool delegated prefix utilization of the shared network. func (s *sharedNetworkStats) GetOutOfPoolDelegatedPrefixUtilization() float64 { - return s.totalAssignedDelegatedPrefixes.Subtract(s.totalAssignedDelegatedPrefixesInPools). + return storkutil.NewBigCounter(0).Subtract(s.totalAssignedDelegatedPrefixes, s.totalAssignedDelegatedPrefixesInPools). DivideSafeBy( - s.totalDelegatedPrefixes.Subtract(s.totalDelegatedPrefixesInPools), + storkutil.NewBigCounter(0).Subtract(s.totalDelegatedPrefixes, s.totalDelegatedPrefixesInPools), ) } @@ -141,34 +141,34 @@ func (s *sharedNetworkStats) GetOutOfPoolDelegatedPrefixUtilization() float64 { func (s *sharedNetworkStats) GetStatistics() dbmodel.Stats { return dbmodel.Stats{ dbmodel.StatNameTotalNAs: s.totalAddresses.ConvertToNativeType(), - dbmodel.StatNameTotalOutOfPoolNAs: s.totalAddresses.Subtract(s.totalAddressesInPools).ConvertToNativeType(), + dbmodel.StatNameTotalOutOfPoolNAs: storkutil.NewBigCounter(0).Subtract(s.totalAddresses, s.totalAddressesInPools).ConvertToNativeType(), dbmodel.StatNameAssignedNAs: s.totalAssignedAddresses.ConvertToNativeType(), - dbmodel.StatNameAssignedOutOfPoolNAs: s.totalAssignedAddresses.Subtract(s.totalAssignedAddressesInPools).ConvertToNativeType(), + dbmodel.StatNameAssignedOutOfPoolNAs: storkutil.NewBigCounter(0).Subtract(s.totalAssignedAddresses, s.totalAssignedAddressesInPools).ConvertToNativeType(), dbmodel.StatNameTotalPDs: s.totalDelegatedPrefixes.ConvertToNativeType(), - dbmodel.StatNameTotalOutOfPoolPDs: s.totalDelegatedPrefixes.Subtract(s.totalDelegatedPrefixesInPools).ConvertToNativeType(), + dbmodel.StatNameTotalOutOfPoolPDs: storkutil.NewBigCounter(0).Subtract(s.totalDelegatedPrefixes, s.totalDelegatedPrefixesInPools).ConvertToNativeType(), dbmodel.StatNameAssignedPDs: s.totalAssignedDelegatedPrefixes.ConvertToNativeType(), - dbmodel.StatNameAssignedOutOfPoolPDs: s.totalAssignedDelegatedPrefixes.Subtract(s.totalAssignedDelegatedPrefixesInPools).ConvertToNativeType(), + dbmodel.StatNameAssignedOutOfPoolPDs: storkutil.NewBigCounter(0).Subtract(s.totalAssignedDelegatedPrefixes, s.totalAssignedDelegatedPrefixesInPools).ConvertToNativeType(), } } // Add the IPv4 subnet statistics to the shared network state. func (s *sharedNetworkStats) addIPv4Subnet(subnet *subnetIPv4Stats) { - s.totalAddresses.AddUint64(subnet.totalAddresses) - s.totalAddressesInPools.AddUint64(subnet.totalAddressesInPools) - s.totalAssignedAddresses.AddUint64(subnet.totalAssignedAddresses) - s.totalAssignedAddressesInPools.AddUint64(subnet.totalAssignedAddressesInPools) + s.totalAddresses.AddUint64(s.totalAddresses, subnet.totalAddresses) + s.totalAddressesInPools.AddUint64(s.totalAddressesInPools, subnet.totalAddressesInPools) + s.totalAssignedAddresses.AddUint64(s.totalAssignedAddresses, subnet.totalAssignedAddresses) + s.totalAssignedAddressesInPools.AddUint64(s.totalAssignedAddressesInPools, subnet.totalAssignedAddressesInPools) } // Add the IPv6 subnet statistics to the shared network state. func (s *sharedNetworkStats) addIPv6Subnet(subnet *subnetIPv6Stats) { - s.totalAddresses.Add(subnet.totalAddresses) - s.totalAddressesInPools.Add(subnet.totalAddressesInPools) - s.totalAssignedAddresses.Add(subnet.totalAssignedAddresses) - s.totalAssignedAddressesInPools.Add(subnet.totalAssignedAddressesInPools) - s.totalDelegatedPrefixes.Add(subnet.totalDelegatedPrefixes) - s.totalDelegatedPrefixesInPools.Add(subnet.totalDelegatedPrefixesInPools) - s.totalAssignedDelegatedPrefixes.Add(subnet.totalAssignedDelegatedPrefixes) - s.totalAssignedDelegatedPrefixesInPools.Add(subnet.totalAssignedDelegatedPrefixesInPools) + s.totalAddresses.Add(s.totalAddresses, subnet.totalAddresses) + s.totalAddressesInPools.Add(s.totalAddressesInPools, subnet.totalAddressesInPools) + s.totalAssignedAddresses.Add(s.totalAssignedAddresses, subnet.totalAssignedAddresses) + s.totalAssignedAddressesInPools.Add(s.totalAssignedAddressesInPools, subnet.totalAssignedAddressesInPools) + s.totalDelegatedPrefixes.Add(s.totalDelegatedPrefixes, subnet.totalDelegatedPrefixes) + s.totalDelegatedPrefixesInPools.Add(s.totalDelegatedPrefixesInPools, subnet.totalDelegatedPrefixesInPools) + s.totalAssignedDelegatedPrefixes.Add(s.totalAssignedDelegatedPrefixes, subnet.totalAssignedDelegatedPrefixes) + s.totalAssignedDelegatedPrefixesInPools.Add(s.totalAssignedDelegatedPrefixesInPools, subnet.totalAssignedDelegatedPrefixesInPools) } // IPv4 statistics retrieved from the single subnet. @@ -247,9 +247,9 @@ func (s *subnetIPv6Stats) GetAddressUtilization() float64 { // Returns the out-of-pool IPv6 address utilization for a single IPv6 subnet. func (s *subnetIPv6Stats) GetOutOfPoolAddressUtilization() float64 { - return s.totalAssignedAddresses.Clone().Subtract(s.totalAssignedAddressesInPools). + return storkutil.NewBigCounter(0).Subtract(s.totalAssignedAddresses, s.totalAssignedAddressesInPools). DivideSafeBy( - s.totalAddresses.Clone().Subtract(s.totalAddressesInPools), + storkutil.NewBigCounter(0).Subtract(s.totalAddresses, s.totalAddressesInPools), ) } @@ -260,9 +260,9 @@ func (s *subnetIPv6Stats) GetDelegatedPrefixUtilization() float64 { // Returns the out-of-pool delegated prefix utilization for a single IPv6 subnet. func (s *subnetIPv6Stats) GetOutOfPoolDelegatedPrefixUtilization() float64 { - return s.totalAssignedDelegatedPrefixes.Clone().Subtract(s.totalAssignedDelegatedPrefixesInPools). + return storkutil.NewBigCounter(0).Subtract(s.totalAssignedDelegatedPrefixes, s.totalAssignedDelegatedPrefixesInPools). DivideSafeBy( - s.totalDelegatedPrefixes.Clone().Subtract(s.totalDelegatedPrefixesInPools), + storkutil.NewBigCounter(0).Subtract(s.totalDelegatedPrefixes, s.totalDelegatedPrefixesInPools), ) } @@ -271,15 +271,15 @@ func (s *subnetIPv6Stats) GetOutOfPoolDelegatedPrefixUtilization() float64 { func (s *subnetIPv6Stats) GetStatistics() dbmodel.Stats { return dbmodel.Stats{ dbmodel.StatNameTotalNAs: s.totalAddresses.ConvertToNativeType(), - dbmodel.StatNameTotalOutOfPoolNAs: s.totalAddresses.Subtract(s.totalAddressesInPools).ConvertToNativeType(), + dbmodel.StatNameTotalOutOfPoolNAs: storkutil.NewBigCounter(0).Subtract(s.totalAddresses, s.totalAddressesInPools).ConvertToNativeType(), dbmodel.StatNameAssignedNAs: s.totalAssignedAddresses.ConvertToNativeType(), - dbmodel.StatNameAssignedOutOfPoolNAs: s.totalAssignedAddresses.Subtract(s.totalAssignedAddressesInPools).ConvertToNativeType(), + dbmodel.StatNameAssignedOutOfPoolNAs: storkutil.NewBigCounter(0).Subtract(s.totalAssignedAddresses, s.totalAssignedAddressesInPools).ConvertToNativeType(), dbmodel.StatNameDeclinedNAs: s.totalDeclinedAddresses.ConvertToNativeType(), - dbmodel.StatNameDeclinedOutOfPoolNAs: s.totalDeclinedAddresses.Subtract(s.totalDeclinedAddressesInPools).ConvertToNativeType(), + dbmodel.StatNameDeclinedOutOfPoolNAs: storkutil.NewBigCounter(0).Subtract(s.totalDeclinedAddresses, s.totalDeclinedAddressesInPools).ConvertToNativeType(), dbmodel.StatNameTotalPDs: s.totalDelegatedPrefixes.ConvertToNativeType(), - dbmodel.StatNameTotalOutOfPoolPDs: s.totalDelegatedPrefixes.Subtract(s.totalDelegatedPrefixesInPools).ConvertToNativeType(), + dbmodel.StatNameTotalOutOfPoolPDs: storkutil.NewBigCounter(0).Subtract(s.totalDelegatedPrefixes, s.totalDelegatedPrefixesInPools).ConvertToNativeType(), dbmodel.StatNameAssignedPDs: s.totalAssignedDelegatedPrefixes.ConvertToNativeType(), - dbmodel.StatNameAssignedOutOfPoolPDs: s.totalAssignedDelegatedPrefixes.Subtract(s.totalAssignedDelegatedPrefixesInPools).ConvertToNativeType(), + dbmodel.StatNameAssignedOutOfPoolPDs: storkutil.NewBigCounter(0).Subtract(s.totalAssignedDelegatedPrefixes, s.totalAssignedDelegatedPrefixesInPools).ConvertToNativeType(), } } @@ -395,18 +395,21 @@ func (c *statisticsCounter) addIPv4Subnet(subnet *dbmodel.Subnet, outOfPool uint // calculated similarly. func (c *statisticsCounter) addIPv6Subnet(subnet *dbmodel.Subnet, outOfPoolTotalAddresses, outOfPoolDelegatedPrefixes uint64) *subnetIPv6Stats { stats := &subnetIPv6Stats{ - totalAddresses: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameTotalNAs, c.excludedDaemons).AddUint64(outOfPoolTotalAddresses), + totalAddresses: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameTotalNAs, c.excludedDaemons), totalAddressesInPools: sumStatAddressPoolsIPv6(subnet, dbmodel.StatNameTotalNAs, c.excludedDaemons), totalAssignedAddresses: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameAssignedNAs, c.excludedDaemons), totalAssignedAddressesInPools: sumStatAddressPoolsIPv6(subnet, dbmodel.StatNameAssignedNAs, c.excludedDaemons), totalDeclinedAddresses: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameDeclinedNAs, c.excludedDaemons), totalDeclinedAddressesInPools: sumStatAddressPoolsIPv6(subnet, dbmodel.StatNameDeclinedNAs, c.excludedDaemons), - totalDelegatedPrefixes: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameTotalPDs, c.excludedDaemons).AddUint64(outOfPoolDelegatedPrefixes), + totalDelegatedPrefixes: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameTotalPDs, c.excludedDaemons), totalDelegatedPrefixesInPools: sumStatPrefixPoolsIPv6(subnet, dbmodel.StatNameTotalPDs, c.excludedDaemons), totalAssignedDelegatedPrefixes: sumStatLocalSubnetsIPv6(subnet, dbmodel.StatNameAssignedPDs, c.excludedDaemons), totalAssignedDelegatedPrefixesInPools: sumStatPrefixPoolsIPv6(subnet, dbmodel.StatNameAssignedPDs, c.excludedDaemons), } + stats.totalAddresses.AddUint64(stats.totalAddresses, outOfPoolTotalAddresses) + stats.totalDelegatedPrefixes.AddUint64(stats.totalDelegatedPrefixes, outOfPoolDelegatedPrefixes) + if subnet.SharedNetworkID != 0 { c.sharedNetworks[subnet.SharedNetworkID].addIPv6Subnet(stats) } @@ -431,7 +434,7 @@ func sumStatLocalSubnetsIPv6(subnet *dbmodel.Subnet, statName string, excludedDa continue } - sum.Add(value) + sum.Add(sum, value) } return sum } @@ -460,7 +463,7 @@ func sumStatAddressPoolsIPv6(subnet *dbmodel.Subnet, statName string, excludedDa continue } - sum.Add(value) + sum.Add(sum, value) } } return sum @@ -490,7 +493,7 @@ func sumStatPrefixPoolsIPv6(subnet *dbmodel.Subnet, statName string, excludedDae continue } - sum.Add(value) + sum.Add(sum, value) } } return sum @@ -556,22 +559,47 @@ func sumStatAddressPoolsIPv4(subnet *dbmodel.Subnet, statName string, excludedDa // Returns the global statistics. func (c *statisticsCounter) GetStatistics() dbmodel.Stats { - return dbmodel.Stats{ - dbmodel.StatNameTotalAddresses: c.global.totalIPv4Addresses.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalAddresses).ToBigInt(), - dbmodel.StatNameTotalOutOfPoolAddresses: c.global.totalIPv4Addresses.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalAddresses).Subtract(c.global.totalIPv4AddressesInPools).ToBigInt(), - dbmodel.StatNameAssignedAddresses: c.global.totalAssignedIPv4Addresses.ToBigInt(), - dbmodel.StatNameAssignedOutOfPoolAddresses: c.global.totalAssignedIPv4Addresses.Clone().Subtract(c.global.totalAssignedIPv4AddressesInPools).ToBigInt(), - dbmodel.StatNameDeclinedAddresses: c.global.totalDeclinedIPv4Addresses.ToBigInt(), - dbmodel.StatNameDeclinedOutOfPoolAddresses: c.global.totalDeclinedIPv4Addresses.Subtract(c.global.totalDeclinedIPv4AddressesInPools).ToBigInt(), - dbmodel.StatNameTotalNAs: c.global.totalIPv6Addresses.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalNAs).ToBigInt(), - dbmodel.StatNameTotalOutOfPoolNAs: c.global.totalIPv6Addresses.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalNAs).Subtract(c.global.totalIPv6AddressesInPools).ToBigInt(), - dbmodel.StatNameAssignedNAs: c.global.totalAssignedIPv6Addresses.ToBigInt(), - dbmodel.StatNameAssignedOutOfPoolNAs: c.global.totalAssignedIPv6Addresses.Clone().Subtract(c.global.totalAssignedIPv6AddressesInPools).ToBigInt(), - dbmodel.StatNameDeclinedNAs: c.global.totalDeclinedIPv6Addresses.ToBigInt(), - dbmodel.StatNameDeclinedOutOfPoolNAs: c.global.totalDeclinedIPv6Addresses.Clone().Subtract(c.global.totalDeclinedIPv6AddressesInPools).ToBigInt(), - dbmodel.StatNameTotalPDs: c.global.totalDelegatedPrefixes.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalPrefixes).ToBigInt(), - dbmodel.StatNameTotalOutOfPoolPDs: c.global.totalDelegatedPrefixes.Clone().AddUint64(c.outOfPoolShifts.outOfPoolGlobalPrefixes).Subtract(c.global.totalDelegatedPrefixesInPools).ToBigInt(), - dbmodel.StatNameAssignedPDs: c.global.totalAssignedDelegatedPrefixes.ToBigInt(), - dbmodel.StatNameAssignedOutOfPoolPDs: c.global.totalAssignedDelegatedPrefixes.Clone().Subtract(c.global.totalAssignedDelegatedPrefixesInPools).ToBigInt(), - } + stats := dbmodel.Stats{} + + value := storkutil.NewBigCounter(0).AddUint64(c.global.totalIPv4Addresses, c.outOfPoolShifts.outOfPoolGlobalAddresses) + stats[dbmodel.StatNameTotalAddresses] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalIPv4AddressesInPools) + stats[dbmodel.StatNameTotalOutOfPoolAddresses] = value.ConvertToNativeType() + + value = c.global.totalAssignedIPv4Addresses + stats[dbmodel.StatNameAssignedAddresses] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalAssignedIPv4AddressesInPools) + stats[dbmodel.StatNameAssignedOutOfPoolAddresses] = value.ConvertToNativeType() + + value = c.global.totalDeclinedIPv4Addresses + stats[dbmodel.StatNameDeclinedAddresses] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalDeclinedIPv4AddressesInPools) + stats[dbmodel.StatNameDeclinedOutOfPoolAddresses] = value.ConvertToNativeType() + + value = storkutil.NewBigCounter(0).AddUint64(c.global.totalIPv6Addresses, c.outOfPoolShifts.outOfPoolGlobalNAs) + stats[dbmodel.StatNameTotalNAs] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalIPv6AddressesInPools) + stats[dbmodel.StatNameTotalOutOfPoolNAs] = value.ConvertToNativeType() + + value = c.global.totalAssignedIPv6Addresses + stats[dbmodel.StatNameAssignedNAs] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalAssignedIPv6AddressesInPools) + stats[dbmodel.StatNameAssignedOutOfPoolNAs] = value.ConvertToNativeType() + + value = c.global.totalDeclinedIPv6Addresses + stats[dbmodel.StatNameDeclinedNAs] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalDeclinedIPv6AddressesInPools) + stats[dbmodel.StatNameDeclinedOutOfPoolNAs] = value.ConvertToNativeType() + + value = storkutil.NewBigCounter(0).AddUint64(c.global.totalDelegatedPrefixes, c.outOfPoolShifts.outOfPoolGlobalPrefixes) + stats[dbmodel.StatNameTotalPDs] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalDelegatedPrefixesInPools) + stats[dbmodel.StatNameTotalOutOfPoolPDs] = value.ConvertToNativeType() + + value = c.global.totalAssignedDelegatedPrefixes + stats[dbmodel.StatNameAssignedPDs] = value.ConvertToNativeType() + value = storkutil.NewBigCounter(0).Subtract(value, c.global.totalAssignedDelegatedPrefixesInPools) + stats[dbmodel.StatNameAssignedOutOfPoolPDs] = value.ConvertToNativeType() + + return stats } diff --git a/backend/server/apps/kea/statisticscounter_test.go b/backend/server/apps/kea/statisticscounter_test.go index 270d4164a..73898c4ed 100644 --- a/backend/server/apps/kea/statisticscounter_test.go +++ b/backend/server/apps/kea/statisticscounter_test.go @@ -1118,3 +1118,70 @@ func TestCounterGetStatisticsForSharedNetwork(t *testing.T) { require.EqualValues(t, 140, stats["total-pds"]) require.EqualValues(t, 40, stats["assigned-pds"]) } + +// Test that multiple calls to GetStatistics return the same result. +func TestGetStatisticsIdempotent(t *testing.T) { + // Arrange + subnet := &dbmodel.Subnet{ + ID: 2, + SharedNetworkID: 1, + Prefix: "20::/64", + LocalSubnets: []*dbmodel.LocalSubnet{ + { + Stats: dbmodel.Stats{ + "total-nas": uint64(100), + "assigned-nas": uint64(10), + "declined-nas": uint64(20), + "total-pds": uint64(40), + "assigned-pds": uint64(30), + }, + AddressPools: []dbmodel.AddressPool{ + { + Stats: dbmodel.Stats{ + "total-nas": uint64(90), + "assigned-nas": uint64(9), + "declined-nas": uint64(18), + "total-pds": uint64(36), + "assigned-pds": uint64(27), + }, + KeaParameters: &keaconfig.PoolParameters{PoolID: 0}, + }, + }, + PrefixPools: []dbmodel.PrefixPool{ + { + Stats: dbmodel.Stats{ + "total-pds": uint64(30), + "assigned-pds": uint64(10), + }, + KeaParameters: &keaconfig.PoolParameters{PoolID: 0}, + }, + }, + }, + }, + } + + counter := newStatisticsCounter() + counter.setOutOfPoolShifts(outOfPoolShifts{ + outOfPoolAddresses: map[int64]uint64{2: 10}, + outOfPoolPrefixes: map[int64]uint64{1: 4}, + outOfPoolGlobalAddresses: 0, + outOfPoolGlobalNAs: 10, + outOfPoolGlobalPrefixes: 4, + }) + + subnetStats := counter.add(subnet) + networkStats := counter.sharedNetworks[1] + + // Act + globalStats1 := counter.GetStatistics() + globalStats2 := counter.GetStatistics() + networkStats1 := networkStats.GetStatistics() + networkStats2 := networkStats.GetStatistics() + subnetStats1 := subnetStats.GetStatistics() + subnetStats2 := subnetStats.GetStatistics() + + // Assert + require.Equal(t, globalStats1, globalStats2) + require.Equal(t, networkStats1, networkStats2) + require.Equal(t, subnetStats1, subnetStats2) +} diff --git a/backend/server/configreview/keachecker.go b/backend/server/configreview/keachecker.go index 8316aa65c..d4ee694e4 100644 --- a/backend/server/configreview/keachecker.go +++ b/backend/server/configreview/keachecker.go @@ -946,7 +946,7 @@ func addressPoolsExhaustedByReservations(ctx *ReviewContext) (*Report, error) { for _, address := range reservedAddresses { if address.IsInRange(lb, ub) { // Increment by one. - reservationsInPoolCount.AddUint64(1) + reservationsInPoolCount.AddUint64(reservationsInPoolCount, 1) } } @@ -1073,7 +1073,7 @@ func delegatedPrefixPoolsExhaustedByReservations(ctx *ReviewContext) (*Report, e reservationsInPoolCount := storkutil.NewBigCounter(0) for _, prefix := range reservedPrefixes { if prefix.IsInPrefixRange(pool.Prefix, pool.PrefixLen, pool.DelegatedLen) { - reservationsInPoolCount.AddUint64(1) + reservationsInPoolCount.AddUint64(reservationsInPoolCount, 1) } } diff --git a/backend/util/bigcounter.go b/backend/util/bigcounter.go index 72c1df059..2ff97c1d9 100644 --- a/backend/util/bigcounter.go +++ b/backend/util/bigcounter.go @@ -20,38 +20,14 @@ type BigCounter struct { extended *big.Int } -// Indicates that the uint64 can be added to the base value without integer overflow. -func (n *BigCounter) canAddToBase(val uint64) bool { - return n.base <= math.MaxUint64-val +// Indicates that two uint64 can be added without integer overflow. +func canAdd(a, b uint64) bool { + return a <= math.MaxUint64-b } -// Indicates that the uint64 can be subtracted from the base value without -// integer underflow. -func (n *BigCounter) canSubtractFromBase(val uint64) bool { - return n.base >= val -} - -// Indicates that another counting value can be added to the internal state. -func (n *BigCounter) canAdd(other *BigCounter) bool { - if n.isExtended() { - return true - } - if other.isExtended() { - return false - } - return n.canAddToBase(other.base) -} - -// Indicates that another counting value can be subtracted from the internal -// state. -func (n *BigCounter) canSubtract(other *BigCounter) bool { - if n.isExtended() { - return true - } - if other.isExtended() { - return false - } - return n.canSubtractFromBase(other.base) +// Indicates that two uint64 can be subtracted without integer underflow. +func canSubtract(a, b uint64) bool { + return a >= b } // Indicates that this counter uses big-int based counter. @@ -59,83 +35,83 @@ func (n *BigCounter) isExtended() bool { return n.extended != nil } -// Initializes the big-int counter with current value of the uint64 counter. -// Set the uint64 counter to max uint64 value to ensure that the canAddToBase will return false. -// It should be called only once per big counter. -// It should be called only if the counting value exceeds the uint64 range. -func (n *BigCounter) initExtended() { - n.extended = big.NewInt(0).SetUint64(n.base) - n.base = math.MaxUint64 -} - -// Makes a copy of the current big counter. -func (n *BigCounter) Clone() *BigCounter { - var extended *big.Int - if n.extended != nil { - extended = new(big.Int).Set(n.extended) - } - - return &BigCounter{ - base: n.base, - extended: extended, +// Normalizes the big counter to the uint64 range if possible. +func (n *BigCounter) normalize() { + if n.isExtended() && n.extended.IsUint64() { + n.base = n.extended.Uint64() + n.extended = nil } } -// Adds the other big counter value to the internal counting value. -// It modifies the internal state. -func (n *BigCounter) Add(other *BigCounter) *BigCounter { - if !n.canAdd(other) { - n.initExtended() - } +// Adds two big counters and puts the result into the receiver. +func (n *BigCounter) Add(a, b *BigCounter) *BigCounter { + if a.extended != nil || b.extended != nil || !canAdd(a.base, b.base) { + outBigInt := n.extended + if outBigInt == nil { + outBigInt = big.NewInt(0) + } - if n.isExtended() { - n.extended.Add(n.extended, other.ToBigInt()) + n.extended = outBigInt.Add(a.ToBigInt(), b.ToBigInt()) + n.base = math.MaxUint64 } else { - n.base += other.base + n.base = a.base + b.base + n.extended = nil } return n } -// Subtracts the other big counter value from the internal counting value. -func (n *BigCounter) Subtract(other *BigCounter) *BigCounter { - if !n.canSubtract(other) { - n.initExtended() - } - if n.isExtended() { - n.extended.Sub(n.extended, other.ToBigInt()) +// Subtracts the big counters and puts the result into the receiver. +func (n *BigCounter) Subtract(a, b *BigCounter) *BigCounter { + if a.isExtended() || b.isExtended() || !canSubtract(a.base, b.base) { + outBigInt := n.extended + if outBigInt == nil { + outBigInt = big.NewInt(0) + } + + n.extended = outBigInt.Sub(n.ToBigInt(), b.ToBigInt()) + n.base = 0 + n.normalize() } else { - n.base -= other.base + n.base = a.base - b.base + n.extended = nil } return n } -// Adds uint64 number to the internal counting value. -// It modifies the internal state. -func (n *BigCounter) AddUint64(val uint64) *BigCounter { - if !n.isExtended() && !n.canAddToBase(val) { - n.initExtended() - } +// Adds the big counter and the uint64 value and puts the result into the +// receiver. +func (n *BigCounter) AddUint64(a *BigCounter, b uint64) *BigCounter { + if a.isExtended() || !canAdd(a.base, b) { + outBigInt := n.extended + if outBigInt == nil { + outBigInt = big.NewInt(0) + } - if n.isExtended() { - valBig := new(big.Int).SetUint64(val) - n.extended.Add(n.extended, valBig) + n.extended = outBigInt.Add(a.ToBigInt(), big.NewInt(0).SetUint64(b)) + n.base = math.MaxUint64 } else { - n.base += val + n.base = a.base + b + n.extended = nil } return n } -// Adds big.Int number to the internal counting value. -// It modifies the internal state. Only positive integer are allowed. -func (n *BigCounter) AddBigInt(val *big.Int) *BigCounter { - if val.IsUint64() { - return n.AddUint64(val.Uint64()) - } - if !n.isExtended() { - n.initExtended() +// Adds big counter and the big int value and puts the result into the +// receiver. +func (n *BigCounter) AddBigInt(a *BigCounter, b *big.Int) *BigCounter { + if !a.isExtended() && b.IsUint64() && canAdd(a.base, b.Uint64()) { + n.extended = nil + n.base = a.base + b.Uint64() + } else { + outBigInt := n.extended + if outBigInt == nil { + outBigInt = big.NewInt(0) + } + + n.extended = outBigInt.Add(a.ToBigInt(), b) + n.base = math.MaxUint64 } - n.extended.Add(n.extended, val) return n } @@ -154,13 +130,19 @@ func (n *BigCounter) DivideBy(other *BigCounter) float64 { return res } +// Indicates if the big counter is zero. +func (n *BigCounter) IsZero() bool { + if n.isExtended() { + return n.extended.Sign() == 0 + } + return n.base == 0 +} + // Works as the Divide function but returns 0 when the value // of the denominator counter is 0. func (n *BigCounter) DivideSafeBy(other *BigCounter) float64 { - if !other.isExtended() && other.base == 0 { - return 0.0 - } else if other.isExtended() && other.extended.Cmp(big.NewInt(0)) == 0 { - return 0.0 + if other.IsZero() { + return 0 } return n.DivideBy(other) } diff --git a/backend/util/bigcounter_test.go b/backend/util/bigcounter_test.go index 90b8940f4..7632c1cb6 100644 --- a/backend/util/bigcounter_test.go +++ b/backend/util/bigcounter_test.go @@ -22,66 +22,141 @@ func TestBigCounterConstruct(t *testing.T) { require.NotNil(t, counter3) } -// Test addition uint64 in place to the uint64 counter. +// Test addition uint64 to the uint64 counter. func TestBigCounterAddUint64ToUint64(t *testing.T) { // Arrange counter1 := NewBigCounter(5) counter2 := NewBigCounter(37) + counterOut := NewBigCounter(0) // Act - counter1.Add(counter2) + counterOut.Add(counter1, counter2) + + // Assert + require.EqualValues(t, 42, counterOut.ToInt64()) + require.EqualValues(t, 5, counter1.ToInt64()) + require.EqualValues(t, 37, counter2.ToInt64()) +} + +// Test addition in place uint64 to the uint64 counter. +func TestBigCounterAddUint64ToUint64InPlace(t *testing.T) { + // Arrange + counter1 := NewBigCounter(5) + counter2 := NewBigCounter(37) + + // Act + counter1.Add(counter1, counter2) // Assert require.EqualValues(t, 42, counter1.ToInt64()) require.EqualValues(t, 37, counter2.ToInt64()) } -// Test addition big int in place to the uint64 counter. +// Test addition big int to the uint64 counter. func TestBigCounterAddBigIntToUint64(t *testing.T) { // Arrange counter1 := NewBigCounter(5) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(1) + counter2 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1))) + counterOut := NewBigCounter(0) // Act - counter1.Add(counter2) + counterOut.Add(counter1, counter2) + + // Assert + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(6)), counterOut.ToBigInt()) + require.EqualValues(t, 5, counter1.ToInt64()) + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1)), counter2.ToBigInt()) +} + +// Test addition in place big int to the uint64 counter. +func TestBigCounterAddBigIntToUint64InPlace(t *testing.T) { + // Arrange + counter1 := NewBigCounter(5) + counter2 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1))) + + // Act + counter1.Add(counter1, counter2) // Assert require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(6)), counter1.ToBigInt()) require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1)), counter2.ToBigInt()) } -// Test addition uint64 in place to the big int counter. +// Test addition uint64 to the big int counter. func TestBigCounterAddUint64ToBigInt(t *testing.T) { // Arrange - counter1 := NewBigCounter(math.MaxUint64).AddUint64(1) + counter1 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1))) counter2 := NewBigCounter(5) + counterOut := NewBigCounter(0) // Act - counter1.Add(counter2) + counterOut.Add(counter1, counter2) + + // Assert + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(6)), counterOut.ToBigInt()) + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1)), counter1.ToBigInt()) + require.EqualValues(t, 5, counter2.ToInt64()) +} + +// Test addition in place uint64 to the big int counter. +func TestBigCounterAddUint64ToBigIntInPlace(t *testing.T) { + // Arrange + counter1 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(1))) + counter2 := NewBigCounter(5) + + // Act + counter1.Add(counter1, counter2) // Assert require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(6)), counter1.ToBigInt()) require.EqualValues(t, big.NewInt(5), counter2.ToBigInt()) } -// Test addition big int in place to the big int counter. +// Test addition big int to the big int counter. func TestBigCounterAddBigIntToBigInt(t *testing.T) { // Arrange - counter1 := NewBigCounter(math.MaxUint64).AddUint64(37) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(5) + counter1 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(37))) + counter2 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(5))) + expected := big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(0).SetUint64(math.MaxUint64)) + expected = expected.Add(expected, big.NewInt(42)) + counterOut := NewBigCounter(0) + + // Act + counterOut.Add(counter1, counter2) + + // Assert + require.EqualValues(t, expected, counterOut.ToBigInt()) + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(37)), counter1.ToBigInt()) + require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(5)), counter2.ToBigInt()) +} + +// Test addition in place big int to the big int counter. +func TestBigCounterAddBigIntToBigIntInPlace(t *testing.T) { + // Arrange + counter1 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(37))) + counter2 := NewBigCounterFromBigInt(big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(5))) expected := big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(0).SetUint64(math.MaxUint64)) expected = expected.Add(expected, big.NewInt(42)) // Act - counter1.Add(counter2) + counter1.Add(counter1, counter2) // Assert require.EqualValues(t, expected, counter1.ToBigInt()) + require.EqualValues(t, + big.NewInt(0).Add( + big.NewInt(0).SetUint64(math.MaxUint64), + big.NewInt(0).Add( + big.NewInt(0).SetUint64(math.MaxUint64), + big.NewInt(37+5), + ), + ), + counter1.ToBigInt()) require.EqualValues(t, big.NewInt(0).Add(big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(5)), counter2.ToBigInt()) } // Test add in place uint64 shorthand. -func TestBigCounterAddUint64Shorthand(t *testing.T) { +func TestBigCounterAddUint64ShorthandInPlace(t *testing.T) { // Arrange expected := big.NewInt(0).Add( big.NewInt(0).Add( @@ -92,23 +167,19 @@ func TestBigCounterAddUint64Shorthand(t *testing.T) { ) // Act - counter1 := NewBigCounter(1) - counter1.AddUint64(uint64(41)) - counter1.AddUint64(math.MaxUint64) - counter1.AddUint64(math.MaxUint64) - var val int64 = -1 - counter2 := NewBigCounter(0).AddUint64(uint64(val)) + counter := NewBigCounter(1) + counter.AddUint64(counter, uint64(41)) + counter.AddUint64(counter, math.MaxUint64) + counter.AddUint64(counter, math.MaxUint64) // Assert require.EqualValues(t, expected, - counter1.ToBigInt()) - - require.EqualValues(t, big.NewInt(0).SetUint64(math.MaxUint64), counter2.ToBigInt()) + counter.ToBigInt()) } // Test add in place big.Int shorthand. -func TestBigCounterAddBigIntShorthand(t *testing.T) { +func TestBigCounterAddBigIntShorthandInPlace(t *testing.T) { // Arrange expected := big.NewInt(0).Add( big.NewInt(0).Add( @@ -119,9 +190,10 @@ func TestBigCounterAddBigIntShorthand(t *testing.T) { ) // Act counter := NewBigCounter(1) - _ = counter.AddBigInt(big.NewInt(10)) - _ = counter.AddBigInt(big.NewInt(100)) + _ = counter.AddBigInt(counter, big.NewInt(10)) + _ = counter.AddBigInt(counter, big.NewInt(100)) _ = counter.AddBigInt( + counter, big.NewInt(0).Add( big.NewInt(0).SetUint64(math.MaxUint64), big.NewInt(0).SetUint64(math.MaxUint64), @@ -131,14 +203,14 @@ func TestBigCounterAddBigIntShorthand(t *testing.T) { require.EqualValues(t, expected, counter.ToBigInt()) } -// Test that add in place big.Int ignores the negative numbers. -func TestBigCounterAddBigIntShorthandIgnoreNegatives(t *testing.T) { +// Test that add in place big.Int handles the negative numbers. +func TestBigCounterAddBigIntShorthandNegativesInPlace(t *testing.T) { // Arrange & Act counter := NewBigCounter(42) - _ = counter.AddBigInt(big.NewInt(-1)) - _ = counter.AddBigInt(big.NewInt(-2)) - _ = counter.AddBigInt(big.NewInt(math.MinInt64)) - _ = counter.AddBigInt(big.NewInt(0).Add( + _ = counter.AddBigInt(counter, big.NewInt(-1)) + _ = counter.AddBigInt(counter, big.NewInt(-2)) + _ = counter.AddBigInt(counter, big.NewInt(math.MinInt64)) + _ = counter.AddBigInt(counter, big.NewInt(0).Add( big.NewInt(math.MinInt64), big.NewInt(math.MinInt64), )) @@ -165,8 +237,11 @@ func TestBigCounterDivideInt64(t *testing.T) { // Test divide big int counters. func TestBigCounterDivideBigInt(t *testing.T) { // Arrange - counter1 := NewBigCounter(math.MaxUint64).AddUint64(4) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(math.MaxUint64).AddUint64(8) + counter1 := NewBigCounter(math.MaxUint64) + counter1.AddUint64(counter1, 4) + counter2 := NewBigCounter(math.MaxUint64) + counter2.AddUint64(counter2, math.MaxUint64) + counter2.AddUint64(counter2, 8) // Act res := counter1.DivideBy(counter2) @@ -178,7 +253,8 @@ func TestBigCounterDivideBigInt(t *testing.T) { // Test divide big int counter by uint64 and get result in uint64 range. func TestBigCounterDivideBigIntByInt64InInt64Range(t *testing.T) { // Arrange - counter1 := NewBigCounter(math.MaxUint64).AddUint64(math.MaxUint64) + counter1 := NewBigCounter(math.MaxUint64) + counter1.AddUint64(counter1, math.MaxUint64) counter2 := NewBigCounter(2) // Act @@ -188,7 +264,7 @@ func TestBigCounterDivideBigIntByInt64InInt64Range(t *testing.T) { require.EqualValues(t, float64(math.MaxUint64), res) } -// Test that safe divide doesn't panic for the base counter. +// Test that safe divide doesn't panic. func TestBigCounterSafeDivideByZero(t *testing.T) { // Arrange counter1 := NewBigCounter(1) @@ -201,26 +277,11 @@ func TestBigCounterSafeDivideByZero(t *testing.T) { require.Zero(t, res) } -// Test that safe divide doesn't panic for the extended counter. -func TestBigCounterSafeDivideByZeroExtended(t *testing.T) { - // Arrange - counter1 := NewBigCounter(1) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(1). - Subtract( - NewBigCounter(math.MaxUint64).AddUint64(1), - ) - - // Act - res := counter1.DivideSafeBy(counter2) - - // Assert - require.Zero(t, res) -} - // Test that safe divide works as standard divide. func TestBigCounterDivideSafe(t *testing.T) { // Arrange - counter1 := NewBigCounter(math.MaxUint64).AddUint64(math.MaxUint64) + counter1 := NewBigCounter(math.MaxUint64) + counter1.AddUint64(counter1, math.MaxUint64) counter2 := NewBigCounter(2) // Act @@ -235,7 +296,8 @@ func TestBigCounterToInt64(t *testing.T) { // Arrange counter0 := NewBigCounter(0) counter1 := NewBigCounter(math.MaxUint64) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(1) + counter2 := NewBigCounter(math.MaxUint64) + counter2 = counter2.AddUint64(counter2, 1) // Act value0 := counter0.ToInt64() @@ -252,8 +314,10 @@ func TestBigCounterToInt64(t *testing.T) { func TestBigCounterToUint64(t *testing.T) { // Arrange counter0 := NewBigCounter(0) - counter1 := NewBigCounter(0).AddUint64(math.MaxUint64) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(1) + counter1 := NewBigCounter(0) + counter1.AddUint64(counter1, math.MaxUint64) + counter2 := NewBigCounter(math.MaxUint64) + counter2.AddUint64(counter2, 1) // Act value0, ok0 := counter0.ToUint64() @@ -317,7 +381,8 @@ func TestBigCounterToBigInt(t *testing.T) { // Arrange counter0 := NewBigCounter(0) counter1 := NewBigCounter(math.MaxUint64) - counter2 := NewBigCounter(math.MaxUint64).AddUint64(1) + counter2 := NewBigCounter(math.MaxUint64) + counter2.AddUint64(counter2, 1) // Act value0 := counter0.ToBigInt() @@ -334,7 +399,8 @@ func TestBigCounterToBigInt(t *testing.T) { func TestBigCounterToNativeType(t *testing.T) { // Arrange counterBase := NewBigCounter(42) - counterExtended := NewBigCounter(math.MaxUint64).AddUint64(1) + counterExtended := NewBigCounter(math.MaxUint64) + counterExtended.AddUint64(counterExtended, 1) // Act nativeBase := counterBase.ConvertToNativeType() @@ -446,16 +512,19 @@ func TestBigCounterConstructFromBigInt(t *testing.T) { func TestBigCounterSubtractInUInt64Range(t *testing.T) { // Arrange counter0 := NewBigCounter(10000) + counter1 := NewBigCounter(0) + counter2 := NewBigCounter(0) + counter3 := NewBigCounter(0) // Act - counter1 := counter0.Subtract(NewBigCounter(5000)) - counter2 := counter1.Subtract(NewBigCounter(1000)) - counter3 := counter2.Subtract(NewBigCounter(4000)) + counter1.Subtract(counter0, NewBigCounter(5000)) + counter2.Subtract(counter1, NewBigCounter(1000)) + counter3.Subtract(counter2, NewBigCounter(4000)) // Assert - require.Zero(t, counter0.ToInt64()) - require.Zero(t, counter1.ToInt64()) - require.Zero(t, counter2.ToInt64()) + require.EqualValues(t, 10000, counter0.ToInt64()) + require.EqualValues(t, 5000, counter1.ToInt64()) + require.EqualValues(t, 4000, counter2.ToInt64()) require.Zero(t, counter3.ToInt64()) require.False(t, counter3.isExtended()) } @@ -463,12 +532,38 @@ func TestBigCounterSubtractInUInt64Range(t *testing.T) { // Test that the subtraction above uint64 range works correctly. func TestBigCounterSubtractAboveUInt64Range(t *testing.T) { // Arrange - counter := NewBigCounter(math.MaxUint64).AddUint64(10001) + counter1 := NewBigCounter(math.MaxUint64) + counter1.AddUint64(counter1, 10001) + counter2 := NewBigCounter(5000) + counter3 := NewBigCounter(1000) + counter4 := NewBigCounter(4000) + + // Act + counter1.Subtract(counter1, counter2) + counter1.Subtract(counter1, counter3) + counter1.Subtract(counter1, counter4) + + // Assert + require.EqualValues(t, big.NewInt(0).Add( + big.NewInt(0).SetUint64(math.MaxUint64), + big.NewInt(1), + ), counter1.ToBigInt()) + require.True(t, counter1.isExtended()) + require.EqualValues(t, 5000, counter2.ToInt64()) + require.EqualValues(t, 1000, counter3.ToInt64()) + require.EqualValues(t, 4000, counter4.ToInt64()) +} + +// Test that the subtraction in place above uint64 range works correctly. +func TestBigCounterSubtractAboveUInt64RangeInPlace(t *testing.T) { + // Arrange + counter := NewBigCounter(math.MaxUint64) + counter.AddUint64(counter, 10001) // Act - counter.Subtract(NewBigCounter(5000)) - counter.Subtract(NewBigCounter(1000)) - counter.Subtract(NewBigCounter(4000)) + counter.Subtract(counter, NewBigCounter(5000)) + counter.Subtract(counter, NewBigCounter(1000)) + counter.Subtract(counter, NewBigCounter(4000)) // Assert require.EqualValues(t, big.NewInt(0).Add( @@ -482,76 +577,51 @@ func TestBigCounterSubtractAboveUInt64Range(t *testing.T) { // works correctly. func TestBigCounterSubtractFromAboveUint64ToUint64Range(t *testing.T) { // Arrange - counter := NewBigCounter(math.MaxUint64).AddUint64(1) + counter := NewBigCounter(math.MaxUint64) + counter.AddUint64(counter, 1) // Act - counter.Subtract(NewBigCounter(math.MaxUint64)) + counter.Subtract(counter, NewBigCounter(math.MaxUint64)) // Assert require.EqualValues(t, 1, counter.ToInt64()) - require.True(t, counter.isExtended()) + require.False(t, counter.isExtended()) } // Test that the subtraction that results in a number in uint64 range // works correctly. func TestBigCounterSubtractFromAboveUint64ToBelowUint64Range(t *testing.T) { // Arrange - counter := NewBigCounter(math.MaxUint64).AddUint64(1) + counter := NewBigCounter(math.MaxUint64) + counter.AddUint64(counter, 1) // Act - counter.Subtract(NewBigCounter(math.MaxUint64)) - counter.Subtract(NewBigCounter(2)) + counter.Subtract(counter, NewBigCounter(math.MaxUint64)) + counter.Subtract(counter, NewBigCounter(2)) // Assert require.EqualValues(t, -1, counter.ToInt64()) require.True(t, counter.isExtended()) } -// Test that the cloned non-extended big counter is a separate instance -// and modifications to the original do not affect the clone. -func TestCloneInt64(t *testing.T) { - // Arrange - counter := NewBigCounter(42) - - // Act - clone := counter.Clone() - clone.AddUint64(1) - - // Assert - require.EqualValues(t, 42, counter.ToInt64()) - require.EqualValues(t, 43, clone.ToInt64()) -} - -// Test that the cloned extended big counter is a separate instance -// and modifications to the original do not affect the clone. -func TestCloneBigInt(t *testing.T) { - // Arrange - counter := NewBigCounter(math.MaxUint64).AddUint64(1) - - // Act - clone := counter.Clone() - clone.AddUint64(1) - - // Assert - require.EqualValues(t, big.NewInt(0).Add( - big.NewInt(0).SetUint64(math.MaxUint64), - big.NewInt(1), - ), counter.ToBigInt()) - require.EqualValues(t, big.NewInt(0).Add( - big.NewInt(0).SetUint64(math.MaxUint64), - big.NewInt(2), - ), clone.ToBigInt()) -} - // Benchmarks. // The below benchmark measure the big counter performance. +// +// I refactored the big counter to put results into the receiver in #1953. +// Below are results of the benchmarks before and after refactoring: +// | Benchmark | Before [ns/op] | After [ns/op] | +// | BenchmarkBigCounterInUint64Range-12. | 2.8420 | 2.2840 | +// | BenchmarkBigCounterOutUint64Range-12. | 14.5600 | 15.7900 | +// | BenchmarkBigIntInUint64Range-12. | 6.7410 | 6.6240 | +// | BenchmarkBigIntOutUint64Range-12. | 8.3120 | 8.3030 | +// | BenchmarkStandardUint64InUint64Range-12 | 0.2248 | 0.2297 | // Benchmark the addition to the big counter in uint64 range. func BenchmarkBigCounterInUint64Range(b *testing.B) { counter := NewBigCounter(0) for i := 0; i < b.N; i++ { - counter.AddUint64(1) + counter.AddUint64(counter, 1) } } @@ -559,7 +629,7 @@ func BenchmarkBigCounterInUint64Range(b *testing.B) { func BenchmarkBigCounterOutUint64Range(b *testing.B) { counter := NewBigCounter(math.MaxUint64) for i := 0; i < b.N; i++ { - counter.AddUint64(1) + counter.AddUint64(counter, 1) } }