Repository: puzpuzpuz/xsync (go)

Issue:
Map: make size() public
Hello,

It would be really nice to have this function in the public API:
https://github.com/puzpuzpuz/xsync/blob/d5f0f9d5b79c39bf68bf9771f52f477262972f6f/mapof.go#L325

Thanks!

Pull request description:
Expose map size method
Closes #39

Patch:
diff --git a/.github/workflows/build-1.18.yml b/.github/workflows/build-1.18.yml
index 9b54560..058c88b 100644
--- a/.github/workflows/build-1.18.yml
+++ b/.github/workflows/build-1.18.yml
@@ -6,7 +6,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go-version: [1.18.0-rc1]
+        go-version: [1.18.x]
     name: Build with Go ${{ matrix.go-version }}
     steps:
       - uses: actions/checkout@v2.3.4
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index bb2f0b0..19d15e5 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -6,7 +6,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go-version: [1.18.0-rc1]
+        go-version: [1.18.x]
     name: Build with Go ${{ matrix.go-version }}
     steps:
       - uses: actions/checkout@v2.3.4
diff --git a/README.md b/README.md
index 7e11c60..10b567e 100644
--- a/README.md
+++ b/README.md
@@ -31,19 +31,20 @@ Works better in comparison with a single atomically updated int64 counter in hig
 
 ## Map
 
-A `Map` is like a concurrent hash table based map. It follows the interface of sync.Map.
+A `Map` is like a concurrent hash table based map. It follows the interface of `sync.Map` with a few extensions, like the `Size` method.
 
 ```go
 m := xsync.NewMap()
 m.Store("foo", "bar")
 v, ok := m.Load("foo")
+s := m.Size()
 ```
 
 `Map` uses a modified version of Cache-Line Hash Table (CLHT) data structure: https://github.com/LPD-EPFL/CLHT
 
-CLHT is built around idea to organize the hash table in cache-line-sized buckets, so that on all modern CPUs update operations complete with at most one cache-line transfer. Also, Get operations involve no write to memory, as well as no mutexes or any other sort of locks. Due to this design, in all considered scenarios Map outperforms sync.Map.
+CLHT is built around idea to organize the hash table in cache-line-sized buckets, so that on all modern CPUs update operations complete with minimal cache-line transfer. Also, `Get` operations involve no writes to shared memory, hence no mutexes or any other sort of locks. Due to this design, in all considered scenarios `Map` outperforms `sync.Map`.
 
-One important difference with sync.Map is that only string keys are supported. That's because Golang standard library does not expose the built-in hash functions for `interface{}` values.
+One important difference with `sync.Map` is that only string keys are supported. That's because Golang standard library does not expose the built-in hash functions for `interface{}` values.
 
 `MapOf[V]` is an implementation with parametrized value type. It is available for Go 1.18 or later.
 
diff --git a/map.go b/map.go
index cbb7d68..f57930c 100644
--- a/map.go
+++ b/map.go
@@ -453,8 +453,8 @@ func copyRangeEntries(b *bucket, destEntries *[]rangeEntry) {
 	b.mu.Unlock()
 }
 
-// used in tests to verify the table counter
-func (m *Map) size() int {
+// Size returns current size of the map.
+func (m *Map) Size() int {
 	table := (*mapTable)(atomic.LoadPointer(&m.table))
 	return int(sumSize(table))
 }
diff --git a/mapof.go b/mapof.go
index a45903f..6d3e83c 100644
--- a/mapof.go
+++ b/mapof.go
@@ -321,8 +321,8 @@ func (m *MapOf[V]) Range(f func(key string, value V) bool) {
 	}
 }
 
-// used in tests to verify the table counter
-func (m *MapOf[V]) size() int {
+// Size returns current size of the map.
+func (m *MapOf[V]) Size() int {
 	table := (*mapTable)(atomic.LoadPointer(&m.table))
 	return int(sumSize(table))
 }


Test patch:
diff --git a/export_mapof_test.go b/export_mapof_test.go
index a0c5827..e8d191c 100644
--- a/export_mapof_test.go
+++ b/export_mapof_test.go
@@ -6,7 +6,3 @@ package xsync
 func CollectMapOfStats[V any](m *MapOf[V]) MapStats {
 	return MapStats{m.stats()}
 }
-
-func MapOfSize[V any](m *MapOf[V]) int {
-	return m.size()
-}
diff --git a/export_test.go b/export_test.go
index eabfa00..6bfcc82 100644
--- a/export_test.go
+++ b/export_test.go
@@ -18,10 +18,6 @@ func CollectMapStats(m *Map) MapStats {
 	return MapStats{m.stats()}
 }
 
-func MapSize(m *Map) int {
-	return m.size()
-}
-
 func TopHashMatch(hash, topHashes uint64, idx int) bool {
 	return topHashMatch(hash, topHashes, idx)
 }
diff --git a/map_test.go b/map_test.go
index 97cece4..a19c475 100644
--- a/map_test.go
+++ b/map_test.go
@@ -235,7 +235,7 @@ func TestMapSerialStoreThenLoadAndDelete(t *testing.T) {
 func TestMapSize(t *testing.T) {
 	const numEntries = 1000
 	m := NewMap()
-	size := MapSize(m)
+	size := m.Size()
 	if size != 0 {
 		t.Errorf("zero size expected: %d", size)
 	}
@@ -243,7 +243,7 @@ func TestMapSize(t *testing.T) {
 	for i := 0; i < numEntries; i++ {
 		m.Store(strconv.Itoa(i), i)
 		expectedSize++
-		size := MapSize(m)
+		size := m.Size()
 		if size != expectedSize {
 			t.Errorf("size of %d was expected, got: %d", expectedSize, size)
 		}
@@ -251,7 +251,7 @@ func TestMapSize(t *testing.T) {
 	for i := 0; i < numEntries; i++ {
 		m.Delete(strconv.Itoa(i))
 		expectedSize--
-		size := MapSize(m)
+		size := m.Size()
 		if size != expectedSize {
 			t.Errorf("size of %d was expected, got: %d", expectedSize, size)
 		}
diff --git a/mapof_test.go b/mapof_test.go
index 2e07c5f..7f9fd1f 100644
--- a/mapof_test.go
+++ b/mapof_test.go
@@ -197,7 +197,7 @@ func TestMapOfSerialStoreThenLoadAndDelete(t *testing.T) {
 func TestMapOfSize(t *testing.T) {
 	const numEntries = 1000
 	m := NewMapOf[int]()
-	size := MapOfSize(m)
+	size := m.Size()
 	if size != 0 {
 		t.Errorf("zero size expected: %d", size)
 	}
@@ -205,7 +205,7 @@ func TestMapOfSize(t *testing.T) {
 	for i := 0; i < numEntries; i++ {
 		m.Store(strconv.Itoa(i), i)
 		expectedSize++
-		size := MapOfSize(m)
+		size := m.Size()
 		if size != expectedSize {
 			t.Errorf("size of %d was expected, got: %d", expectedSize, size)
 		}
@@ -213,7 +213,7 @@ func TestMapOfSize(t *testing.T) {
 	for i := 0; i < numEntries; i++ {
 		m.Delete(strconv.Itoa(i))
 		expectedSize--
-		size := MapOfSize(m)
+		size := m.Size()
 		if size != expectedSize {
 			t.Errorf("size of %d was expected, got: %d", expectedSize, size)
 		}