Skip to content

Commit

Permalink
Add ToPlainMap/ToPlainMapOf utility functions (#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
puzpuzpuz authored Jan 18, 2025
1 parent 5f67a12 commit ac72527
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-32-bit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.19.x, 1.20.x, 1.21.x]
go-version: [1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x]
name: Build with Go ${{ matrix.go-version }} 32-bit
steps:
- uses: actions/checkout@v3
Expand Down
9 changes: 1 addition & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.19.x, 1.20.x, 1.21.x, 1.22.x]
go-version: [1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x]
name: Build with Go ${{ matrix.go-version }}
steps:
- uses: actions/checkout@v3
Expand All @@ -19,13 +19,6 @@ jobs:
- name: Run vet
run: go vet ./...

- name: Run staticcheck
uses: dominikh/[email protected]
with:
version: "2023.1.7"
install-go: false
cache-key: ${{ matrix.go-version }}

- name: Run tests
run: go test -v ./...

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.21.x]
go-version: [1.23.x]
name: Build with Go ${{ matrix.go-version }}
steps:
- uses: actions/checkout@v3
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: lint
on: [push]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.21.x]
name: Build with Go ${{ matrix.go-version }}
steps:
- uses: actions/checkout@v3

- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}

- name: Run staticcheck
uses: dominikh/[email protected]
with:
version: "2023.1.7"
install-go: false
cache-key: ${{ matrix.go-version }}
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ m.Store(Point{42, 42}, 42)
v, ok := m.Load(point{42, 42})
```

Both maps use the built-in Golang's hash function which has DDOS protection. This means that each map instance gets its own seed number and the hash function uses that seed for hash code calculation. However, for smaller keys this hash function has some overhead. So, if you don't need DDOS protection, you may provide a custom hash function when creating a `MapOf`. For instance, Murmur3 finalizer does a decent job when it comes to integers:
Apart from `Range` method available for map iteration, there are also `ToPlainMap`/`ToPlainMapOf` utility functions to convert a `Map`/`MapOf` to a built-in Go's `map`:
```go
m := xsync.NewMapOf[int, int]()
m.Store(42, 42)
pm := xsync.ToPlainMapOf(m)
```

Both `Map` and `MapOf` use the built-in Golang's hash function which has DDOS protection. This means that each map instance gets its own seed number and the hash function uses that seed for hash code calculation. However, for smaller keys this hash function has some overhead. So, if you don't need DDOS protection, you may provide a custom hash function when creating a `MapOf`. For instance, Murmur3 finalizer does a decent job when it comes to integers:

```go
m := NewMapOfWithHasher[int, int](func(i int, _ uint64) uint64 {
Expand Down
15 changes: 15 additions & 0 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ func newMapTable(minTableLen int) *mapTable {
return t
}

// ToPlainMap returns a native map with a copy of xsync Map's
// contents. The copied xsync Map should not be modified while
// this call is made. If the copied Map is modified, the copying
// behavior is the same as in the Range method.
func ToPlainMap(m *Map) map[string]interface{} {
pm := make(map[string]interface{})
if m != nil {
m.Range(func(key string, value interface{}) bool {
pm[key] = value
return true
})
}
return pm
}

// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
Expand Down
24 changes: 24 additions & 0 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,30 @@ func TestMapStats(t *testing.T) {
}
}

func TestToPlainMap_NilPointer(t *testing.T) {
pm := ToPlainMap(nil)
if len(pm) != 0 {
t.Fatalf("got unexpected size of nil map copy: %d", len(pm))
}
}

func TestToPlainMap(t *testing.T) {
const numEntries = 1000
m := NewMap()
for i := 0; i < numEntries; i++ {
m.Store(strconv.Itoa(i), i)
}
pm := ToPlainMap(m)
if len(pm) != numEntries {
t.Fatalf("got unexpected size of nil map copy: %d", len(pm))
}
for i := 0; i < numEntries; i++ {
if v := pm[strconv.Itoa(i)]; v != i {
t.Fatalf("unexpected value for key %d: %d", i, v)
}
}
}

func BenchmarkMap_NoWarmUp(b *testing.B) {
for _, bc := range benchmarkCases {
if bc.readPercentage == 100 {
Expand Down
15 changes: 15 additions & 0 deletions mapof.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ func newMapOfTable[K comparable, V any](minTableLen int) *mapOfTable[K, V] {
return t
}

// ToPlainMapOf returns a native map with a copy of xsync Map's
// contents. The copied xsync Map should not be modified while
// this call is made. If the copied Map is modified, the copying
// behavior is the same as in the Range method.
func ToPlainMapOf[K comparable, V any](m *MapOf[K, V]) map[K]V {
pm := make(map[K]V)
if m != nil {
m.Range(func(key K, value V) bool {
pm[key] = value
return true
})
}
return pm
}

// Load returns the value stored in the map for a key, or zero value
// of type V if no value is present.
// The ok result indicates whether value was found in the map.
Expand Down
24 changes: 24 additions & 0 deletions mapof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,30 @@ func TestMapOfStats(t *testing.T) {
}
}

func TestToPlainMapOf_NilPointer(t *testing.T) {
pm := ToPlainMapOf[int, int](nil)
if len(pm) != 0 {
t.Fatalf("got unexpected size of nil map copy: %d", len(pm))
}
}

func TestToPlainMapOf(t *testing.T) {
const numEntries = 1000
m := NewMapOf[int, int]()
for i := 0; i < numEntries; i++ {
m.Store(i, i)
}
pm := ToPlainMapOf[int, int](m)
if len(pm) != numEntries {
t.Fatalf("got unexpected size of nil map copy: %d", len(pm))
}
for i := 0; i < numEntries; i++ {
if v := pm[i]; v != i {
t.Fatalf("unexpected value for key %d: %d", i, v)
}
}
}

func BenchmarkMapOf_NoWarmUp(b *testing.B) {
for _, bc := range benchmarkCases {
if bc.readPercentage == 100 {
Expand Down

0 comments on commit ac72527

Please sign in to comment.