github.com/wangyougui/gf/v2@v2.6.5/container/gmap/gmap_hash_str_str_map.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with gm file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 // 7 8 package gmap 9 10 import ( 11 "github.com/wangyougui/gf/v2/internal/empty" 12 "github.com/wangyougui/gf/v2/internal/json" 13 "github.com/wangyougui/gf/v2/internal/rwmutex" 14 "github.com/wangyougui/gf/v2/util/gconv" 15 ) 16 17 // StrStrMap implements map[string]string with RWMutex that has switch. 18 type StrStrMap struct { 19 mu rwmutex.RWMutex 20 data map[string]string 21 } 22 23 // NewStrStrMap returns an empty StrStrMap object. 24 // The parameter `safe` is used to specify whether using map in concurrent-safety, 25 // which is false in default. 26 func NewStrStrMap(safe ...bool) *StrStrMap { 27 return &StrStrMap{ 28 data: make(map[string]string), 29 mu: rwmutex.Create(safe...), 30 } 31 } 32 33 // NewStrStrMapFrom creates and returns a hash map from given map `data`. 34 // Note that, the param `data` map will be set as the underlying data map(no deep copy), 35 // there might be some concurrent-safe issues when changing the map outside. 36 func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap { 37 return &StrStrMap{ 38 mu: rwmutex.Create(safe...), 39 data: data, 40 } 41 } 42 43 // Iterator iterates the hash map readonly with custom callback function `f`. 44 // If `f` returns true, then it continues iterating; or false to stop. 45 func (m *StrStrMap) Iterator(f func(k string, v string) bool) { 46 m.mu.RLock() 47 defer m.mu.RUnlock() 48 for k, v := range m.data { 49 if !f(k, v) { 50 break 51 } 52 } 53 } 54 55 // Clone returns a new hash map with copy of current map data. 56 func (m *StrStrMap) Clone() *StrStrMap { 57 return NewStrStrMapFrom(m.MapCopy(), m.mu.IsSafe()) 58 } 59 60 // Map returns the underlying data map. 61 // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data, 62 // or else a pointer to the underlying data. 63 func (m *StrStrMap) Map() map[string]string { 64 m.mu.RLock() 65 defer m.mu.RUnlock() 66 if !m.mu.IsSafe() { 67 return m.data 68 } 69 data := make(map[string]string, len(m.data)) 70 for k, v := range m.data { 71 data[k] = v 72 } 73 return data 74 } 75 76 // MapStrAny returns a copy of the underlying data of the map as map[string]interface{}. 77 func (m *StrStrMap) MapStrAny() map[string]interface{} { 78 m.mu.RLock() 79 data := make(map[string]interface{}, len(m.data)) 80 for k, v := range m.data { 81 data[k] = v 82 } 83 m.mu.RUnlock() 84 return data 85 } 86 87 // MapCopy returns a copy of the underlying data of the hash map. 88 func (m *StrStrMap) MapCopy() map[string]string { 89 m.mu.RLock() 90 defer m.mu.RUnlock() 91 data := make(map[string]string, len(m.data)) 92 for k, v := range m.data { 93 data[k] = v 94 } 95 return data 96 } 97 98 // FilterEmpty deletes all key-value pair of which the value is empty. 99 // Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty. 100 func (m *StrStrMap) FilterEmpty() { 101 m.mu.Lock() 102 for k, v := range m.data { 103 if empty.IsEmpty(v) { 104 delete(m.data, k) 105 } 106 } 107 m.mu.Unlock() 108 } 109 110 // Set sets key-value to the hash map. 111 func (m *StrStrMap) Set(key string, val string) { 112 m.mu.Lock() 113 if m.data == nil { 114 m.data = make(map[string]string) 115 } 116 m.data[key] = val 117 m.mu.Unlock() 118 } 119 120 // Sets batch sets key-values to the hash map. 121 func (m *StrStrMap) Sets(data map[string]string) { 122 m.mu.Lock() 123 if m.data == nil { 124 m.data = data 125 } else { 126 for k, v := range data { 127 m.data[k] = v 128 } 129 } 130 m.mu.Unlock() 131 } 132 133 // Search searches the map with given `key`. 134 // Second return parameter `found` is true if key was found, otherwise false. 135 func (m *StrStrMap) Search(key string) (value string, found bool) { 136 m.mu.RLock() 137 if m.data != nil { 138 value, found = m.data[key] 139 } 140 m.mu.RUnlock() 141 return 142 } 143 144 // Get returns the value by given `key`. 145 func (m *StrStrMap) Get(key string) (value string) { 146 m.mu.RLock() 147 if m.data != nil { 148 value = m.data[key] 149 } 150 m.mu.RUnlock() 151 return 152 } 153 154 // Pop retrieves and deletes an item from the map. 155 func (m *StrStrMap) Pop() (key, value string) { 156 m.mu.Lock() 157 defer m.mu.Unlock() 158 for key, value = range m.data { 159 delete(m.data, key) 160 return 161 } 162 return 163 } 164 165 // Pops retrieves and deletes `size` items from the map. 166 // It returns all items if size == -1. 167 func (m *StrStrMap) Pops(size int) map[string]string { 168 m.mu.Lock() 169 defer m.mu.Unlock() 170 if size > len(m.data) || size == -1 { 171 size = len(m.data) 172 } 173 if size == 0 { 174 return nil 175 } 176 var ( 177 index = 0 178 newMap = make(map[string]string, size) 179 ) 180 for k, v := range m.data { 181 delete(m.data, k) 182 newMap[k] = v 183 index++ 184 if index == size { 185 break 186 } 187 } 188 return newMap 189 } 190 191 // doSetWithLockCheck checks whether value of the key exists with mutex.Lock, 192 // if not exists, set value to the map with given `key`, 193 // or else just return the existing value. 194 // 195 // It returns value with given `key`. 196 func (m *StrStrMap) doSetWithLockCheck(key string, value string) string { 197 m.mu.Lock() 198 defer m.mu.Unlock() 199 if m.data == nil { 200 m.data = make(map[string]string) 201 } 202 if v, ok := m.data[key]; ok { 203 return v 204 } 205 m.data[key] = value 206 return value 207 } 208 209 // GetOrSet returns the value by key, 210 // or sets value with given `value` if it does not exist and then returns this value. 211 func (m *StrStrMap) GetOrSet(key string, value string) string { 212 if v, ok := m.Search(key); !ok { 213 return m.doSetWithLockCheck(key, value) 214 } else { 215 return v 216 } 217 } 218 219 // GetOrSetFunc returns the value by key, 220 // or sets value with returned value of callback function `f` if it does not exist 221 // and then returns this value. 222 func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string { 223 if v, ok := m.Search(key); !ok { 224 return m.doSetWithLockCheck(key, f()) 225 } else { 226 return v 227 } 228 } 229 230 // GetOrSetFuncLock returns the value by key, 231 // or sets value with returned value of callback function `f` if it does not exist 232 // and then returns this value. 233 // 234 // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f` 235 // with mutex.Lock of the hash map. 236 func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string { 237 if v, ok := m.Search(key); !ok { 238 m.mu.Lock() 239 defer m.mu.Unlock() 240 if m.data == nil { 241 m.data = make(map[string]string) 242 } 243 if v, ok = m.data[key]; ok { 244 return v 245 } 246 v = f() 247 m.data[key] = v 248 return v 249 } else { 250 return v 251 } 252 } 253 254 // SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true. 255 // It returns false if `key` exists, and `value` would be ignored. 256 func (m *StrStrMap) SetIfNotExist(key string, value string) bool { 257 if !m.Contains(key) { 258 m.doSetWithLockCheck(key, value) 259 return true 260 } 261 return false 262 } 263 264 // SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true. 265 // It returns false if `key` exists, and `value` would be ignored. 266 func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool { 267 if !m.Contains(key) { 268 m.doSetWithLockCheck(key, f()) 269 return true 270 } 271 return false 272 } 273 274 // SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true. 275 // It returns false if `key` exists, and `value` would be ignored. 276 // 277 // SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that 278 // it executes function `f` with mutex.Lock of the hash map. 279 func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool { 280 if !m.Contains(key) { 281 m.mu.Lock() 282 defer m.mu.Unlock() 283 if m.data == nil { 284 m.data = make(map[string]string) 285 } 286 if _, ok := m.data[key]; !ok { 287 m.data[key] = f() 288 } 289 return true 290 } 291 return false 292 } 293 294 // Removes batch deletes values of the map by keys. 295 func (m *StrStrMap) Removes(keys []string) { 296 m.mu.Lock() 297 if m.data != nil { 298 for _, key := range keys { 299 delete(m.data, key) 300 } 301 } 302 m.mu.Unlock() 303 } 304 305 // Remove deletes value from map by given `key`, and return this deleted value. 306 func (m *StrStrMap) Remove(key string) (value string) { 307 m.mu.Lock() 308 if m.data != nil { 309 var ok bool 310 if value, ok = m.data[key]; ok { 311 delete(m.data, key) 312 } 313 } 314 m.mu.Unlock() 315 return 316 } 317 318 // Keys returns all keys of the map as a slice. 319 func (m *StrStrMap) Keys() []string { 320 m.mu.RLock() 321 var ( 322 keys = make([]string, len(m.data)) 323 index = 0 324 ) 325 for key := range m.data { 326 keys[index] = key 327 index++ 328 } 329 m.mu.RUnlock() 330 return keys 331 } 332 333 // Values returns all values of the map as a slice. 334 func (m *StrStrMap) Values() []string { 335 m.mu.RLock() 336 var ( 337 values = make([]string, len(m.data)) 338 index = 0 339 ) 340 for _, value := range m.data { 341 values[index] = value 342 index++ 343 } 344 m.mu.RUnlock() 345 return values 346 } 347 348 // Contains checks whether a key exists. 349 // It returns true if the `key` exists, or else false. 350 func (m *StrStrMap) Contains(key string) bool { 351 var ok bool 352 m.mu.RLock() 353 if m.data != nil { 354 _, ok = m.data[key] 355 } 356 m.mu.RUnlock() 357 return ok 358 } 359 360 // Size returns the size of the map. 361 func (m *StrStrMap) Size() int { 362 m.mu.RLock() 363 length := len(m.data) 364 m.mu.RUnlock() 365 return length 366 } 367 368 // IsEmpty checks whether the map is empty. 369 // It returns true if map is empty, or else false. 370 func (m *StrStrMap) IsEmpty() bool { 371 return m.Size() == 0 372 } 373 374 // Clear deletes all data of the map, it will remake a new underlying data map. 375 func (m *StrStrMap) Clear() { 376 m.mu.Lock() 377 m.data = make(map[string]string) 378 m.mu.Unlock() 379 } 380 381 // Replace the data of the map with given `data`. 382 func (m *StrStrMap) Replace(data map[string]string) { 383 m.mu.Lock() 384 m.data = data 385 m.mu.Unlock() 386 } 387 388 // LockFunc locks writing with given callback function `f` within RWMutex.Lock. 389 func (m *StrStrMap) LockFunc(f func(m map[string]string)) { 390 m.mu.Lock() 391 defer m.mu.Unlock() 392 f(m.data) 393 } 394 395 // RLockFunc locks reading with given callback function `f` within RWMutex.RLock. 396 func (m *StrStrMap) RLockFunc(f func(m map[string]string)) { 397 m.mu.RLock() 398 defer m.mu.RUnlock() 399 f(m.data) 400 } 401 402 // Flip exchanges key-value of the map to value-key. 403 func (m *StrStrMap) Flip() { 404 m.mu.Lock() 405 defer m.mu.Unlock() 406 n := make(map[string]string, len(m.data)) 407 for k, v := range m.data { 408 n[v] = k 409 } 410 m.data = n 411 } 412 413 // Merge merges two hash maps. 414 // The `other` map will be merged into the map `m`. 415 func (m *StrStrMap) Merge(other *StrStrMap) { 416 m.mu.Lock() 417 defer m.mu.Unlock() 418 if m.data == nil { 419 m.data = other.MapCopy() 420 return 421 } 422 if other != m { 423 other.mu.RLock() 424 defer other.mu.RUnlock() 425 } 426 for k, v := range other.data { 427 m.data[k] = v 428 } 429 } 430 431 // String returns the map as a string. 432 func (m *StrStrMap) String() string { 433 if m == nil { 434 return "" 435 } 436 b, _ := m.MarshalJSON() 437 return string(b) 438 } 439 440 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 441 func (m StrStrMap) MarshalJSON() ([]byte, error) { 442 m.mu.RLock() 443 defer m.mu.RUnlock() 444 return json.Marshal(m.data) 445 } 446 447 // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. 448 func (m *StrStrMap) UnmarshalJSON(b []byte) error { 449 m.mu.Lock() 450 defer m.mu.Unlock() 451 if m.data == nil { 452 m.data = make(map[string]string) 453 } 454 if err := json.UnmarshalUseNumber(b, &m.data); err != nil { 455 return err 456 } 457 return nil 458 } 459 460 // UnmarshalValue is an interface implement which sets any type of value for map. 461 func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) { 462 m.mu.Lock() 463 defer m.mu.Unlock() 464 m.data = gconv.MapStrStr(value) 465 return 466 } 467 468 // DeepCopy implements interface for deep copy of current type. 469 func (m *StrStrMap) DeepCopy() interface{} { 470 if m == nil { 471 return nil 472 } 473 m.mu.RLock() 474 defer m.mu.RUnlock() 475 data := make(map[string]string, len(m.data)) 476 for k, v := range m.data { 477 data[k] = v 478 } 479 return NewStrStrMapFrom(data, m.mu.IsSafe()) 480 } 481 482 // IsSubOf checks whether the current map is a sub-map of `other`. 483 func (m *StrStrMap) IsSubOf(other *StrStrMap) bool { 484 if m == other { 485 return true 486 } 487 m.mu.RLock() 488 defer m.mu.RUnlock() 489 other.mu.RLock() 490 defer other.mu.RUnlock() 491 for key, value := range m.data { 492 otherValue, ok := other.data[key] 493 if !ok { 494 return false 495 } 496 if otherValue != value { 497 return false 498 } 499 } 500 return true 501 } 502 503 // Diff compares current map `m` with map `other` and returns their different keys. 504 // The returned `addedKeys` are the keys that are in map `m` but not in map `other`. 505 // The returned `removedKeys` are the keys that are in map `other` but not in map `m`. 506 // The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). 507 func (m *StrStrMap) Diff(other *StrStrMap) (addedKeys, removedKeys, updatedKeys []string) { 508 m.mu.RLock() 509 defer m.mu.RUnlock() 510 other.mu.RLock() 511 defer other.mu.RUnlock() 512 513 for key := range m.data { 514 if _, ok := other.data[key]; !ok { 515 removedKeys = append(removedKeys, key) 516 } else if m.data[key] != other.data[key] { 517 updatedKeys = append(updatedKeys, key) 518 } 519 } 520 for key := range other.data { 521 if _, ok := m.data[key]; !ok { 522 addedKeys = append(addedKeys, key) 523 } 524 } 525 return 526 }