all: sync with master
This commit is contained in:
@@ -5,9 +5,9 @@ package aghalg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Coalesce returns the first non-zero value. It is named after function
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package aghalg_test
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// elements is a helper function that returns n elements of the buffer.
|
||||
|
||||
86
internal/aghalg/sortedmap.go
Normal file
86
internal/aghalg/sortedmap.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package aghalg
|
||||
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
// SortedMap is a map that keeps elements in order with internal sorting
|
||||
// function. Must be initialised by the [NewSortedMap].
|
||||
type SortedMap[K comparable, V any] struct {
|
||||
vals map[K]V
|
||||
cmp func(a, b K) (res int)
|
||||
keys []K
|
||||
}
|
||||
|
||||
// NewSortedMap initializes the new instance of sorted map. cmp is a sort
|
||||
// function to keep elements in order.
|
||||
//
|
||||
// TODO(s.chzhen): Use cmp.Compare in Go 1.21.
|
||||
func NewSortedMap[K comparable, V any](cmp func(a, b K) (res int)) SortedMap[K, V] {
|
||||
return SortedMap[K, V]{
|
||||
vals: map[K]V{},
|
||||
cmp: cmp,
|
||||
}
|
||||
}
|
||||
|
||||
// Set adds val with key to the sorted map. It panics if the m is nil.
|
||||
func (m *SortedMap[K, V]) Set(key K, val V) {
|
||||
m.vals[key] = val
|
||||
|
||||
i, has := slices.BinarySearchFunc(m.keys, key, m.cmp)
|
||||
if has {
|
||||
m.keys[i] = key
|
||||
} else {
|
||||
m.keys = slices.Insert(m.keys, i, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns val by key from the sorted map.
|
||||
func (m *SortedMap[K, V]) Get(key K) (val V, ok bool) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
val, ok = m.vals[key]
|
||||
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Del removes the value by key from the sorted map.
|
||||
func (m *SortedMap[K, V]) Del(key K) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, has := m.vals[key]; !has {
|
||||
return
|
||||
}
|
||||
|
||||
delete(m.vals, key)
|
||||
i, _ := slices.BinarySearchFunc(m.keys, key, m.cmp)
|
||||
m.keys = slices.Delete(m.keys, i, i+1)
|
||||
}
|
||||
|
||||
// Clear removes all elements from the sorted map.
|
||||
func (m *SortedMap[K, V]) Clear() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.keys = nil
|
||||
clear(m.vals)
|
||||
}
|
||||
|
||||
// Range calls cb for each element of the map, sorted by m.cmp. If cb returns
|
||||
// false it stops.
|
||||
func (m *SortedMap[K, V]) Range(cb func(K, V) (cont bool)) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, k := range m.keys {
|
||||
if !cb(k, m.vals[k]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
95
internal/aghalg/sortedmap_test.go
Normal file
95
internal/aghalg/sortedmap_test.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package aghalg
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSortedMap(t *testing.T) {
|
||||
var m SortedMap[string, int]
|
||||
|
||||
letters := []string{}
|
||||
for i := 0; i < 10; i++ {
|
||||
r := string('a' + rune(i))
|
||||
letters = append(letters, r)
|
||||
}
|
||||
|
||||
t.Run("create_and_fill", func(t *testing.T) {
|
||||
m = NewSortedMap[string, int](strings.Compare)
|
||||
|
||||
nums := []int{}
|
||||
for i, r := range letters {
|
||||
m.Set(r, i)
|
||||
nums = append(nums, i)
|
||||
}
|
||||
|
||||
gotLetters := []string{}
|
||||
gotNums := []int{}
|
||||
m.Range(func(k string, v int) bool {
|
||||
gotLetters = append(gotLetters, k)
|
||||
gotNums = append(gotNums, v)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Equal(t, letters, gotLetters)
|
||||
assert.Equal(t, nums, gotNums)
|
||||
|
||||
n, ok := m.Get(letters[0])
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, nums[0], n)
|
||||
})
|
||||
|
||||
t.Run("clear", func(t *testing.T) {
|
||||
lastLetter := letters[len(letters)-1]
|
||||
m.Del(lastLetter)
|
||||
|
||||
_, ok := m.Get(lastLetter)
|
||||
assert.False(t, ok)
|
||||
|
||||
m.Clear()
|
||||
|
||||
gotLetters := []string{}
|
||||
m.Range(func(k string, _ int) bool {
|
||||
gotLetters = append(gotLetters, k)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Len(t, gotLetters, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedMap_nil(t *testing.T) {
|
||||
const (
|
||||
key = "key"
|
||||
val = "val"
|
||||
)
|
||||
|
||||
var m SortedMap[string, string]
|
||||
|
||||
assert.Panics(t, func() {
|
||||
m.Set(key, val)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
_, ok := m.Get(key)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
m.Range(func(_, _ string) (cont bool) {
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
m.Del(key)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
m.Clear()
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user