diff --git a/cowmap.go b/cowmap.go index 8e7857c..46e5316 100644 --- a/cowmap.go +++ b/cowmap.go @@ -5,84 +5,30 @@ import ( "sync/atomic" ) -type tmap map[any]any - // CowMap is a wrapper of Copy-On-Write map type CowMap struct { - mu sync.Mutex - readable atomic.Value + sync.Map } -// NewCowMap creates a new CowMap instance +// CowMap creates a new CowMap instance func NewCowMap() *CowMap { - m := make(tmap) - c := &CowMap{} - c.readable.Store(m) - return c -} - -// clone creates a copy of the map by iterating over the original map -// and copying its key-value pairs to the new map -func (c *CowMap) clone() tmap { - m := make(tmap) - for k, v := range c.readable.Load().(tmap) { - m[k] = v - } - return m -} - -// Load returns the value stored in the map for a given key, -// or nil if the key is not present. -// The ok result indicates whether the value was found in the map. -func (c *CowMap) Load(key any) (value any, ok bool) { - value, ok = c.readable.Load().(tmap)[key] - return + return &CowMap{} } // Len returns the number of key-value pairs stored in the map -func (c *CowMap) Len() int { - return len(c.readable.Load().(tmap)) -} - -// Store sets the value for a given key by creating a new copy of the map -// and adding the new key-value pair to it -func (c *CowMap) Store(key, value any) { - c.mu.Lock() - defer c.mu.Unlock() - - copy := c.clone() // create a copy of the map - copy[key] = value // add the new key-value pair to the copy - c.readable.Store(copy) // update the atomic value with the new copy -} - -// Delete removes a key-value pair from the map by creating a new copy of the map -// and deleting the specified key from it -func (c *CowMap) Delete(key any) { - c.mu.Lock() - defer c.mu.Unlock() - - copy := c.clone() - delete(copy, key) - c.readable.Store(copy) +func (c *CowMap) Len() uint32 { + var size uint32 + c.Range(func(k, v any) bool { + atomic.AddUint32(&size, 1) + return true + }) + return size } // Clear Removes all key-value pairs from the map func (c *CowMap) Clear() { - c.mu.Lock() - defer c.mu.Unlock() - - m := make(tmap) - c.readable.Store(m) -} - -// Range calls the provided function for each key-value pair in the map, -// stopping the iteration if the function returns false -func (c *CowMap) Range(f func(key, value any) bool) { - m := c.readable.Load().(tmap) - - for k, v := range m { - if !f(k, v) { - break - } - } + c.Range(func(k, v any) bool { + c.Delete(k) + return true + }) } diff --git a/cowmap_test.go b/cowmap_test.go index 8304ba3..554bc6a 100644 --- a/cowmap_test.go +++ b/cowmap_test.go @@ -13,15 +13,6 @@ func Test_NewCowMap(t *testing.T) { assert.NotNil(t, m) assert.NotNil(t, m.readable) } - -func Test_CowMapClone(t *testing.T) { - m := NewCowMap() - for i := 0; i < 100; i++ { - m.Store(i, strconv.Itoa(i)) - } - clone := m.clone() - assert.NotNil(t, clone) - assert.Equal(t, 100, len(clone)) } func Test_CowMapLoad(t *testing.T) { @@ -47,7 +38,7 @@ func Test_CowMapStore(t *testing.T) { m.Store(i, strconv.Itoa(i)) } - assert.Equal(t, 100, m.Len()) + assert.Equal(t, uint32(100), m.Len()) } func Test_CowMapDelete(t *testing.T) { @@ -80,7 +71,7 @@ func Test_CowMapClear(t *testing.T) { } m.Clear() - assert.Equal(t, 0, m.Len()) + assert.Equal(t, uint32(0), m.Len()) } func Test_CowMapLen(t *testing.T) { @@ -88,7 +79,7 @@ func Test_CowMapLen(t *testing.T) { for i := 0; i < 100; i++ { m.Store(i, strconv.Itoa(i)) } - assert.Equal(t, 100, m.Len()) + assert.Equal(t, uint32(100), m.Len()) } func Test_CowMapRange(t *testing.T) { diff --git a/eventbus_test.go b/eventbus_test.go index 6a2434d..f2ccfc9 100644 --- a/eventbus_test.go +++ b/eventbus_test.go @@ -81,7 +81,7 @@ func Test_channelClose(t *testing.T) { err := ch.subscribe(busHandlerOne) assert.Nil(t, err) ch.close() - assert.Equal(t, 0, ch.handlers.Len()) + assert.Equal(t, uint32(0), ch.handlers.Len()) ch.close() }