diff --git a/database/helpers_test.go b/database/helpers_test.go new file mode 100644 index 000000000000..79e37ea74b67 --- /dev/null +++ b/database/helpers_test.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package database + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "golang.org/x/exp/slices" + + "github.com/ava-labs/avalanchego/utils" +) + +func TestSortednessUint64(t *testing.T) { + seed := time.Now().UnixNano() + t.Log("Seed: ", seed) + rand := rand.New(rand.NewSource(seed)) //#nosec G404 + + ints := make([]uint64, 1024) + for i := range ints { + ints[i] = rand.Uint64() + } + slices.Sort(ints) + + intBytes := make([][]byte, 1024) + for i, val := range ints { + intBytes[i] = PackUInt64(val) + } + require.True(t, utils.IsSortedBytes(intBytes)) +} + +func TestSortednessUint32(t *testing.T) { + seed := time.Now().UnixNano() + t.Log("Seed: ", seed) + rand := rand.New(rand.NewSource(seed)) //#nosec G404 + + ints := make([]uint32, 1024) + for i := range ints { + ints[i] = rand.Uint32() + } + slices.Sort(ints) + + intBytes := make([][]byte, 1024) + for i, val := range ints { + intBytes[i] = PackUInt32(val) + } + require.True(t, utils.IsSortedBytes(intBytes)) +} diff --git a/utils/sorting.go b/utils/sorting.go index 6c3911f1e7c8..1a52574f0349 100644 --- a/utils/sorting.go +++ b/utils/sorting.go @@ -38,12 +38,22 @@ func SortByHash[T ~[]byte](s []T) { // Sorts a 2D byte slice. // Each byte slice is not sorted internally; the byte slices are sorted relative // to one another. -func SortBytes[T ~[]byte](arr []T) { - slices.SortFunc(arr, func(i, j T) bool { +func SortBytes[T ~[]byte](s []T) { + slices.SortFunc(s, func(i, j T) bool { return bytes.Compare(i, j) == -1 }) } +// Returns true iff the elements in [s] are sorted. +func IsSortedBytes[T ~[]byte](s []T) bool { + for i := 0; i < len(s)-1; i++ { + if bytes.Compare(s[i], s[i+1]) == 1 { + return false + } + } + return true +} + // Returns true iff the elements in [s] are unique and sorted. func IsSortedAndUniqueSortable[T Sortable[T]](s []T) bool { for i := 0; i < len(s)-1; i++ { @@ -82,10 +92,10 @@ func IsSortedAndUniqueByHash[T ~[]byte](s []T) bool { } // Returns true iff the elements in [s] are unique. -func IsUnique[T comparable](elts []T) bool { +func IsUnique[T comparable](s []T) bool { // Can't use set.Set because it'd be a circular import. - asMap := make(map[T]struct{}, len(elts)) - for _, elt := range elts { + asMap := make(map[T]struct{}, len(s)) + for _, elt := range s { if _, ok := asMap[elt]; ok { return false } diff --git a/utils/sorting_test.go b/utils/sorting_test.go index 8be6f26ff47c..99a8c1d3ec67 100644 --- a/utils/sorting_test.go +++ b/utils/sorting_test.go @@ -4,7 +4,9 @@ package utils import ( + "math/rand" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -57,6 +59,23 @@ func TestSortSliceSortable(t *testing.T) { require.Equal([]sortable{1, 2, 3}, s) } +func TestSortBytesIsSortedBytes(t *testing.T) { + require := require.New(t) + + seed := time.Now().UnixNano() + t.Log("Seed: ", seed) + rand := rand.New(rand.NewSource(seed)) //#nosec G404 + + slices := make([][]byte, 1024) + for j := 0; j < len(slices); j++ { + slices[j] = make([]byte, 32) + _, _ = rand.Read(slices[j]) + } + require.False(IsSortedBytes(slices)) + SortBytes(slices) + require.True(IsSortedBytes(slices)) +} + func TestIsSortedAndUniqueSortable(t *testing.T) { require := require.New(t)