mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
532 lines
12 KiB
Go
532 lines
12 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with gm file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package gmap
|
|
|
|
import (
|
|
"github.com/gogf/gf/v2/internal/empty"
|
|
"github.com/gogf/gf/v2/internal/json"
|
|
"github.com/gogf/gf/v2/internal/rwmutex"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
// IntStrMap implements map[int]string with RWMutex that has switch.
|
|
type IntStrMap struct {
|
|
mu rwmutex.RWMutex
|
|
data map[int]string
|
|
}
|
|
|
|
// NewIntStrMap returns an empty IntStrMap object.
|
|
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
|
// which is false in default.
|
|
func NewIntStrMap(safe ...bool) *IntStrMap {
|
|
return &IntStrMap{
|
|
mu: rwmutex.Create(safe...),
|
|
data: make(map[int]string),
|
|
}
|
|
}
|
|
|
|
// NewIntStrMapFrom creates and returns a hash map from given map `data`.
|
|
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
|
// there might be some concurrent-safe issues when changing the map outside.
|
|
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
|
|
return &IntStrMap{
|
|
mu: rwmutex.Create(safe...),
|
|
data: data,
|
|
}
|
|
}
|
|
|
|
// Iterator iterates the hash map readonly with custom callback function `f`.
|
|
// If `f` returns true, then it continues iterating; or false to stop.
|
|
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
|
for k, v := range m.Map() {
|
|
if !f(k, v) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clone returns a new hash map with copy of current map data.
|
|
func (m *IntStrMap) Clone() *IntStrMap {
|
|
return NewIntStrMapFrom(m.MapCopy(), m.mu.IsSafe())
|
|
}
|
|
|
|
// Map returns the underlying data map.
|
|
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
|
// or else a pointer to the underlying data.
|
|
func (m *IntStrMap) Map() map[int]string {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
if !m.mu.IsSafe() {
|
|
return m.data
|
|
}
|
|
data := make(map[int]string, len(m.data))
|
|
for k, v := range m.data {
|
|
data[k] = v
|
|
}
|
|
return data
|
|
}
|
|
|
|
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
|
|
func (m *IntStrMap) MapStrAny() map[string]any {
|
|
m.mu.RLock()
|
|
data := make(map[string]any, len(m.data))
|
|
for k, v := range m.data {
|
|
data[gconv.String(k)] = v
|
|
}
|
|
m.mu.RUnlock()
|
|
return data
|
|
}
|
|
|
|
// MapCopy returns a copy of the underlying data of the hash map.
|
|
func (m *IntStrMap) MapCopy() map[int]string {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
data := make(map[int]string, len(m.data))
|
|
for k, v := range m.data {
|
|
data[k] = v
|
|
}
|
|
return data
|
|
}
|
|
|
|
// FilterEmpty deletes all key-value pair of which the value is empty.
|
|
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
|
func (m *IntStrMap) FilterEmpty() {
|
|
m.mu.Lock()
|
|
for k, v := range m.data {
|
|
if empty.IsEmpty(v) {
|
|
delete(m.data, k)
|
|
}
|
|
}
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// Set sets key-value to the hash map.
|
|
func (m *IntStrMap) Set(key int, val string) {
|
|
m.mu.Lock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
m.data[key] = val
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// Sets batch sets key-values to the hash map.
|
|
func (m *IntStrMap) Sets(data map[int]string) {
|
|
m.mu.Lock()
|
|
if m.data == nil {
|
|
m.data = data
|
|
} else {
|
|
for k, v := range data {
|
|
m.data[k] = v
|
|
}
|
|
}
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// Search searches the map with given `key`.
|
|
// Second return parameter `found` is true if key was found, otherwise false.
|
|
func (m *IntStrMap) Search(key int) (value string, found bool) {
|
|
m.mu.RLock()
|
|
if m.data != nil {
|
|
value, found = m.data[key]
|
|
}
|
|
m.mu.RUnlock()
|
|
return
|
|
}
|
|
|
|
// Get returns the value by given `key`.
|
|
func (m *IntStrMap) Get(key int) (value string) {
|
|
m.mu.RLock()
|
|
if m.data != nil {
|
|
value = m.data[key]
|
|
}
|
|
m.mu.RUnlock()
|
|
return
|
|
}
|
|
|
|
// Pop retrieves and deletes an item from the map.
|
|
func (m *IntStrMap) Pop() (key int, value string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
for key, value = range m.data {
|
|
delete(m.data, key)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// Pops retrieves and deletes `size` items from the map.
|
|
// It returns all items if size == -1.
|
|
func (m *IntStrMap) Pops(size int) map[int]string {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if size > len(m.data) || size == -1 {
|
|
size = len(m.data)
|
|
}
|
|
if size == 0 {
|
|
return nil
|
|
}
|
|
var (
|
|
index = 0
|
|
newMap = make(map[int]string, size)
|
|
)
|
|
for k, v := range m.data {
|
|
delete(m.data, k)
|
|
newMap[k] = v
|
|
index++
|
|
if index == size {
|
|
break
|
|
}
|
|
}
|
|
return newMap
|
|
}
|
|
|
|
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
|
// if not exists, set value to the map with given `key`,
|
|
// or else just return the existing value.
|
|
//
|
|
// It returns value with given `key`.
|
|
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
if v, ok := m.data[key]; ok {
|
|
return v
|
|
}
|
|
m.data[key] = value
|
|
return value
|
|
}
|
|
|
|
// GetOrSet returns the value by key,
|
|
// or sets value with given `value` if it does not exist and then returns this value.
|
|
func (m *IntStrMap) GetOrSet(key int, value string) string {
|
|
if v, ok := m.Search(key); !ok {
|
|
return m.doSetWithLockCheck(key, value)
|
|
} else {
|
|
return v
|
|
}
|
|
}
|
|
|
|
// GetOrSetFunc returns the value by key,
|
|
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
|
func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
|
|
if v, ok := m.Search(key); !ok {
|
|
return m.doSetWithLockCheck(key, f())
|
|
} else {
|
|
return v
|
|
}
|
|
}
|
|
|
|
// GetOrSetFuncLock returns the value by key,
|
|
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
|
//
|
|
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
|
// with mutex.Lock of the hash map.
|
|
func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
|
|
if v, ok := m.Search(key); !ok {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
if v, ok = m.data[key]; ok {
|
|
return v
|
|
}
|
|
v = f()
|
|
m.data[key] = v
|
|
return v
|
|
} else {
|
|
return v
|
|
}
|
|
}
|
|
|
|
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
|
// It returns false if `key` exists, and `value` would be ignored.
|
|
func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
|
|
if !m.Contains(key) {
|
|
m.doSetWithLockCheck(key, value)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
|
// It returns false if `key` exists, and `value` would be ignored.
|
|
func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
|
|
if !m.Contains(key) {
|
|
m.doSetWithLockCheck(key, f())
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
|
// It returns false if `key` exists, and `value` would be ignored.
|
|
//
|
|
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
|
// it executes function `f` with mutex.Lock of the hash map.
|
|
func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
|
if !m.Contains(key) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
if _, ok := m.data[key]; !ok {
|
|
m.data[key] = f()
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Removes batch deletes values of the map by keys.
|
|
func (m *IntStrMap) Removes(keys []int) {
|
|
m.mu.Lock()
|
|
if m.data != nil {
|
|
for _, key := range keys {
|
|
delete(m.data, key)
|
|
}
|
|
}
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// Remove deletes value from map by given `key`, and return this deleted value.
|
|
func (m *IntStrMap) Remove(key int) (value string) {
|
|
m.mu.Lock()
|
|
if m.data != nil {
|
|
var ok bool
|
|
if value, ok = m.data[key]; ok {
|
|
delete(m.data, key)
|
|
}
|
|
}
|
|
m.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
// Keys returns all keys of the map as a slice.
|
|
func (m *IntStrMap) Keys() []int {
|
|
m.mu.RLock()
|
|
var (
|
|
keys = make([]int, len(m.data))
|
|
index = 0
|
|
)
|
|
for key := range m.data {
|
|
keys[index] = key
|
|
index++
|
|
}
|
|
m.mu.RUnlock()
|
|
return keys
|
|
}
|
|
|
|
// Values returns all values of the map as a slice.
|
|
func (m *IntStrMap) Values() []string {
|
|
m.mu.RLock()
|
|
var (
|
|
values = make([]string, len(m.data))
|
|
index = 0
|
|
)
|
|
for _, value := range m.data {
|
|
values[index] = value
|
|
index++
|
|
}
|
|
m.mu.RUnlock()
|
|
return values
|
|
}
|
|
|
|
// Contains checks whether a key exists.
|
|
// It returns true if the `key` exists, or else false.
|
|
func (m *IntStrMap) Contains(key int) bool {
|
|
var ok bool
|
|
m.mu.RLock()
|
|
if m.data != nil {
|
|
_, ok = m.data[key]
|
|
}
|
|
m.mu.RUnlock()
|
|
return ok
|
|
}
|
|
|
|
// Size returns the size of the map.
|
|
func (m *IntStrMap) Size() int {
|
|
m.mu.RLock()
|
|
length := len(m.data)
|
|
m.mu.RUnlock()
|
|
return length
|
|
}
|
|
|
|
// IsEmpty checks whether the map is empty.
|
|
// It returns true if map is empty, or else false.
|
|
func (m *IntStrMap) IsEmpty() bool {
|
|
return m.Size() == 0
|
|
}
|
|
|
|
// Clear deletes all data of the map, it will remake a new underlying data map.
|
|
func (m *IntStrMap) Clear() {
|
|
m.mu.Lock()
|
|
m.data = make(map[int]string)
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// Replace the data of the map with given `data`.
|
|
func (m *IntStrMap) Replace(data map[int]string) {
|
|
m.mu.Lock()
|
|
m.data = data
|
|
m.mu.Unlock()
|
|
}
|
|
|
|
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
|
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
f(m.data)
|
|
}
|
|
|
|
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
|
func (m *IntStrMap) RLockFunc(f func(m map[int]string)) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
f(m.data)
|
|
}
|
|
|
|
// Flip exchanges key-value of the map to value-key.
|
|
func (m *IntStrMap) Flip() {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
n := make(map[int]string, len(m.data))
|
|
for k, v := range m.data {
|
|
n[gconv.Int(v)] = gconv.String(k)
|
|
}
|
|
m.data = n
|
|
}
|
|
|
|
// Merge merges two hash maps.
|
|
// The `other` map will be merged into the map `m`.
|
|
func (m *IntStrMap) Merge(other *IntStrMap) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = other.MapCopy()
|
|
return
|
|
}
|
|
if other != m {
|
|
other.mu.RLock()
|
|
defer other.mu.RUnlock()
|
|
}
|
|
for k, v := range other.data {
|
|
m.data[k] = v
|
|
}
|
|
}
|
|
|
|
// String returns the map as a string.
|
|
func (m *IntStrMap) String() string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
b, _ := m.MarshalJSON()
|
|
return string(b)
|
|
}
|
|
|
|
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
|
func (m IntStrMap) MarshalJSON() ([]byte, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return json.Marshal(m.data)
|
|
}
|
|
|
|
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
|
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnmarshalValue is an interface implement which sets any type of value for map.
|
|
func (m *IntStrMap) UnmarshalValue(value any) (err error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.data == nil {
|
|
m.data = make(map[int]string)
|
|
}
|
|
switch value.(type) {
|
|
case string, []byte:
|
|
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
|
default:
|
|
for k, v := range gconv.Map(value) {
|
|
m.data[gconv.Int(k)] = gconv.String(v)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DeepCopy implements interface for deep copy of current type.
|
|
func (m *IntStrMap) DeepCopy() any {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
data := make(map[int]string, len(m.data))
|
|
for k, v := range m.data {
|
|
data[k] = v
|
|
}
|
|
return NewIntStrMapFrom(data, m.mu.IsSafe())
|
|
}
|
|
|
|
// IsSubOf checks whether the current map is a sub-map of `other`.
|
|
func (m *IntStrMap) IsSubOf(other *IntStrMap) bool {
|
|
if m == other {
|
|
return true
|
|
}
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
other.mu.RLock()
|
|
defer other.mu.RUnlock()
|
|
for key, value := range m.data {
|
|
otherValue, ok := other.data[key]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if otherValue != value {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Diff compares current map `m` with map `other` and returns their different keys.
|
|
// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
|
|
// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
|
|
// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
|
|
func (m *IntStrMap) Diff(other *IntStrMap) (addedKeys, removedKeys, updatedKeys []int) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
other.mu.RLock()
|
|
defer other.mu.RUnlock()
|
|
|
|
for key := range m.data {
|
|
if _, ok := other.data[key]; !ok {
|
|
removedKeys = append(removedKeys, key)
|
|
} else if m.data[key] != other.data[key] {
|
|
updatedKeys = append(updatedKeys, key)
|
|
}
|
|
}
|
|
for key := range other.data {
|
|
if _, ok := m.data[key]; !ok {
|
|
addedKeys = append(addedKeys, key)
|
|
}
|
|
}
|
|
return
|
|
}
|