diff --git a/README.MD b/README.MD index 6059c1ae8..7085c7660 100644 --- a/README.MD +++ b/README.MD @@ -66,12 +66,14 @@ func main() { # Contributors +- [aloncn](https://github.com/aloncn) - [chenyang351](https://github.com/chenyang351) - [garfieldkwong](https://gitee.com/garfieldkwong) - [hailaz](https://gitee.com/hailaz) - [johng](https://gitee.com/johng) - [pibigstar](https://github.com/pibigstar) - [qq1054000800](https://gitee.com/qq1054000800) +- [qq976739120](https://github.com/qq976739120) - [wenzi1](https://gitee.com/wenzi1) - [wxkj001](https://github.com/wxkj001) - [ymrjqyy](https://gitee.com/ymrjqyy) diff --git a/README_ZH.MD b/README_ZH.MD index 998f83995..5473bfce5 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -86,12 +86,14 @@ func main() { # 贡献者 +- [aloncn](https://github.com/aloncn) - [chenyang351](https://github.com/chenyang351) - [garfieldkwong](https://gitee.com/garfieldkwong) - [hailaz](https://gitee.com/hailaz) - [johng](https://gitee.com/johng) - [pibigstar](https://github.com/pibigstar) - [qq1054000800](https://gitee.com/qq1054000800) +- [qq976739120](https://github.com/qq976739120) - [wenzi1](https://gitee.com/wenzi1) - [wxkj001](https://github.com/wxkj001) - [ymrjqyy](https://gitee.com/ymrjqyy) diff --git a/TODO.MD b/TODO.MD index 1efa597b9..e456a8c6c 100644 --- a/TODO.MD +++ b/TODO.MD @@ -43,7 +43,7 @@ 1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持; 1. 添加sqlite数据库的单元测试用例; 1. gredis增加cluster支持; - +1. gset.Add/Remove/Contains方法增加批量操作支持; # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/container/gmap/gmap_int_string_map.go b/g/container/gmap/gmap_int_string_map.go index b2bb701c7..f6290f23d 100644 --- a/g/container/gmap/gmap_int_string_map.go +++ b/g/container/gmap/gmap_int_string_map.go @@ -4,12 +4,11 @@ // 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/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" ) type IntStringMap struct { @@ -20,21 +19,21 @@ type IntStringMap struct { // NewIntStringMap returns an empty IntStringMap object. // The param used to specify whether using map with un-concurrent-safety, // which is false in default, means concurrent-safe. -func NewIntStringMap(unsafe...bool) *IntStringMap { +func NewIntStringMap(unsafe ...bool) *IntStringMap { return &IntStringMap{ - m : make(map[int]string), - mu : rwmutex.New(unsafe...), - } + m: make(map[int]string), + mu: rwmutex.New(unsafe...), + } } // NewIntStringMapFrom returns an IntStringMap object from given map . // Notice that, the param map is a type of pointer, // there might be some concurrent-safe issues when changing the map outside. -func NewIntStringMapFrom(m map[int]string, unsafe...bool) *IntStringMap { - return &IntStringMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewIntStringMapFrom(m map[int]string, unsafe ...bool) *IntStringMap { + return &IntStringMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // NewIntStringMapFromArray returns an IntStringMap object from given array. @@ -43,37 +42,37 @@ func NewIntStringMapFrom(m map[int]string, unsafe...bool) *IntStringMap { // // If length of is greater than that of , // the corresponding overflow map values will be the default value of its type. -func NewIntStringMapFromArray(keys []int, values []string, unsafe...bool) *IntStringMap { - m := make(map[int]string) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = "" - } - } - return &IntStringMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewIntStringMapFromArray(keys []int, values []string, unsafe ...bool) *IntStringMap { + m := make(map[int]string) + l := len(values) + for i, k := range keys { + if i < l { + m[k] = values[i] + } else { + m[k] = "" + } + } + return &IntStringMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // Iterator iterates the hash map with custom callback function . // If f returns true, then continue iterating; or false to stop. -func (gm *IntStringMap) Iterator(f func (k int, v string) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } +func (gm *IntStringMap) Iterator(f func(k int, v string) bool) { + gm.mu.RLock() + defer gm.mu.RUnlock() + for k, v := range gm.m { + if !f(k, v) { + break + } + } } // Clone returns a new hash map with copy of current map data. func (gm *IntStringMap) Clone() *IntStringMap { - return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe()) + return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe()) } // Map returns a copy of the data of the hash map. @@ -83,7 +82,7 @@ func (gm *IntStringMap) Map() map[int]string { for k, v := range gm.m { m[k] = v } - gm.mu.RUnlock() + gm.mu.RUnlock() return m } @@ -117,40 +116,40 @@ func (gm *IntStringMap) Get(key int) string { // // It returns value with given . func (gm *IntStringMap) doSetWithLockCheck(key int, value string) string { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value + gm.mu.Lock() + if v, ok := gm.m[key]; ok { + gm.mu.Unlock() + return v + } + gm.m[key] = value + gm.mu.Unlock() + return value } // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (gm *IntStringMap) GetOrSet(key int, value string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } + gm.mu.RLock() + v, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + return gm.doSetWithLockCheck(key, value) + } else { + return v + } } // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist and returns this value. func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } + gm.mu.RLock() + v, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + return gm.doSetWithLockCheck(key, f()) + } else { + return v + } } // GetOrSetFuncLock returns the value by key, @@ -159,31 +158,31 @@ func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } + gm.mu.RLock() + val, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + gm.mu.Lock() + defer gm.mu.Unlock() + if v, ok := gm.m[key]; ok { + return v + } + val = f() + gm.m[key] = val + return val + } else { + return val + } } // SetIfNotExist sets to the map if the does not exist, then return true. // It returns false if exists, and would be ignored. func (gm *IntStringMap) SetIfNotExist(key int, value string) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false + if !gm.Contains(key) { + gm.doSetWithLockCheck(key, value) + return true + } + return false } // SetIfNotExistFunc sets value with return value of callback function , then return true. @@ -213,117 +212,116 @@ func (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool { return false } - // BatchRemove batch deletes values of the map by keys. func (gm *IntStringMap) BatchRemove(keys []int) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() + gm.mu.Lock() + for _, key := range keys { + delete(gm.m, key) + } + gm.mu.Unlock() } // Remove deletes value from map by given , and return this deleted value. func (gm *IntStringMap) Remove(key int) string { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val + gm.mu.Lock() + val, exists := gm.m[key] + if exists { + delete(gm.m, key) + } + gm.mu.Unlock() + return val } // Keys returns all keys of the map as a slice. func (gm *IntStringMap) Keys() []int { - gm.mu.RLock() - keys := make([]int, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys + gm.mu.RLock() + keys := make([]int, 0) + for key, _ := range gm.m { + keys = append(keys, key) + } + gm.mu.RUnlock() + return keys } // Values returns all values of the map as a slice. func (gm *IntStringMap) Values() []string { - gm.mu.RLock() - vals := make([]string, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals + gm.mu.RLock() + vals := make([]string, 0) + for _, val := range gm.m { + vals = append(vals, val) + } + gm.mu.RUnlock() + return vals } // Contains checks whether a key exists. // It returns true if the exists, or else false. func (gm *IntStringMap) Contains(key int) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists + gm.mu.RLock() + _, exists := gm.m[key] + gm.mu.RUnlock() + return exists } // Size returns the size of the map. func (gm *IntStringMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length + gm.mu.RLock() + length := len(gm.m) + gm.mu.RUnlock() + return length } // IsEmpty checks whether the map is empty. // It returns true if map is empty, or else false. func (gm *IntStringMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty + gm.mu.RLock() + empty := len(gm.m) == 0 + gm.mu.RUnlock() + return empty } // Clear deletes all data of the map, it will remake a new underlying map data map. func (gm *IntStringMap) Clear() { - gm.mu.Lock() - gm.m = make(map[int]string) - gm.mu.Unlock() + gm.mu.Lock() + gm.m = make(map[int]string) + gm.mu.Unlock() } // LockFunc locks writing with given callback function and mutex.Lock. func (gm *IntStringMap) LockFunc(f func(m map[int]string)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) + gm.mu.Lock() + defer gm.mu.Unlock() + f(gm.m) } // RLockFunc locks reading with given callback function and mutex.RLock. func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) + gm.mu.RLock() + defer gm.mu.RUnlock() + f(gm.m) } // Flip exchanges key-value of the map, it will change key-value to value-key. func (gm *IntStringMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[int]string, len(gm.m)) - for k, v := range gm.m { - n[gconv.Int(v)] = gconv.String(k) - } - gm.m = n + gm.mu.Lock() + defer gm.mu.Unlock() + n := make(map[int]string, len(gm.m)) + for k, v := range gm.m { + n[gconv.Int(v)] = gconv.String(k) + } + gm.m = n } // Merge merges two hash maps. // The map will be merged into the map . func (gm *IntStringMap) Merge(other *IntStringMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} \ No newline at end of file + gm.mu.Lock() + defer gm.mu.Unlock() + if other != gm { + other.mu.RLock() + defer other.mu.RUnlock() + } + for k, v := range other.m { + gm.m[k] = v + } +} diff --git a/g/container/gmap/gmap_string_int_map.go b/g/container/gmap/gmap_string_int_map.go index 052d956e3..028f82c74 100644 --- a/g/container/gmap/gmap_string_int_map.go +++ b/g/container/gmap/gmap_string_int_map.go @@ -8,8 +8,8 @@ package gmap import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" ) type StringIntMap struct { @@ -20,21 +20,21 @@ type StringIntMap struct { // NewStringIntMap returns an empty StringIntMap object. // The param used to specify whether using map with un-concurrent-safety, // which is false in default, means concurrent-safe. -func NewStringIntMap(unsafe...bool) *StringIntMap { +func NewStringIntMap(unsafe ...bool) *StringIntMap { return &StringIntMap{ - m : make(map[string]int), - mu : rwmutex.New(unsafe...), - } + m: make(map[string]int), + mu: rwmutex.New(unsafe...), + } } // NewStringIntMapFrom returns an StringIntMap object from given map . // Notice that, the param map is a type of pointer, // there might be some concurrent-safe issues when changing the map outside. -func NewStringIntMapFrom(m map[string]int, unsafe...bool) *StringIntMap { - return &StringIntMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewStringIntMapFrom(m map[string]int, unsafe ...bool) *StringIntMap { + return &StringIntMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // NewStringIntMapFromArray returns an StringIntMap object from given array. @@ -43,48 +43,48 @@ func NewStringIntMapFrom(m map[string]int, unsafe...bool) *StringIntMap { // // If length of is greater than that of , // the corresponding overflow map values will be the default value of its type. -func NewStringIntMapFromArray(keys []string, values []int, unsafe...bool) *StringIntMap { - m := make(map[string]int) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = 0 - } - } - return &StringIntMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewStringIntMapFromArray(keys []string, values []int, unsafe ...bool) *StringIntMap { + m := make(map[string]int) + l := len(values) + for i, k := range keys { + if i < l { + m[k] = values[i] + } else { + m[k] = 0 + } + } + return &StringIntMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // Iterator iterates the hash map with custom callback function . // If f returns true, then continue iterating; or false to stop. -func (gm *StringIntMap) Iterator(f func (k string, v int) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } +func (gm *StringIntMap) Iterator(f func(k string, v int) bool) { + gm.mu.RLock() + defer gm.mu.RUnlock() + for k, v := range gm.m { + if !f(k, v) { + break + } + } } // Clone returns a new hash map with copy of current map data. func (gm *StringIntMap) Clone() *StringIntMap { - return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe()) + return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe()) } // Map returns a copy of the data of the hash map. func (gm *StringIntMap) Map() map[string]int { - m := make(map[string]int) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m + m := make(map[string]int) + gm.mu.RLock() + for k, v := range gm.m { + m[k] = v + } + gm.mu.RUnlock() + return m } // Set sets key-value to the hash map. @@ -107,7 +107,7 @@ func (gm *StringIntMap) BatchSet(m map[string]int) { func (gm *StringIntMap) Get(key string) int { gm.mu.RLock() val, _ := gm.m[key] - gm.mu.RUnlock() + gm.mu.RUnlock() return val } @@ -117,41 +117,41 @@ func (gm *StringIntMap) Get(key string) int { // // It returns value with given . func (gm *StringIntMap) doSetWithLockCheck(key string, value int) int { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value + gm.mu.Lock() + if v, ok := gm.m[key]; ok { + gm.mu.Unlock() + return v + } + gm.m[key] = value + gm.mu.Unlock() + return value } // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (gm *StringIntMap) GetOrSet(key string, value int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } + gm.mu.RLock() + v, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + return gm.doSetWithLockCheck(key, value) + } else { + return v + } } // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist // and returns this value. func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } + gm.mu.RLock() + v, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + return gm.doSetWithLockCheck(key, f()) + } else { + return v + } } // GetOrSetFuncLock returns the value by key, @@ -161,31 +161,31 @@ func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } + gm.mu.RLock() + val, ok := gm.m[key] + gm.mu.RUnlock() + if !ok { + gm.mu.Lock() + defer gm.mu.Unlock() + if v, ok := gm.m[key]; ok { + return v + } + val = f() + gm.m[key] = val + return val + } else { + return val + } } // SetIfNotExist sets to the map if the does not exist, then return true. // It returns false if exists, and would be ignored. func (gm *StringIntMap) SetIfNotExist(key string, value int) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false + if !gm.Contains(key) { + gm.doSetWithLockCheck(key, value) + return true + } + return false } // SetIfNotExistFunc sets value with return value of callback function , then return true. @@ -217,114 +217,114 @@ func (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool { // BatchRemove batch deletes values of the map by keys. func (gm *StringIntMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() + gm.mu.Lock() + for _, key := range keys { + delete(gm.m, key) + } + gm.mu.Unlock() } // Remove deletes value from map by given , and return this deleted value. func (gm *StringIntMap) Remove(key string) int { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val + gm.mu.Lock() + val, exists := gm.m[key] + if exists { + delete(gm.m, key) + } + gm.mu.Unlock() + return val } // Keys returns all keys of the map as a slice. func (gm *StringIntMap) Keys() []string { - gm.mu.RLock() - keys := make([]string, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys + gm.mu.RLock() + keys := make([]string, 0) + for key, _ := range gm.m { + keys = append(keys, key) + } + gm.mu.RUnlock() + return keys } // Values returns all values of the map as a slice. func (gm *StringIntMap) Values() []int { - gm.mu.RLock() - vals := make([]int, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals + gm.mu.RLock() + vals := make([]int, 0) + for _, val := range gm.m { + vals = append(vals, val) + } + gm.mu.RUnlock() + return vals } // Contains checks whether a key exists. // It returns true if the exists, or else false. func (gm *StringIntMap) Contains(key string) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists + gm.mu.RLock() + _, exists := gm.m[key] + gm.mu.RUnlock() + return exists } // Size returns the size of the map. func (gm *StringIntMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length + gm.mu.RLock() + length := len(gm.m) + gm.mu.RUnlock() + return length } // IsEmpty checks whether the map is empty. // It returns true if map is empty, or else false. func (gm *StringIntMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty + gm.mu.RLock() + empty := len(gm.m) == 0 + gm.mu.RUnlock() + return empty } // Clear deletes all data of the map, it will remake a new underlying map data map. func (gm *StringIntMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]int) - gm.mu.Unlock() + gm.mu.Lock() + gm.m = make(map[string]int) + gm.mu.Unlock() } // LockFunc locks writing with given callback function and mutex.Lock. func (gm *StringIntMap) LockFunc(f func(m map[string]int)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) + gm.mu.Lock() + defer gm.mu.Unlock() + f(gm.m) } // RLockFunc locks reading with given callback function and mutex.RLock. func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) + gm.mu.RLock() + defer gm.mu.RUnlock() + f(gm.m) } // Flip exchanges key-value of the map, it will change key-value to value-key. func (gm *StringIntMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[string]int, len(gm.m)) - for k, v := range gm.m { - n[gconv.String(v)] = gconv.Int(k) - } - gm.m = n + gm.mu.Lock() + defer gm.mu.Unlock() + n := make(map[string]int, len(gm.m)) + for k, v := range gm.m { + n[gconv.String(v)] = gconv.Int(k) + } + gm.m = n } // Merge merges two hash maps. // The map will be merged into the map . func (gm *StringIntMap) Merge(other *StringIntMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} \ No newline at end of file + gm.mu.Lock() + defer gm.mu.Unlock() + if other != gm { + other.mu.RLock() + defer other.mu.RUnlock() + } + for k, v := range other.m { + gm.m[k] = v + } +} diff --git a/g/container/gmap/gmap_string_interface_map.go b/g/container/gmap/gmap_string_interface_map.go index 3fab490e9..5c354ea89 100644 --- a/g/container/gmap/gmap_string_interface_map.go +++ b/g/container/gmap/gmap_string_interface_map.go @@ -20,21 +20,21 @@ type StringInterfaceMap struct { // NewStringInterfaceMap returns an empty StringInterfaceMap object. // The param used to specify whether using map with un-concurrent-safety, // which is false in default, means concurrent-safe. -func NewStringInterfaceMap(unsafe...bool) *StringInterfaceMap { +func NewStringInterfaceMap(unsafe ...bool) *StringInterfaceMap { return &StringInterfaceMap{ - m : make(map[string]interface{}), - mu : rwmutex.New(unsafe...), + m: make(map[string]interface{}), + mu: rwmutex.New(unsafe...), } } // NewStringInterfaceMapFrom returns an StringInterfaceMap object from given map . // Notice that, the param map is a type of pointer, // there might be some concurrent-safe issues when changing the map outside. -func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe...bool) *StringInterfaceMap { - return &StringInterfaceMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe ...bool) *StringInterfaceMap { + return &StringInterfaceMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array. @@ -43,25 +43,25 @@ func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe...bool) *StringI // // If length of is greater than that of , // the corresponding overflow map values will be the default value of its type. -func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe...bool) *StringInterfaceMap { - m := make(map[string]interface{}) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = interface{}(nil) - } - } - return &StringInterfaceMap{ - m : m, - mu : rwmutex.New(unsafe...), - } +func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe ...bool) *StringInterfaceMap { + m := make(map[string]interface{}) + l := len(values) + for i, k := range keys { + if i < l { + m[k] = values[i] + } else { + m[k] = interface{}(nil) + } + } + return &StringInterfaceMap{ + m: m, + mu: rwmutex.New(unsafe...), + } } // Iterator iterates the hash map with custom callback function . // If f returns true, then continue iterating; or false to stop. -func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) { +func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) bool) { gm.mu.RLock() defer gm.mu.RUnlock() for k, v := range gm.m { @@ -73,18 +73,18 @@ func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) { // Clone returns a new hash map with copy of current map data. func (gm *StringInterfaceMap) Clone() *StringInterfaceMap { - return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe()) + return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe()) } // Map returns a copy of the data of the hash map. func (gm *StringInterfaceMap) Map() map[string]interface{} { - m := make(map[string]interface{}) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m + m := make(map[string]interface{}) + gm.mu.RLock() + for k, v := range gm.m { + m[k] = v + } + gm.mu.RUnlock() + return m } // Set sets key-value to the hash map. @@ -96,11 +96,11 @@ func (gm *StringInterfaceMap) Set(key string, val interface{}) { // BatchSet batch sets key-values to the hash map. func (gm *StringInterfaceMap) BatchSet(m map[string]interface{}) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() + gm.mu.Lock() + for k, v := range m { + gm.m[k] = v + } + gm.mu.Unlock() } // Get returns the value by given . @@ -123,37 +123,37 @@ func (gm *StringInterfaceMap) Get(key string) interface{} { func (gm *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} { gm.mu.Lock() defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - if f, ok := value.(func() interface {}); ok { - value = f() - } - if value != nil { - gm.m[key] = value - } - return value + if v, ok := gm.m[key]; ok { + return v + } + if f, ok := value.(func() interface{}); ok { + value = f() + } + if value != nil { + gm.m[key] = value + } + return value } // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (gm *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } + if v := gm.Get(key); v == nil { + return gm.doSetWithLockCheck(key, value) + } else { + return v + } } // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist // and returns this value. func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } + if v := gm.Get(key); v == nil { + return gm.doSetWithLockCheck(key, f()) + } else { + return v + } } // GetOrSetFuncLock returns the value by key, @@ -163,21 +163,21 @@ func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) int // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f) - } else { - return v - } + if v := gm.Get(key); v == nil { + return gm.doSetWithLockCheck(key, f) + } else { + return v + } } // SetIfNotExist sets to the map if the does not exist, then return true. // It returns false if exists, and would be ignored. func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false + if !gm.Contains(key) { + gm.doSetWithLockCheck(key, value) + return true + } + return false } // SetIfNotExistFunc sets value with return value of callback function , then return true. @@ -205,11 +205,11 @@ func (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interfa // BatchRemove batch deletes values of the map by keys. func (gm *StringInterfaceMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() + gm.mu.Lock() + for _, key := range keys { + delete(gm.m, key) + } + gm.mu.Unlock() } // Remove deletes value from map by given , and return this deleted value. @@ -230,7 +230,7 @@ func (gm *StringInterfaceMap) Keys() []string { for key, _ := range gm.m { keys = append(keys, key) } - gm.mu.RUnlock() + gm.mu.RUnlock() return keys } @@ -273,9 +273,9 @@ func (gm *StringInterfaceMap) IsEmpty() bool { // Clear deletes all data of the map, it will remake a new underlying map data map. func (gm *StringInterfaceMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]interface{}) - gm.mu.Unlock() + gm.mu.Lock() + gm.m = make(map[string]interface{}) + gm.mu.Unlock() } // LockFunc locks writing with given callback function and mutex.Lock. @@ -315,4 +315,4 @@ func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) { for k, v := range other.m { gm.m[k] = v } -} \ No newline at end of file +} diff --git a/g/container/gmap/gmap_z_int_bool_test.go b/g/container/gmap/gmap_z_int_bool_test.go new file mode 100644 index 000000000..078bd3077 --- /dev/null +++ b/g/container/gmap/gmap_z_int_bool_test.go @@ -0,0 +1,87 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func getBool() bool { + return true +} +func intBoolCallBack(int, bool) bool { + return true +} +func Test_IntBoolMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewIntBoolMap() + m.Set(1, true) + + gtest.Assert(m.Get(1), true) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet(2, false), false) + gtest.Assert(m.SetIfNotExist(2, false), false) + + gtest.Assert(m.SetIfNotExist(3, false), true) + + gtest.Assert(m.Remove(2), false) + gtest.Assert(m.Contains(2), false) + + gtest.AssertIN(3, m.Keys()) + gtest.AssertIN(1, m.Keys()) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false}) + gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false}) + m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false}) + gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false}) + + }) +} +func Test_IntBoolMap_Set_Fun(t *testing.T) { + m := gmap.NewIntBoolMap() + + m.GetOrSetFunc(1, getBool) + m.GetOrSetFuncLock(2, getBool) + gtest.Assert(m.Get(1), true) + gtest.Assert(m.Get(2), true) + gtest.Assert(m.SetIfNotExistFunc(1, getBool), false) + gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false) +} + +func Test_IntBoolMap_Batch(t *testing.T) { + m := gmap.NewIntBoolMap() + + m.BatchSet(map[int]bool{1: true, 2: false, 3: true}) + m.Iterator(intBoolCallBack) + gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true}) + m.BatchRemove([]int{1, 2}) + gtest.Assert(m.Map(), map[int]bool{3: true}) +} + +func Test_IntBoolMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false}) + + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove(2) + //修改clone map,原 map 不影响 + gtest.AssertIN(2, m.Keys()) +} +func Test_IntBoolMap_Merge(t *testing.T) { + m1 := gmap.NewIntBoolMap() + m2 := gmap.NewIntBoolMap() + m1.Set(1, true) + m2.Set(2, false) + m1.Merge(m2) + gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false}) +} diff --git a/g/container/gmap/gmap_z_int_int_test.go b/g/container/gmap/gmap_z_int_int_test.go new file mode 100644 index 000000000..c7151960e --- /dev/null +++ b/g/container/gmap/gmap_z_int_int_test.go @@ -0,0 +1,91 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func getInt() int { + return 123 +} +func intIntCallBack(int, int) bool { + return true +} +func Test_IntIntMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewIntIntMap() + m.Set(1, 1) + + gtest.Assert(m.Get(1), 1) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet(2, 2), 2) + gtest.Assert(m.SetIfNotExist(2, 2), false) + + gtest.Assert(m.SetIfNotExist(3, 3), true) + + gtest.Assert(m.Remove(2), 2) + gtest.Assert(m.Contains(2), false) + + gtest.AssertIN(3, m.Keys()) + gtest.AssertIN(1, m.Keys()) + gtest.AssertIN(3, m.Values()) + gtest.AssertIN(1, m.Values()) + m.Flip() + gtest.Assert(m.Map(), map[int]int{1: 1, 3: 3}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2}) + gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2}) + m3 := gmap.NewIntIntMapFromArray([]int{1, 2}, []int{1, 2}) + gtest.Assert(m3.Map(), map[int]int{1: 1, 2: 2}) + + }) +} +func Test_IntIntMap_Set_Fun(t *testing.T) { + m := gmap.NewIntIntMap() + + m.GetOrSetFunc(1, getInt) + m.GetOrSetFuncLock(2, getInt) + gtest.Assert(m.Get(1), 123) + gtest.Assert(m.Get(2), 123) + gtest.Assert(m.SetIfNotExistFunc(1, getInt), false) + gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false) +} + +func Test_IntIntMap_Batch(t *testing.T) { + m := gmap.NewIntIntMap() + + m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3}) + m.Iterator(intIntCallBack) + gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3}) + m.BatchRemove([]int{1, 2}) + gtest.Assert(m.Map(), map[int]int{3: 3}) +} + +func Test_IntIntMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2}) + + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove(2) + //修改clone map,原 map 不影响 + gtest.AssertIN(2, m.Keys()) +} +func Test_IntIntMap_Merge(t *testing.T) { + m1 := gmap.NewIntIntMap() + m2 := gmap.NewIntIntMap() + m1.Set(1, 1) + m2.Set(2, 2) + m1.Merge(m2) + gtest.Assert(m1.Map(), map[int]int{1: 1, 2: 2}) +} diff --git a/g/container/gmap/gmap_z_int_interface_test.go b/g/container/gmap/gmap_z_int_interface_test.go new file mode 100644 index 000000000..eb5a47598 --- /dev/null +++ b/g/container/gmap/gmap_z_int_interface_test.go @@ -0,0 +1,91 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func getInterface() interface{} { + return 123 +} +func intInterfaceCallBack(int, interface{}) bool { + return true +} +func Test_IntInterfaceMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewIntInterfaceMap() + m.Set(1, 1) + + gtest.Assert(m.Get(1), 1) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet(2, "2"), "2") + gtest.Assert(m.SetIfNotExist(2, "2"), false) + + gtest.Assert(m.SetIfNotExist(3, 3), true) + + gtest.Assert(m.Remove(2), "2") + gtest.Assert(m.Contains(2), false) + + gtest.AssertIN(3, m.Keys()) + gtest.AssertIN(1, m.Keys()) + gtest.AssertIN(3, m.Values()) + gtest.AssertIN(1, m.Values()) + m.Flip() + gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"}) + gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"}) + m3 := gmap.NewIntInterfaceMapFromArray([]int{1, 2}, []interface{}{1, "2"}) + gtest.Assert(m3.Map(), map[int]interface{}{1: 1, 2: "2"}) + + }) +} +func Test_IntInterfaceMap_Set_Fun(t *testing.T) { + m := gmap.NewIntInterfaceMap() + + m.GetOrSetFunc(1, getInterface) + m.GetOrSetFuncLock(2, getInterface) + gtest.Assert(m.Get(1), 123) + gtest.Assert(m.Get(2), 123) + gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false) + gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false) +} + +func Test_IntInterfaceMap_Batch(t *testing.T) { + m := gmap.NewIntInterfaceMap() + + m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3}) + m.Iterator(intInterfaceCallBack) + gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3}) + m.BatchRemove([]int{1, 2}) + gtest.Assert(m.Map(), map[int]interface{}{3: 3}) +} + +func Test_IntInterfaceMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"}) + + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove(2) + //修改clone map,原 map 不影响 + gtest.AssertIN(2, m.Keys()) +} +func Test_IntInterfaceMap_Merge(t *testing.T) { + m1 := gmap.NewIntInterfaceMap() + m2 := gmap.NewIntInterfaceMap() + m1.Set(1, 1) + m2.Set(2, "2") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"}) +} diff --git a/g/container/gmap/gmap_z_int_string_test.go b/g/container/gmap/gmap_z_int_string_test.go new file mode 100644 index 000000000..386e3a710 --- /dev/null +++ b/g/container/gmap/gmap_z_int_string_test.go @@ -0,0 +1,96 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func getString() string { + return "z" +} +func intStringCallBack(int, string) bool { + return true +} +func Test_IntStringMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewIntStringMap() + m.Set(1, "a") + + gtest.Assert(m.Get(1), "a") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet(2, "b"), "b") + gtest.Assert(m.SetIfNotExist(2, "b"), false) + + gtest.Assert(m.SetIfNotExist(3, "c"), true) + + gtest.Assert(m.Remove(2), "b") + gtest.Assert(m.Contains(2), false) + + gtest.AssertIN(3, m.Keys()) + gtest.AssertIN(1, m.Keys()) + gtest.AssertIN("a", m.Values()) + gtest.AssertIN("c", m.Values()) + + //反转之后不成为以下 map,flip 操作只是翻转原 map + //gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3}) + m_f := gmap.NewIntStringMap() + m_f.Set(1, "2") + m_f.Flip() + gtest.Assert(m_f.Map(), map[int]string{2: "1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"}) + gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"}) + m3 := gmap.NewIntStringMapFromArray([]int{1, 2}, []string{"a", "b"}) + gtest.Assert(m3.Map(), map[int]string{1: "a", 2: "b"}) + + }) +} +func Test_IntStringMap_Set_Fun(t *testing.T) { + m := gmap.NewIntStringMap() + + m.GetOrSetFunc(1, getString) + m.GetOrSetFuncLock(2, getString) + gtest.Assert(m.Get(1), "z") + gtest.Assert(m.Get(2), "z") + gtest.Assert(m.SetIfNotExistFunc(1, getString), false) + gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false) +} + +func Test_IntStringMap_Batch(t *testing.T) { + m := gmap.NewIntStringMap() + + m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"}) + m.Iterator(intStringCallBack) + gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"}) + m.BatchRemove([]int{1, 2}) + gtest.Assert(m.Map(), map[int]interface{}{3: "c"}) +} + +func Test_IntStringMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"}) + + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove(2) + //修改clone map,原 map 不影响 + gtest.AssertIN(2, m.Keys()) +} +func Test_IntStringMap_Merge(t *testing.T) { + m1 := gmap.NewIntStringMap() + m2 := gmap.NewIntStringMap() + m1.Set(1, "a") + m2.Set(2, "b") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"}) +} diff --git a/g/container/gmap/gmap_z_interface_interface_basic_test.go b/g/container/gmap/gmap_z_interface_interface_basic_test.go new file mode 100644 index 000000000..ead2dae73 --- /dev/null +++ b/g/container/gmap/gmap_z_interface_interface_basic_test.go @@ -0,0 +1,91 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func getValue() interface{} { + return 3 +} +func callBack(k interface{}, v interface{}) bool { + return true +} +func Test_Map_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.New() + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Flip() + gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + m3 := gmap.NewFromArray([]interface{}{1, "key1"}, []interface{}{1, "val1"}) + gtest.Assert(m3.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + + }) +} +func Test_Map_Set_Fun(t *testing.T) { + m := gmap.New() + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_Map_Batch(t *testing.T) { + m := gmap.New() + m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Iterator(callBack) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.BatchRemove([]interface{}{"key1", 1}) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) +} + +func Test_Map_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} +func Test_Map_Basic_Merge(t *testing.T) { + m1 := gmap.New() + m2 := gmap.New() + m1.Set("key1", "val1") + m2.Set("key2", "val2") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"}) +} diff --git a/g/container/gmap/gmap_z_normal_example_test.go b/g/container/gmap/gmap_z_normal_example_test.go new file mode 100644 index 000000000..b134c5ff8 --- /dev/null +++ b/g/container/gmap/gmap_z_normal_example_test.go @@ -0,0 +1,71 @@ +package gmap_test + +import ( + "fmt" + "github.com/gogf/gf/g/container/gmap" +) + +func Example_Normal_Basic() { + m := gmap.New() + + //Add data + m.Set("key1", "val1") + + //Print size + fmt.Println(m.Size()) + //output 1 + + add_map := make(map[interface{}]interface{}) + add_map["key2"] = "val2" + add_map["key3"] = "val3" + add_map[1] = 1 + + fmt.Println(m.Values()) + + //Batch add data + m.BatchSet(add_map) + + //Gets the value of the corresponding key + key3_val := m.Get("key3") + fmt.Println(key3_val) + + //Get the value by key, or set it with given key-value if not exist. + get_or_set_val := m.GetOrSet("key4", "val4") + fmt.Println(get_or_set_val) + + // Set key-value if the key does not exist, then return true; or else return false. + is_set := m.SetIfNotExist("key3", "val3") + fmt.Println(is_set) + + //Remove key + m.Remove("key2") + fmt.Println(m.Keys()) + + //Batch remove keys + remove_keys := []interface{}{"key1", 1} + m.BatchRemove(remove_keys) + fmt.Println(m.Keys()) + + //Contains checks whether a key exists. + is_contain := m.Contains("key3") + fmt.Println(is_contain) + + //Flip exchanges key-value of the map, it will change key-value to value-key. + m.Flip() + fmt.Println(m.Map()) + + // Clear deletes all data of the map, + m.Clear() + + fmt.Println(m.Size()) + +} +func Example_Normal_Merge(){ + m1 := gmap.New() + m2 := gmap.New() + m1.Set("key1","val1") + m2.Set("key2","val2") + m1.Merge(m2) + fmt.Println(m1.Map()) +} + diff --git a/g/container/gmap/gmap_z_string_bool_test.go b/g/container/gmap/gmap_z_string_bool_test.go new file mode 100644 index 000000000..63b251ed3 --- /dev/null +++ b/g/container/gmap/gmap_z_string_bool_test.go @@ -0,0 +1,85 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + + +func StringBoolCallBack( string, bool) bool { + return true +} +func Test_StringBoolMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewStringBoolMap() + m.Set("a", true) + + gtest.Assert(m.Get("a"), true) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("b", false), false) + gtest.Assert(m.SetIfNotExist("b", false), false) + + gtest.Assert(m.SetIfNotExist("c", false), true) + + gtest.Assert(m.Remove("b"), false) + gtest.Assert(m.Contains("b"), false) + + gtest.AssertIN("c", m.Keys()) + gtest.AssertIN("a", m.Keys()) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false}) + gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false}) + m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false}) + gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false}) + + }) +} +func Test_StringBoolMap_Set_Fun(t *testing.T) { + m := gmap.NewStringBoolMap() + + m.GetOrSetFunc("a", getBool) + m.GetOrSetFuncLock("b", getBool) + gtest.Assert(m.Get("a"), true) + gtest.Assert(m.Get("b"), true) + gtest.Assert(m.SetIfNotExistFunc("a", getBool), false) + gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false) +} + +func Test_StringBoolMap_Batch(t *testing.T) { + m := gmap.NewStringBoolMap() + + m.BatchSet(map[string]bool{"a": true, "b": false, "c": true}) + m.Iterator(StringBoolCallBack) + gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true}) + m.BatchRemove([]string{"a", "b"}) + gtest.Assert(m.Map(), map[string]bool{"c": true}) +} + +func Test_StringBoolMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false}) + + m_clone := m.Clone() + m.Remove("a") + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN("a", m_clone.Keys()) + + m_clone.Remove("b") + //修改clone map,原 map 不影响 + gtest.AssertIN("b", m.Keys()) +} +func Test_StringBoolMap_Merge(t *testing.T) { + m1 := gmap.NewStringBoolMap() + m2 := gmap.NewStringBoolMap() + m1.Set("a", true) + m2.Set("b", false) + m1.Merge(m2) + gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false}) +} diff --git a/g/container/gmap/gmap_z_string_int_test.go b/g/container/gmap/gmap_z_string_int_test.go new file mode 100644 index 000000000..7c1050d46 --- /dev/null +++ b/g/container/gmap/gmap_z_string_int_test.go @@ -0,0 +1,91 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func stringIntCallBack(string, int) bool { + return true +} +func Test_StringIntMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewStringIntMap() + m.Set("a", 1) + + gtest.Assert(m.Get("a"), 1) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("b", 2), 2) + gtest.Assert(m.SetIfNotExist("b", 2), false) + + gtest.Assert(m.SetIfNotExist("c", 3), true) + + gtest.Assert(m.Remove("b"), 2) + gtest.Assert(m.Contains("b"), false) + + gtest.AssertIN("c", m.Keys()) + gtest.AssertIN("a", m.Keys()) + gtest.AssertIN(3, m.Values()) + gtest.AssertIN(1, m.Values()) + + m_f := gmap.NewStringIntMap() + m_f.Set("1", 2) + m_f.Flip() + gtest.Assert(m_f.Map(), map[string]int{"2": 1}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2}) + gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2}) + m3 := gmap.NewStringIntMapFromArray([]string{"a", "b"}, []int{1, 2}) + gtest.Assert(m3.Map(), map[string]int{"a": 1, "b": 2}) + + }) +} +func Test_StringIntMap_Set_Fun(t *testing.T) { + m := gmap.NewStringIntMap() + + m.GetOrSetFunc("a", getInt) + m.GetOrSetFuncLock("b", getInt) + gtest.Assert(m.Get("a"), 123) + gtest.Assert(m.Get("b"), 123) + gtest.Assert(m.SetIfNotExistFunc("a", getInt), false) + gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false) +} + +func Test_StringIntMap_Batch(t *testing.T) { + m := gmap.NewStringIntMap() + + m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3}) + m.Iterator(stringIntCallBack) + gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3}) + m.BatchRemove([]string{"a", "b"}) + gtest.Assert(m.Map(), map[string]int{"c": 3}) +} + +func Test_StringIntMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3}) + + m_clone := m.Clone() + m.Remove("a") + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN("a", m_clone.Keys()) + + m_clone.Remove("b") + //修改clone map,原 map 不影响 + gtest.AssertIN("b", m.Keys()) +} +func Test_StringIntMap_Merge(t *testing.T) { + m1 := gmap.NewStringIntMap() + m2 := gmap.NewStringIntMap() + m1.Set("a", 1) + m2.Set("b", 2) + m1.Merge(m2) + gtest.Assert(m1.Map(), map[string]int{"a": 1, "b": 2}) +} diff --git a/g/container/gmap/gmap_z_string_interface_test.go b/g/container/gmap/gmap_z_string_interface_test.go new file mode 100644 index 000000000..74e94115d --- /dev/null +++ b/g/container/gmap/gmap_z_string_interface_test.go @@ -0,0 +1,89 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func stringInterfaceCallBack(string, interface{}) bool { + return true +} +func Test_StringInterfaceMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewStringInterfaceMap() + m.Set("a", 1) + + gtest.Assert(m.Get("a"), 1) + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("b", "2"), "2") + gtest.Assert(m.SetIfNotExist("b", "2"), false) + + gtest.Assert(m.SetIfNotExist("c", 3), true) + + gtest.Assert(m.Remove("b"), "2") + gtest.Assert(m.Contains("b"), false) + + gtest.AssertIN("c", m.Keys()) + gtest.AssertIN("a", m.Keys()) + gtest.AssertIN(3, m.Values()) + gtest.AssertIN(1, m.Values()) + + m.Flip() + gtest.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"}) + gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"}) + m3 := gmap.NewStringInterfaceMapFromArray([]string{"a", "b"}, []interface{}{1, "2"}) + gtest.Assert(m3.Map(), map[string]interface{}{"a": 1, "b": "2"}) + + }) +} +func Test_StringInterfaceMap_Set_Fun(t *testing.T) { + m := gmap.NewStringInterfaceMap() + + m.GetOrSetFunc("a", getInterface) + m.GetOrSetFuncLock("b", getInterface) + gtest.Assert(m.Get("a"), 123) + gtest.Assert(m.Get("b"), 123) + gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false) + gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false) +} + +func Test_StringInterfaceMap_Batch(t *testing.T) { + m := gmap.NewStringInterfaceMap() + + m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3}) + m.Iterator(stringInterfaceCallBack) + gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3}) + m.BatchRemove([]string{"a", "b"}) + gtest.Assert(m.Map(), map[string]interface{}{"c": 3}) +} + +func Test_StringInterfaceMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"}) + + m_clone := m.Clone() + m.Remove("a") + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN("a", m_clone.Keys()) + + m_clone.Remove("b") + //修改clone map,原 map 不影响 + gtest.AssertIN("b", m.Keys()) +} +func Test_StringInterfaceMap_Merge(t *testing.T) { + m1 := gmap.NewStringInterfaceMap() + m2 := gmap.NewStringInterfaceMap() + m1.Set("a", 1) + m2.Set("b", "2") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"}) +} diff --git a/g/container/gmap/gmap_z_string_string_test.go b/g/container/gmap/gmap_z_string_string_test.go new file mode 100644 index 000000000..443878839 --- /dev/null +++ b/g/container/gmap/gmap_z_string_string_test.go @@ -0,0 +1,90 @@ +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func stringStringCallBack(string, string) bool { + return true +} +func Test_StringStringMap_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewStringStringMap() + m.Set("a", "a") + + gtest.Assert(m.Get("a"), "a") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("b", "b"), "b") + gtest.Assert(m.SetIfNotExist("b", "b"), false) + + gtest.Assert(m.SetIfNotExist("c", "c"), true) + + gtest.Assert(m.Remove("b"), "b") + gtest.Assert(m.Contains("b"), false) + + gtest.AssertIN("c", m.Keys()) + gtest.AssertIN("a", m.Keys()) + gtest.AssertIN("a", m.Values()) + gtest.AssertIN("c", m.Values()) + + m.Flip() + + gtest.Assert(m.Map(), map[string]string{"a": "a", "c": "c"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"}) + gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"}) + m3 := gmap.NewStringStringMapFromArray([]string{"a", "b"}, []string{"a", "b"}) + gtest.Assert(m3.Map(), map[string]string{"a": "a", "b": "b"}) + + }) +} +func Test_StringStringMap_Set_Fun(t *testing.T) { + m := gmap.NewStringStringMap() + + m.GetOrSetFunc("a", getString) + m.GetOrSetFuncLock("b", getString) + gtest.Assert(m.Get("a"), "z") + gtest.Assert(m.Get("b"), "z") + gtest.Assert(m.SetIfNotExistFunc("a", getString), false) + gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false) +} + +func Test_StringStringMap_Batch(t *testing.T) { + m := gmap.NewStringStringMap() + + m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"}) + m.Iterator(stringStringCallBack) + gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"}) + m.BatchRemove([]string{"a", "b"}) + gtest.Assert(m.Map(), map[string]string{"c": "c"}) +} + +func Test_StringStringMap_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"}) + + m_clone := m.Clone() + m.Remove("a") + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN("a", m_clone.Keys()) + + m_clone.Remove("b") + //修改clone map,原 map 不影响 + gtest.AssertIN("b", m.Keys()) +} +func Test_StringStringMap_Merge(t *testing.T) { + m1 := gmap.NewStringStringMap() + m2 := gmap.NewStringStringMap() + m1.Set("a", "a") + m2.Set("b", "b") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"}) +} diff --git a/g/container/gring/gring_unit_test.go b/g/container/gring/gring_unit_test.go new file mode 100644 index 000000000..4f209d9df --- /dev/null +++ b/g/container/gring/gring_unit_test.go @@ -0,0 +1,229 @@ +package gring_test + +import ( + "container/ring" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/container/gring" + "github.com/gogf/gf/g/test/gtest" + "testing" +) +type Student struct { + position int + name string + upgrade bool +} + +func TestRing_Val(t *testing.T) { + gtest.Case(t, func() { + //定义cap 为3的ring类型数据 + r := gring.New(3, true) + //分别给3个元素初始化赋值 + r.Put(&Student{1,"jimmy", true}) + r.Put(&Student{2,"tom", true}) + r.Put(&Student{3,"alon", false}) + + //元素取值并判断和预设值是否相等 + gtest.Assert(r.Val().(*Student).name,"jimmy") + //从当前位置往后移两个元素 + r.Move(2) + gtest.Assert(r.Val().(*Student).name,"alon") + //更新元素值 + //测试 value == nil + r.Set(nil) + gtest.Assert(r.Val(),nil) + //测试value != nil + r.Set(&Student{3, "jack", true}) + }) +} +func TestRing_CapLen(t *testing.T) { + gtest.Case(t, func() { + r := gring.New(10) + r.Put("goframe") + //cap长度 10 + gtest.Assert(r.Cap(), 10) + //已有数据项 1 + gtest.Assert(r.Len(), 1) + }) +} + +func TestRing_Position(t *testing.T) { + gtest.Case(t, func() { + r := gring.New(2) + r.Put(1) + r.Put(2) + //往后移动1个元素 + r.Next() + gtest.Assert(r.Val(),2) + //往前移动1个元素 + r.Prev() + gtest.Assert(r.Val(),1) + + }) +} + +func TestRing_Link(t *testing.T) { + gtest.Case(t, func() { + r := gring.New(3) + r.Put(1) + r.Put(2) + r.Put(3) + s := gring.New(2) + s.Put("a") + s.Put("b") + + rs := r.Link(s) + gtest.Assert(rs.Move(2).Val(), "b") + + }) +} + +func TestRing_Unlink(t *testing.T) { + gtest.Case(t, func() { + r := gring.New(5) + for i := 0; i< 5; i++ { + r.Put(i+1) + } + // 1 2 3 4 + // 删除当前位置往后的2个数据,返回被删除的数据 + // 重新计算s len + s := r.Unlink(2) // 2 3 + gtest.Assert(s.Val(), 2) + gtest.Assert(s.Len(), 1) + }) +} + +func TestRing_Slice(t *testing.T) { + gtest.Case(t, func() { + ringLen := 5 + r := gring.New(ringLen) + for i := 0; i< ringLen; i++ { + r.Put(i+1) + } + r.Move(2) // 3 + array := r.SliceNext() // [3 4 5 1 2] + gtest.Assert(array[0], 3) + gtest.Assert(len(array), 5) + + //判断array是否等于[3 4 5 1 2] + ra := []int{3,4,5,1,2} + gtest.Assert(ra, array) + + //第3个元素设为nil + r.Set(nil) + array2 := r.SliceNext() //[4 5 1 2] + //返回当前位置往后不为空的元素数组,长度为4 + gtest.Assert(array2, g.Slice{4,5,1,2}) + + array3 := r.SlicePrev() //[2 1 5 4] + gtest.Assert(array3, g.Slice{2,1,5,4}) + + s := gring.New(ringLen) + for i := 0; i< ringLen; i++ { + s.Put(i+1) + } + array4 := s.SlicePrev() // [] + gtest.Assert(array4, g.Slice{1,5,4,3,2}) + + }) +} + +func TestRing_RLockIterator(t *testing.T) { + gtest.Case(t, func() { + ringLen := 5 + r := gring.New(ringLen) + + //ring不存在有值元素 + r.RLockIteratorNext(func(v interface{}) bool { + gtest.Assert(v, nil) + return false + }) + r.RLockIteratorNext(func(v interface{}) bool { + gtest.Assert(v, nil) + return true + }) + + r.RLockIteratorPrev(func(v interface{}) bool { + gtest.Assert(v, nil) + return true + }) + + for i := 0; i< ringLen; i++ { + r.Put(i+1) + } + + //回调函数返回true,RLockIteratorNext遍历5次,期望值分别是1、2、3、4、5 + i := 0 + r.RLockIteratorNext(func(v interface{}) bool { + gtest.Assert(v, i+1) + i++; + return true + }) + + //RLockIteratorPrev遍历1次返回 false,退出遍历 + r.RLockIteratorPrev(func(v interface{}) bool { + gtest.Assert(v, 1) + return false + }) + + }) +} + +func TestRing_LockIterator(t *testing.T) { + gtest.Case(t, func() { + ringLen := 5 + r := gring.New(ringLen) + + //不存在有值元素 + r.LockIteratorNext(func(item *ring.Ring) bool { + gtest.Assert(item.Value, nil) + return false + }) + r.LockIteratorNext(func(item *ring.Ring) bool { + gtest.Assert(item.Value, nil) + return false + }) + r.LockIteratorNext(func(item *ring.Ring) bool { + gtest.Assert(item.Value, nil) + return true + }) + + r.LockIteratorPrev(func(item *ring.Ring) bool { + gtest.Assert(item.Value, nil) + return false + }) + r.LockIteratorPrev(func(item *ring.Ring) bool { + gtest.Assert(item.Value, nil) + return true + }) + + //ring初始化元素值 + for i := 0; i< ringLen; i++ { + r.Put(i+1) + } + + //往后遍历组成数据 [1,2,3,4,5] + array1 := g.Slice{1,2,3,4,5} + ii := 0 + r.LockIteratorNext(func(item *ring.Ring) bool { + //校验每一次遍历取值是否是期望值 + gtest.Assert(item.Value, array1[ii]) + ii++; + return true + }) + + //往后取3个元素组成数组 + //获得 [1,5,4] + i := 0 + a := g.Slice{1,5,4} + r.LockIteratorPrev(func(item *ring.Ring) bool { + if i > 2 { + return false + } + gtest.Assert(item.Value, a[i]) + i++; + return true + }) + + + }) +} \ No newline at end of file diff --git a/g/encoding/gjson/gjson_unit_basic_test.go b/g/encoding/gjson/gjson_unit_basic_test.go index 790878289..102a2e15b 100644 --- a/g/encoding/gjson/gjson_unit_basic_test.go +++ b/g/encoding/gjson/gjson_unit_basic_test.go @@ -139,9 +139,9 @@ func Test_GetMap(t *testing.T) { gtest.Case(t, func() { j, err := gjson.DecodeToJson(data) gtest.Assert(err, nil) - gtest.Assert(j.GetMap("n"), g.Map{}) + gtest.Assert(j.GetMap("n"), nil) gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"}) - gtest.Assert(j.GetMap("a"), g.Map{}) + gtest.Assert(j.GetMap("a"), nil) }) } @@ -189,7 +189,7 @@ func Test_GetStrings(t *testing.T) { gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"}) gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`}) gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"}) - gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{}) + gtest.AssertEQ(j.GetStrings("i"), nil) }) } diff --git a/g/encoding/gparser/gparser_unit_basic_test.go b/g/encoding/gparser/gparser_unit_basic_test.go index bf7067753..4fb894d4f 100644 --- a/g/encoding/gparser/gparser_unit_basic_test.go +++ b/g/encoding/gparser/gparser_unit_basic_test.go @@ -107,9 +107,9 @@ func Test_GetMap(t *testing.T) { gtest.Case(t, func() { j := gparser.New(data) gtest.AssertNE(j, nil) - gtest.Assert(j.GetMap("n"), g.Map{}) + gtest.Assert(j.GetMap("n"), nil) gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"}) - gtest.Assert(j.GetMap("a"), g.Map{}) + gtest.Assert(j.GetMap("a"), nil) }) } @@ -144,7 +144,7 @@ func Test_GetStrings(t *testing.T) { gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"}) gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`}) gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"}) - gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{}) + gtest.AssertEQ(j.GetStrings("i"), nil) }) } diff --git a/g/internal/cmdenv/cmdenv.go b/g/internal/cmdenv/cmdenv.go index 31e7475bc..9dd9a8d08 100644 --- a/g/internal/cmdenv/cmdenv.go +++ b/g/internal/cmdenv/cmdenv.go @@ -7,12 +7,27 @@ package cmdenv import ( - "github.com/gogf/gf/g/container/gvar" - "github.com/gogf/gf/g/os/gcmd" - "github.com/gogf/gf/g/os/genv" - "strings" + "github.com/gogf/gf/g/container/gvar" + "os" + "regexp" + "strings" ) +var ( + // Console options. + cmdOptions = make(map[string]string) +) + +func init() { + reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`) + for i := 0; i < len(os.Args); i++ { + result := reg.FindStringSubmatch(os.Args[i]) + if len(result) > 1 { + cmdOptions[result[1]] = result[2] + } + } +} + // 获取指定名称的命令行参数,当不存在时获取环境变量参数,皆不存在时,返回给定的默认值。 // 规则: // 1、命令行参数以小写字母格式,使用: gf.包名.变量名 传递; @@ -22,11 +37,11 @@ func Get(key string, def...interface{}) gvar.VarRead { if len(def) > 0 { value = def[0] } - if v := gcmd.Option.Get(key); v != "" { + if v, ok := cmdOptions[key]; ok { value = v } else { key = strings.ToUpper(strings.Replace(key, ".", "_", -1)) - if v := genv.Get(key); v != "" { + if v := os.Getenv(key); v != "" { value = v } } diff --git a/g/net/ghttp/ghttp_server_router_group.go b/g/net/ghttp/ghttp_server_router_group.go index cd90806f8..ad3dd3f2d 100644 --- a/g/net/ghttp/ghttp_server_router_group.go +++ b/g/net/ghttp/ghttp_server_router_group.go @@ -26,13 +26,13 @@ type GroupItem = []interface{} // 获取分组路由对象 func (s *Server) Group(prefix...string) *RouterGroup { + group := &RouterGroup{ + server : s, + } if len(prefix) > 0 { - return &RouterGroup{ - server : s, - prefix : prefix[0], - } + group.prefix = prefix[0] } - return &RouterGroup{} + return group } // 获取分组路由对象 diff --git a/g/os/gcmd/gcmd.go b/g/os/gcmd/gcmd.go index 4fe997010..a5601806f 100644 --- a/g/os/gcmd/gcmd.go +++ b/g/os/gcmd/gcmd.go @@ -6,33 +6,28 @@ // // Package gcmd provides console operations, like options/values reading and command running. -// -// 命令行管理. package gcmd import ( + "github.com/gogf/gf/g/os/glog" "os" - "errors" - "regexp" + "regexp" ) -// 命令行参数列表 +// Console values. type gCmdValue struct { values []string } -// 命令行选项列表 +// Console options. type gCmdOption struct { options map[string]string } -// 终端管理对象(全局) -var Value = &gCmdValue{} // 终端参数-命令参数列表 -var Option = &gCmdOption{} // 终端参数-选项参数列表 -var cmdFuncMap = make(map[string]func()) // 终端命令及函数地址对应表 +var Value = &gCmdValue{} // Console values. +var Option = &gCmdOption{} // Console options. +var cmdFuncMap = make(map[string]func()) // Registered callback functions. -// 检查并初始化console参数,在包加载的时候触发 -// 初始化时执行,不影响运行时性能 func init() { reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`) Option.options = make(map[string]string) @@ -46,67 +41,34 @@ func init() { } } -// 返回所有的命令行参数values -func (c *gCmdValue) GetAll() []string { - return c.values -} - -// 返回所有的命令行参数options -func (c *gCmdOption) GetAll() map[string]string { - return c.options -} - -// 获得一条指定索引位置的value参数 -func (c *gCmdValue) Get(index uint8, def...string) string { - if index < uint8(len(c.values)) { - return c.values[index] - } else if len(def) > 0 { - return def[0] - } - return "" -} - -// 获得一条指定索引位置的option参数; -func (c *gCmdOption) Get(key string, def...string) string { - if option, ok := c.options[key]; ok { - return option - } else if len(def) > 0 { - return def[0] - } - return "" -} - -// 绑定命令行参数及对应的命令函数,注意命令函数参数是函数的内存地址 -// 如果操作失败返回错误信息 -func BindHandle (cmd string, f func()) error { +// BindHandle registers callback function with . +func BindHandle (cmd string, f func()) { if _, ok := cmdFuncMap[cmd]; ok { - return errors.New("duplicated handle for command:" + cmd) + glog.Fatal("duplicated handle for command:" + cmd) } else { cmdFuncMap[cmd] = f - return nil } } -// 执行命令对应的函数 -func RunHandle (cmd string) error { +// RunHandle executes the callback function registered by . +func RunHandle (cmd string) { if handle, ok := cmdFuncMap[cmd]; ok { handle() - return nil } else { - return errors.New("no handle found for command:" + cmd) + glog.Fatal("no handle found for command:" + cmd) } } -// 自动识别命令参数并执行命令参数对应的函数 -func AutoRun () error { +// AutoRun automatically recognizes and executes the callback function +// by value of index 0 (the first console parameter). +func AutoRun () { if cmd := Value.Get(1); cmd != "" { if handle, ok := cmdFuncMap[cmd]; ok { handle() - return nil } else { - return errors.New("no handle found for command:" + cmd) + glog.Fatal("no handle found for command:" + cmd) } } else { - return errors.New("no command found") + glog.Fatal("no command found") } } diff --git a/g/os/gcmd/gcmd_option.go b/g/os/gcmd/gcmd_option.go new file mode 100644 index 000000000..4cc0147f4 --- /dev/null +++ b/g/os/gcmd/gcmd_option.go @@ -0,0 +1,32 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gcmd + +import "github.com/gogf/gf/g/container/gvar" + +// GetAll returns all option values as map[string]string. +func (c *gCmdOption) GetAll() map[string]string { + return c.options +} + +// Get returns the option value string specified by , +// if value dose not exist, then returns . +func (c *gCmdOption) Get(key string, def...string) string { + if option, ok := c.options[key]; ok { + return option + } else if len(def) > 0 { + return def[0] + } + return "" +} + +// GetVar returns the option value as gvar.VarRead object specified by , +// if value does not exist, then returns as its default value. +func (c *gCmdOption) GetVar(key string, def...string) gvar.VarRead { + return gvar.NewRead(c.Get(key, def...), true) +} diff --git a/g/os/gcmd/gcmd_value.go b/g/os/gcmd/gcmd_value.go new file mode 100644 index 000000000..b1144b6ac --- /dev/null +++ b/g/os/gcmd/gcmd_value.go @@ -0,0 +1,32 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gcmd + +import "github.com/gogf/gf/g/container/gvar" + +// GetAll returns all values as a slice of string. +func (c *gCmdValue) GetAll() []string { + return c.values +} + +// Get returns value by index as string, +// if value does not exist, then returns . +func (c *gCmdValue) Get(index int, def...string) string { + if index < len(c.values) { + return c.values[index] + } else if len(def) > 0 { + return def[0] + } + return "" +} + +// GetVar returns value by index as gvar.VarRead object, +// if value does not exist, then returns as its default value. +func (c *gCmdValue) GetVar(index int, def...string) gvar.VarRead { + return gvar.NewRead(c.Get(index, def...), true) +} diff --git a/g/test/gtest/gtest.go b/g/test/gtest/gtest.go index 0b520e0a8..89256a481 100644 --- a/g/test/gtest/gtest.go +++ b/g/test/gtest/gtest.go @@ -32,12 +32,9 @@ func Case(t *testing.T, f func()) { // Assert checks and EQUAL. func Assert(value, expect interface{}) { - rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) - if rvValue.Kind() == reflect.Ptr { - if rvValue.IsNil() { - value = nil - } + if isNil(value) { + value = nil } if rvExpect.Kind() == reflect.Map { if err := compareMap(value, expect); err != nil { @@ -53,13 +50,10 @@ func Assert(value, expect interface{}) { // AssertEQ checks and EQUAL, including their TYPES. func AssertEQ(value, expect interface{}) { // Value assert. - rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) - if rvValue.Kind() == reflect.Ptr { - if rvValue.IsNil() { - value = nil - } - } + if isNil(value) { + value = nil + } if rvExpect.Kind() == reflect.Map { if err := compareMap(value, expect); err != nil { panic(err) @@ -79,13 +73,10 @@ func AssertEQ(value, expect interface{}) { // AssertNE checks and NOT EQUAL. func AssertNE(value, expect interface{}) { - rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) - if rvValue.Kind() == reflect.Ptr { - if rvValue.IsNil() { - value = nil - } - } + if isNil(value) { + value = nil + } if rvExpect.Kind() == reflect.Map { if err := compareMap(value, expect); err == nil { panic(fmt.Sprintf(`[ASSERT] EXPECT %v != %v`, value, expect)) @@ -259,11 +250,9 @@ func Fatal(message...interface{}) { func compareMap(value, expect interface{}) error { rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) - if rvValue.Kind() == reflect.Ptr { - if rvValue.IsNil() { - value = nil - } - } + if isNil(value) { + value = nil + } if rvExpect.Kind() == reflect.Map { if rvValue.Kind() == reflect.Map { if rvExpect.Len() == rvValue.Len() { @@ -336,4 +325,15 @@ func getBacktrace(skip...int) string { } } return backtrace +} + +// isNil checks whether is nil. +func isNil(value interface{}) bool { + rv := reflect.ValueOf(value) + switch rv.Kind() { + case reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr, reflect.Func: + return rv.IsNil() + default: + return value == nil + } } \ No newline at end of file diff --git a/g/text/gstr/gstr.go b/g/text/gstr/gstr.go index fc03ce3bc..b1dad21fd 100644 --- a/g/text/gstr/gstr.go +++ b/g/text/gstr/gstr.go @@ -594,4 +594,21 @@ func QuoteMeta(str string) string { buf.WriteRune(char) } return buf.String() +} + +// SearchArray searches string in string slice case-sensitively, +// returns its index in . +// If is not found in , it returns -1. +func SearchArray (a []string, s string) int { + for i, v := range a { + if s == v { + return i + } + } + return -1 +} + +// InArray checks whether string in slice . +func InArray (a []string, s string) bool { + return SearchArray(a, s) != -1 } \ No newline at end of file diff --git a/g/text/gstr/gstr_z_unit_basic_test.go b/g/text/gstr/gstr_z_unit_basic_test.go index b69939236..d15e312db 100644 --- a/g/text/gstr/gstr_z_unit_basic_test.go +++ b/g/text/gstr/gstr_z_unit_basic_test.go @@ -383,3 +383,22 @@ func Test_ContainsAny(t *testing.T) { }) } +func Test_SearchArray(t *testing.T) { + gtest.Case(t, func() { + a := g.SliceStr{"a", "b", "c"} + gtest.AssertEQ(gstr.SearchArray(a, "a"), 0) + gtest.AssertEQ(gstr.SearchArray(a, "b"), 1) + gtest.AssertEQ(gstr.SearchArray(a, "c"), 2) + gtest.AssertEQ(gstr.SearchArray(a, "d"), -1) + }) +} + +func Test_InArray(t *testing.T) { + gtest.Case(t, func() { + a := g.SliceStr{"a", "b", "c"} + gtest.AssertEQ(gstr.InArray(a, "a"), true) + gtest.AssertEQ(gstr.InArray(a, "b"), true) + gtest.AssertEQ(gstr.InArray(a, "c"), true) + gtest.AssertEQ(gstr.InArray(a, "d"), false) + }) +} diff --git a/g/util/grand/grand_intn.go b/g/util/grand/grand_intn.go index de8d6244c..14dcc032e 100644 --- a/g/util/grand/grand_intn.go +++ b/g/util/grand/grand_intn.go @@ -7,8 +7,9 @@ package grand import ( - "crypto/rand" - "encoding/binary" + "crypto/rand" + "encoding/binary" + "os" ) const ( @@ -27,6 +28,7 @@ func init() { for { if n, err := rand.Read(buffer); err != nil { panic(err) + os.Exit(1) } else { // 使用缓冲区数据进行一次完整的随机数生成 for i := 0; i < n - 4; { @@ -34,10 +36,18 @@ func init() { i ++ } // 充分利用缓冲区数据,随机索引递增 - step = int(buffer[0])%10 + for i := 0; i < n; i++ { + step = int(buffer[0])%10 + if step != 0 { + break + } + } + if step == 0 { + step = 2 + } for i := 0; i < n - 4; { - bufferChan <- binary.BigEndian.Uint32(buffer[i : i + 4]) - i += step + bufferChan <- binary.BigEndian.Uint32(buffer[i : i + 4]) + i += step } } } diff --git a/geg/os/gtimer/gtimer-batch.go b/geg/os/gtimer/gtimer-batch.go new file mode 100644 index 000000000..c5d8dcfb9 --- /dev/null +++ b/geg/os/gtimer/gtimer-batch.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/os/gtimer" + "time" +) + +func main() { + for i := 0; i < 100000; i++ { + gtimer.Add(time.Second, func() { + + }) + } + fmt.Println("start") + time.Sleep(48*time.Hour) +} diff --git a/geg/other/test.go b/geg/other/test.go index ed997310b..63996a987 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,9 +2,14 @@ package main import ( "fmt" - "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/util/grand" ) + + func main() { - fmt.Println(gtime.Now().Format(`Y-m-j G:i:su`)) + for { + fmt.Println(grand.Intn(100)) + } + } \ No newline at end of file diff --git a/version.go b/version.go index 0d8a2b6f5..49e1a370b 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.6.0" +const VERSION = "v1.6.2" const AUTHORS = "john"