diff --git a/dataext/orderedMap.go b/dataext/orderedMap.go index e1a29f8..569c97d 100644 --- a/dataext/orderedMap.go +++ b/dataext/orderedMap.go @@ -6,15 +6,28 @@ import ( ) // OrderedMap is like a normal map[TKey, TVal] - but its elements stay in order +// NOT THREADSAFE !!! type OrderedMap[TKey comparable, TVal any] struct { m map[TKey]*TVal a []TKey } -func NewOrderedMap[TKey comparable, TVal any](cap int) *OrderedMap[TKey, TVal] { +func NewOrderedMap[TKey comparable, TVal any](caps ...int) *OrderedMap[TKey, TVal] { + if len(caps) == 0 { + return &OrderedMap[TKey, TVal]{ + m: make(map[TKey]*TVal), + a: make([]TKey, 0), + } + } + + omcap := 0 + for _, v := range caps { + omcap += v + } + return &OrderedMap[TKey, TVal]{ - m: make(map[TKey]*TVal, cap), - a: make([]TKey, 0, cap), + m: make(map[TKey]*TVal, omcap), + a: make([]TKey, 0, omcap), } } @@ -85,7 +98,17 @@ func (o *OrderedMap[TKey, TVal]) Remove(key TKey) bool { return false } -func (o *OrderedMap[TKey, TVal]) Iterate() iter.Seq[TVal] { +func (o *OrderedMap[TKey, TVal]) Iterate() iter.Seq2[TKey, TVal] { + return func(yield func(TKey, TVal) bool) { + for _, v := range o.a { + if !yield(v, *o.m[v]) { + return + } + } + } +} + +func (o *OrderedMap[TKey, TVal]) IterateValues() iter.Seq[TVal] { return func(yield func(TVal) bool) { for _, v := range o.a { if !yield(*o.m[v]) { diff --git a/dataext/orderedMap_test.go b/dataext/orderedMap_test.go index 4777149..069065c 100644 --- a/dataext/orderedMap_test.go +++ b/dataext/orderedMap_test.go @@ -15,6 +15,22 @@ func TestOrderedMapNew(t *testing.T) { tst.AssertArrayEqual(t, m.Array(), []int{}) } +func TestOrderedMapNewNoCap(t *testing.T) { + m := NewOrderedMap[string, int]() + + tst.AssertEqual(t, m.Size(), 0) + tst.AssertArrayEqual(t, m.Keys(), []string{}) + tst.AssertArrayEqual(t, m.Array(), []int{}) +} + +func TestOrderedMapNewWIthCap(t *testing.T) { + m := NewOrderedMap[string, int](4) + + tst.AssertEqual(t, m.Size(), 0) + tst.AssertArrayEqual(t, m.Keys(), []string{}) + tst.AssertArrayEqual(t, m.Array(), []int{}) +} + func TestOrderedMapAddAndGet(t *testing.T) { m := NewOrderedMap[string, int](0) @@ -238,7 +254,7 @@ func TestOrderedMapIterate(t *testing.T) { m.Add("c", 3) got := make([]int, 0) - for v := range m.Iterate() { + for v := range m.IterateValues() { got = append(got, v) } @@ -252,7 +268,7 @@ func TestOrderedMapIterateBreak(t *testing.T) { m.Add("c", 3) got := make([]int, 0) - for v := range m.Iterate() { + for v := range m.IterateValues() { got = append(got, v) if v == 2 { break @@ -262,6 +278,61 @@ func TestOrderedMapIterateBreak(t *testing.T) { tst.AssertArrayEqual(t, got, []int{1, 2}) } +func TestOrderedMapIterateSeq2(t *testing.T) { + m := NewOrderedMap[string, int](0) + m.Add("a", 1) + m.Add("b", 2) + m.Add("c", 3) + + gotKeys := make([]string, 0) + gotVals := make([]int, 0) + for k, v := range m.Iterate() { + gotKeys = append(gotKeys, k) + gotVals = append(gotVals, v) + } + + tst.AssertArrayEqual(t, gotKeys, []string{"a", "b", "c"}) + tst.AssertArrayEqual(t, gotVals, []int{1, 2, 3}) +} + +func TestOrderedMapIterateSeq2Break(t *testing.T) { + m := NewOrderedMap[string, int](0) + m.Add("a", 1) + m.Add("b", 2) + m.Add("c", 3) + + gotKeys := make([]string, 0) + gotVals := make([]int, 0) + for k, v := range m.Iterate() { + gotKeys = append(gotKeys, k) + gotVals = append(gotVals, v) + if k == "b" { + break + } + } + + tst.AssertArrayEqual(t, gotKeys, []string{"a", "b"}) + tst.AssertArrayEqual(t, gotVals, []int{1, 2}) +} + +func TestOrderedMapIterateSeq2AfterReorder(t *testing.T) { + m := NewOrderedMap[string, int](0) + m.Add("a", 1) + m.Add("b", 2) + m.Add("c", 3) + m.Add("a", 10) // moves "a" to end with new value + + gotKeys := make([]string, 0) + gotVals := make([]int, 0) + for k, v := range m.Iterate() { + gotKeys = append(gotKeys, k) + gotVals = append(gotVals, v) + } + + tst.AssertArrayEqual(t, gotKeys, []string{"b", "c", "a"}) + tst.AssertArrayEqual(t, gotVals, []int{2, 3, 10}) +} + func TestOrderedMapIterateKeys(t *testing.T) { m := NewOrderedMap[string, int](0) m.Add("a", 1) diff --git a/exerr/listener.go b/exerr/listener.go index 3c27492..38a83dc 100644 --- a/exerr/listener.go +++ b/exerr/listener.go @@ -2,8 +2,15 @@ package exerr import ( "sync" + + "git.blackforestbytes.com/BlackForestBytes/goext/dataext" + "git.blackforestbytes.com/BlackForestBytes/goext/langext" ) +type ListenerKey struct { + _k string +} + type ListenerOpt struct { NoLog bool } @@ -11,20 +18,30 @@ type ListenerOpt struct { type Listener = func(method Method, v *ExErr, opt ListenerOpt) var listenerLock = sync.Mutex{} -var listener = make([]Listener, 0) +var listener = dataext.NewOrderedMap[ListenerKey, Listener]() -func RegisterListener(l Listener) { +func RegisterListener(l Listener) ListenerKey { listenerLock.Lock() defer listenerLock.Unlock() - listener = append(listener, l) + k := ListenerKey{_k: langext.MustHexUUID()} + + listener.Add(k, l) + return k +} + +func DeregisterListener(key ListenerKey) { + listenerLock.Lock() + defer listenerLock.Unlock() + + listener.Remove(key) } func (ee *ExErr) CallListener(m Method, opt ListenerOpt) { listenerLock.Lock() defer listenerLock.Unlock() - for _, v := range listener { + for _, v := range listener.Iterate() { v(m, ee, opt) } } diff --git a/goextVersion.go b/goextVersion.go index 60fa30f..fefdd68 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.643" +const GoextVersion = "0.0.644" -const GoextVersionTimestamp = "2026-05-30T00:07:10+0200" +const GoextVersionTimestamp = "2026-05-30T00:13:03+0200"