Compare commits

...

78 Commits

Author SHA1 Message Date
6f0aee1cc5 version updates 2019-05-23 20:43:09 +08:00
3575e20137 update Writer feature of glog 2019-05-23 20:34:06 +08:00
f69f529258 Merge branch 'master' into develop 2019-05-23 19:14:19 +08:00
3e7416ca7d add Skip for glog 2019-05-23 19:14:16 +08:00
9da883a50c comment updates 2019-05-23 11:49:33 +08:00
ea3e03aaba example updates for glog 2019-05-23 09:26:37 +08:00
8d9fdfeafc change parameter to unnecssary for glog.Header/Stdout; version updates 2019-05-22 21:43:56 +08:00
b2a8285ecb remove unnecessary pkg gscanner; update comment for gsmtp 2019-05-22 21:20:17 +08:00
162e8f7e51 comment updates for glog 2019-05-22 20:31:14 +08:00
6c658813cd refract glog to improve performance 2019-05-22 09:19:21 +08:00
0839ea385f refract glog to improve performance; 2019-05-21 23:57:03 +08:00
68fa235412 add gjson.GetJsonMap; refract glog to improve performance; 2019-05-21 21:52:17 +08:00
e9f7b8bc0c README updates 2019-05-18 14:55:15 +08:00
e31861af2e README updates 2019-05-18 13:52:18 +08:00
1af482d950 improve gfile.MainPkgPath 2019-05-17 21:37:48 +08:00
dd2436925b remove package feature for gudp, considering UDP protocal has its own border for message handling 2019-05-17 00:17:45 +08:00
b92b69564b improve gfile.MainPkgPath 2019-05-16 23:41:09 +08:00
75725db6fb remove gmlock for print of glog; add time.Time support for gdb 2019-05-15 23:53:48 +08:00
5cd8475143 add time.Time support for convertParam function of gdb 2019-05-15 16:47:39 +08:00
5629f37939 version updates 2019-05-14 22:38:03 +08:00
08ec04d8b6 fix issue in unit test case of gredis 2019-05-14 22:37:13 +08:00
c0b46f364a version updates 2019-05-14 22:02:09 +08:00
8c5f74e8bb add DoVar/ReceiveVar function for gredis 2019-05-14 21:34:38 +08:00
94832262e3 version updates 2019-05-13 22:37:31 +08:00
aefbfd52e9 add more example for gtree 2019-05-13 22:37:05 +08:00
f3f0689bd4 rename LinkMap to ListMap for gmap 2019-05-13 22:26:39 +08:00
5198d4c5fc add unit test cases for gtree.AVLTree/BTree 2019-05-12 22:56:13 +08:00
123f2d3e4e remove return value from RLockFunc/LockFunc for gset 2019-05-12 21:26:01 +08:00
3c750c3c92 copy treemap logics to new file 2019-05-12 21:22:07 +08:00
17b29cd19f improve performance for Map/Keys/Values functions for gmap; add unit test cases for gtree.RedBlackTree 2019-05-12 21:11:26 +08:00
cf1077bec4 add LinkMap for gmap package 2019-05-12 20:43:52 +08:00
4e2e4e95e0 add gmap.HashMap/TreeMap/AnyAnyMap for gmap; add unit test cases for TreeMap 2019-05-11 20:47:25 +08:00
61d64e7ae4 remove gvar.VarRead 2019-05-11 18:47:35 +08:00
883797c495 fix issue in gconv 2019-05-11 18:03:49 +08:00
0113971877 version updates 2019-05-11 17:59:56 +08:00
664b0c06a6 gjson updates 2019-05-11 17:56:14 +08:00
bd4c75a98e add AVLTree container for gtee 2019-05-10 22:29:06 +08:00
d35840409b add BTree container for gtree package 2019-05-10 21:31:35 +08:00
abaef9ba87 add gtree.RedBlackTree container 2019-05-10 13:38:06 +08:00
b15d8bdd2e adding gtree.RedBlackTree 2019-05-09 22:53:42 +08:00
718997327a add default value for gcfg.Get* functions; rename gconv.TimeDuration to gconv.Duration, and do corresponding changes to caller packages 2019-05-09 17:14:24 +08:00
fdfefbb94d add default value feature for gjson/gparser; update default value type for ghttp.Request 2019-05-09 14:19:27 +08:00
2b865a55ac update gjson/gparser 2019-05-08 22:04:36 +08:00
8138215597 comments update 2019-05-08 21:03:04 +08:00
7cc0c7a1cc rename map type of gmap; rename BatchSet/BatchRemove to Sets/Removes for gmap/gcache 2019-05-08 17:21:18 +08:00
4c647aaa19 refacting gmap 2019-05-07 22:28:34 +08:00
48b1d616c5 update code format for gtime 2019-05-07 10:22:50 +08:00
693c37d6d6 Merge pull request #116 from jroam/master
add some var flags gtime.format
2019-05-07 10:18:22 +08:00
d525c04826 公开几个常用功能方法 2019-05-06 15:03:14 +08:00
c170edbdfc add ghttp.Request.GetUrl to get current request URL 2019-05-06 13:52:34 +08:00
66e40155a9 add http/https scheme for log of ghttp.Server; add transport setting to ignore tls cert for ghttp.Client; version updates 2019-05-06 09:35:39 +08:00
59ad1a9b00 fix issue in incorrect every running logics of gcron.AddOnce 2019-05-05 22:57:13 +08:00
a5b536e218 去掉不常用参数e 2019-05-05 22:49:52 +08:00
0e6c2e790d copy mysql driver to third folder 2019-05-04 23:02:07 +08:00
5761e73061 优化一些参数性能 2019-05-04 18:14:05 +08:00
34c761e9db rename mysql driver from 'mysql' to 'gf-mysql' to avoid multiple imports error 2019-05-04 00:25:02 +08:00
87e3813636 remove go-sql-driver-mysql from third, add Register function to manually register 'mysql' driver 2019-05-04 00:10:02 +08:00
361ff0315c version updates 2019-05-03 17:04:42 +08:00
2bb227d058 update gtcp exxamples 2019-05-03 15:47:25 +08:00
99dc69e839 update gtcp exxamples 2019-05-03 13:28:27 +08:00
d78fde8099 edit a func's name 2019-04-30 22:31:52 +08:00
5d0c8956d6 edit some errs and fmt some codes 2019-04-30 11:45:26 +08:00
c9537af062 add package feature for gudp; gtcp updates 2019-04-29 23:54:47 +08:00
dfb5b3a8ce Merge pull request #15 from gogf/master
日常更新
2019-04-29 14:30:24 +08:00
a177e44583 edit some infos 2019-04-29 14:29:33 +08:00
7ae03729f3 add package support for gtcp 2019-04-28 23:55:23 +08:00
898ec21a25 del mod contents of 'go1.12' 2019-04-28 23:00:07 +08:00
6d7d8dec02 add var 'e' of gtime.format 2019-04-28 22:53:03 +08:00
a5b8e2aa2f add var 'w' of gtime.format 2019-04-28 18:12:43 +08:00
123333d9c2 edit some info 2019-04-27 22:59:06 +08:00
0c4fa1d96a fmt files 2019-04-27 22:50:44 +08:00
e5805e8c69 Merge branch 'master' of https://github.com/jroam/gf 2019-04-27 22:44:59 +08:00
bf2d45a012 add gtime.format var 't' 2019-04-27 22:37:36 +08:00
a7122788b1 add 'W' to gtime.format func(doing) 2019-04-26 17:52:45 +08:00
a0276f7e81 add param 'z' to gtime.format func 2019-04-24 23:12:48 +08:00
5346ca9046 use golint checked 2019-04-24 18:08:10 +08:00
6d5b552bb7 add param of 'N','s' to gtime.format 2019-04-24 18:03:29 +08:00
836d62f4aa Merge pull request #14 from gogf/master
日常更新
2019-04-24 09:34:50 +08:00
255 changed files with 11178 additions and 6324 deletions

View File

@ -1,5 +1,6 @@
# GoFrame
<img align="right" height="150px" src="https://goframe.org/cover.png">
<div align=center>
<img src="https://goframe.org/logo.png" width="100"/>
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
@ -8,12 +9,15 @@
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
</div>
<!--
GoFrame is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: cache, logging, containers, timer, validator, database orm, etc. Supporting web server integrated with router, cookie, session, logger, configure, template, https, hooks, rewrites and many more features.
-->
`GF(GoFrame)` is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites and many more features.
# Installation
```
go get -u github.com/gogf/gf
@ -25,7 +29,7 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.9.2
golang version >= 1.10
```
# Documentation
@ -64,7 +68,6 @@ func main() {
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Contributors
- [aloncn](https://github.com/aloncn)
@ -87,6 +90,8 @@ func main() {
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](https://goframe.org/images/donate.png)?
- [flyke-xu](https://gitee.com/flyke-xu)
- [hailaz](https://gitee.com/hailaz)
- [ireadx](https://github.com/ireadx)
@ -102,3 +107,12 @@ func main() {
# Sponsor
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.

View File

@ -1,5 +1,6 @@
# GoFrame
<img align="right" height="150px" src="https://goframe.org/cover.png">
<div align=center>
<img src="https://goframe.org/logo.png" width="100"/>
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
@ -8,6 +9,8 @@
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
</div>
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
并发安全容器等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、服务注册、配置管理、模板引擎等等支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
@ -33,7 +36,7 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.9.2
golang版本 >= 1.10
```
# 架构
@ -76,12 +79,12 @@ func main() {
# 捐赠
捐赠支持`GF`框架的研发,
请在捐赠时备注您的`github`/`gitee`账号名称。
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](https://goframe.org/images/donate.png)
请在捐赠时备注您的`github`/`gitee`账号名称。
<a href="https://goframe.org/images/donate.png" target="_blank">
<img src="https://goframe.org/images/donate.png" width="300"/>
</a>
# 赞助
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 john@goframe.org 。
# 贡献者

View File

@ -45,6 +45,7 @@
1. gredis增加cluster支持
1. gset.Add/Remove/Contains方法增加批量操作支持
1. gmlock增加手动清理机制当内存锁不再使用时由调用端决定是否清理内存锁
1. gtimer增加DelayAdd*方法返回Entry对象以便DelayAdd*的定时任务也能进行状态控制gcron同理需要改进
# DONE
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换

View File

@ -29,7 +29,7 @@ func NewIntArray(unsafe...bool) *IntArray {
}
// NewIntArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
return &IntArray{
@ -39,7 +39,7 @@ func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
}
// NewIntArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
return &IntArray{
@ -49,7 +49,7 @@ func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
}
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
newArray := make([]int, len(array))

View File

@ -34,7 +34,7 @@ func NewArray(unsafe...bool) *Array {
}
// NewArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArraySize(size int, cap int, unsafe...bool) *Array {
return &Array{
@ -54,7 +54,7 @@ func NewFromCopy(array []interface{}, unsafe...bool) *Array {
}
// NewArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
return &Array{
@ -64,7 +64,7 @@ func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
}
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
newArray := make([]interface{}, len(array))

View File

@ -30,7 +30,7 @@ func NewStringArray(unsafe...bool) *StringArray {
}
// NewStringArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
return &StringArray{
@ -40,7 +40,7 @@ func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
}
// NewStringArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
return &StringArray {
@ -50,7 +50,7 @@ func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
}
// NewStringArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
newArray := make([]string, len(array))

View File

@ -22,7 +22,7 @@ type SortedIntArray struct {
mu *rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
comparator func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedIntArray creates and returns an empty sorted array.
@ -33,14 +33,14 @@ func NewSortedIntArray(unsafe...bool) *SortedIntArray {
}
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
return &SortedIntArray {
mu : rwmutex.New(unsafe...),
array : make([]int, 0, cap),
unique : gtype.NewBool(),
compareFunc : func(v1, v2 int) int {
comparator : func(v1, v2 int) int {
if v1 < v2 {
return -1
}
@ -53,7 +53,7 @@ func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
}
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, unsafe...)
@ -63,7 +63,7 @@ func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
}
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
newArray := make([]int, len(array))
@ -305,7 +305,7 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.compareFunc(value, a.array[mid])
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
@ -336,7 +336,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
if i == len(a.array) - 1 {
break
}
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++

View File

@ -22,45 +22,45 @@ type SortedArray struct {
mu *rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
comparator func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety, which is false in default.
// The param <compareFunc> used to compare values to sort in array,
// The param <unsafe> used to specify whether using array in un-concurrent-safety, which is false in default.
// The param <comparator> used to compare values to sort in array,
// if it returns value < 0, means v1 < v2;
// if it returns value = 0, means v1 = v2;
// if it returns value > 0, means v1 > v2;
func NewSortedArray(compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return NewSortedArraySize(0, compareFunc, unsafe...)
func NewSortedArray(comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return NewSortedArraySize(0, comparator, unsafe...)
}
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
func NewSortedArraySize(cap int, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return &SortedArray{
mu : rwmutex.New(unsafe...),
unique : gtype.NewBool(),
array : make([]interface{}, 0, cap),
compareFunc : compareFunc,
comparator : comparator,
}
}
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, compareFunc, unsafe...)
func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, unsafe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
newArray := make([]interface{}, len(array))
@ -77,7 +77,7 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
@ -89,7 +89,7 @@ func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
@ -306,7 +306,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.compareFunc(value, a.array[mid])
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
@ -338,7 +338,7 @@ func (a *SortedArray) Unique() *SortedArray {
if i == len(a.array) - 1 {
break
}
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++
@ -353,7 +353,7 @@ func (a *SortedArray) Clone() (newArray *SortedArray) {
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe())
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
}
// Clear deletes all items of current array.

View File

@ -23,7 +23,7 @@ type SortedStringArray struct {
mu *rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
comparator func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedStringArray creates and returns an empty sorted array.
@ -34,21 +34,21 @@ func NewSortedStringArray(unsafe...bool) *SortedStringArray {
}
// NewSortedStringArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
return &SortedStringArray {
mu : rwmutex.New(unsafe...),
array : make([]string, 0, cap),
unique : gtype.NewBool(),
compareFunc : func(v1, v2 string) int {
comparator : func(v1, v2 string) int {
return strings.Compare(v1, v2)
},
}
}
// NewSortedStringArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
a := NewSortedStringArraySize(0, unsafe...)
@ -58,7 +58,7 @@ func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray
}
// NewSortedStringArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
newArray := make([]string, len(array))
@ -300,7 +300,7 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.compareFunc(value, a.array[mid])
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
@ -331,7 +331,7 @@ func (a *SortedStringArray) Unique() *SortedStringArray {
if i == len(a.array) - 1 {
break
}
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++

View File

@ -22,7 +22,7 @@ type (
Element = list.Element
)
// 获得一个变长链表指针
// New creates and returns a new empty doubly linked list.
func New(unsafe...bool) *List {
return &List {
mu : rwmutex.New(unsafe...),
@ -30,7 +30,7 @@ func New(unsafe...bool) *List {
}
}
// 往链表头入栈数据项
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
func (l *List) PushFront(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushFront(v)
@ -38,7 +38,7 @@ func (l *List) PushFront(v interface{}) (e *Element) {
return
}
// 往链表尾入栈数据项
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
func (l *List) PushBack(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushBack(v)
@ -46,8 +46,8 @@ func (l *List) PushBack(v interface{}) (e *Element) {
return
}
// 批量往链表头入栈数据项
func (l *List) BatchPushFront(values []interface{}) {
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
func (l *List) PushFronts(values []interface{}) {
l.mu.Lock()
for _, v := range values {
l.list.PushFront(v)
@ -55,8 +55,8 @@ func (l *List) BatchPushFront(values []interface{}) {
l.mu.Unlock()
}
// 批量往链表尾入栈数据项
func (l *List) BatchPushBack(values []interface{}) {
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
func (l *List) PushBacks(values []interface{}) {
l.mu.Lock()
for _, v := range values {
l.list.PushBack(v)
@ -64,7 +64,7 @@ func (l *List) BatchPushBack(values []interface{}) {
l.mu.Unlock()
}
// 从链表尾端出栈数据项(删除)
// PopBack removes the element from back of <l> and returns the value of the element.
func (l *List) PopBack() (value interface{}) {
l.mu.Lock()
if e := l.list.Back(); e != nil {
@ -74,7 +74,7 @@ func (l *List) PopBack() (value interface{}) {
return
}
// 从链表头端出栈数据项(删除)
// PopFront removes the element from front of <l> and returns the value of the element.
func (l *List) PopFront() (value interface{}) {
l.mu.Lock()
if e := l.list.Front(); e != nil {
@ -84,8 +84,9 @@ func (l *List) PopFront() (value interface{}) {
return
}
// 批量从链表尾端出栈数据项(删除)
func (l *List) BatchPopBack(max int) (values []interface{}) {
// PopBacks removes <max> elements from back of <l>
// and returns values of the removed elements as slice.
func (l *List) PopBacks(max int) (values []interface{}) {
l.mu.Lock()
length := l.list.Len()
if length > 0 {
@ -103,8 +104,9 @@ func (l *List) BatchPopBack(max int) (values []interface{}) {
return
}
// 批量从链表头端出栈数据项(删除)
func (l *List) BatchPopFront(max int) (values []interface{}) {
// PopFronts removes <max> elements from front of <l>
// and returns values of the removed elements as slice.
func (l *List) PopFronts(max int) (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
@ -122,17 +124,19 @@ func (l *List) BatchPopFront(max int) (values []interface{}) {
return
}
// 批量从链表尾端依次获取所有数据(删除)
// PopBackAll removes all elements from back of <l>
// and returns values of the removed elements as slice.
func (l *List) PopBackAll() []interface{} {
return l.BatchPopBack(-1)
return l.PopBacks(-1)
}
// 批量从链表头端依次获取所有数据(删除)
// PopFrontAll removes all elements from front of <l>
// and returns values of the removed elements as slice.
func (l *List) PopFrontAll() []interface{} {
return l.BatchPopFront(-1)
return l.PopFronts(-1)
}
// 从链表头获取所有数据(不删除)
// FrontAll copies and returns values of all elements from front of <l> as slice.
func (l *List) FrontAll() (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
@ -146,7 +150,7 @@ func (l *List) FrontAll() (values []interface{}) {
return
}
// 从链表尾获取所有数据(不删除)
// BackAll copies and returns values of all elements from back of <l> as slice.
func (l *List) BackAll() (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
@ -160,8 +164,8 @@ func (l *List) BackAll() (values []interface{}) {
return
}
// 获取链表头值(不删除)
func (l *List) FrontItem() (value interface{}) {
// FrontValue returns value of the first element of <l> or nil if the list is empty.
func (l *List) FrontValue() (value interface{}) {
l.mu.RLock()
if e := l.list.Front(); e != nil {
value = e.Value
@ -170,8 +174,8 @@ func (l *List) FrontItem() (value interface{}) {
return
}
// 获取链表尾值(不删除)
func (l *List) BackItem() (value interface{}) {
// BackValue returns value of the last element of <l> or nil if the list is empty.
func (l *List) BackValue() (value interface{}) {
l.mu.RLock()
if e := l.list.Back(); e != nil {
value = e.Value
@ -180,7 +184,7 @@ func (l *List) BackItem() (value interface{}) {
return
}
// 获取表头指针
// Front returns the first element of list <l> or nil if the list is empty.
func (l *List) Front() (e *Element) {
l.mu.RLock()
e = l.list.Front()
@ -188,7 +192,7 @@ func (l *List) Front() (e *Element) {
return
}
// 获取表位指针
// Back returns the last element of list <l> or nil if the list is empty.
func (l *List) Back() (e *Element) {
l.mu.RLock()
e = l.list.Back()
@ -196,7 +200,8 @@ func (l *List) Back() (e *Element) {
return
}
// 获取链表长度
// Len returns the number of elements of list <l>.
// The complexity is O(1).
func (l *List) Len() (length int) {
l.mu.RLock()
length = l.list.Len()
@ -204,30 +209,44 @@ func (l *List) Len() (length int) {
return
}
// MoveBefore moves element <e> to its new position before <p>.
// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
// The element and <p> must not be nil.
func (l *List) MoveBefore(e, p *Element) {
l.mu.Lock()
l.list.MoveBefore(e, p)
l.mu.Unlock()
}
// MoveAfter moves element <e> to its new position after <p>.
// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
// The element and <p> must not be nil.
func (l *List) MoveAfter(e, p *Element) {
l.mu.Lock()
l.list.MoveAfter(e, p)
l.mu.Unlock()
}
// MoveToFront moves element <e> to the front of list <l>.
// If <e> is not an element of <l>, the list is not modified.
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
l.mu.Lock()
l.list.MoveToFront(e)
l.mu.Unlock()
}
// MoveToBack moves element <e> to the back of list <l>.
// If <e> is not an element of <l>, the list is not modified.
// The element must not be nil.
func (l *List) MoveToBack(e *Element) {
l.mu.Lock()
l.list.MoveToBack(e)
l.mu.Unlock()
}
// PushBackList inserts a copy of an other list at the back of list <l>.
// The lists <l> and <other> may be the same, but they must not be nil.
func (l *List) PushBackList(other *List) {
if l != other {
other.mu.RLock()
@ -238,6 +257,8 @@ func (l *List) PushBackList(other *List) {
l.mu.Unlock()
}
// PushFrontList inserts a copy of an other list at the front of list <l>.
// The lists <l> and <other> may be the same, but they must not be nil.
func (l *List) PushFrontList(other *List) {
if l != other {
other.mu.RLock()
@ -248,7 +269,9 @@ func (l *List) PushFrontList(other *List) {
l.mu.Unlock()
}
// 在list中元素项p之后插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
// If <p> is not an element of <l>, the list is not modified.
// The <p> must not be nil.
func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertAfter(v, p)
@ -256,7 +279,9 @@ func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) {
return
}
// 在list中元素项p之前插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
// InsertBefore inserts a new element <e> with value <v> immediately before <p> and returns <e>.
// If <p> is not an element of <l>, the list is not modified.
// The <p> must not be nil.
func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertBefore(v, p)
@ -264,7 +289,9 @@ func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) {
return
}
// 删除数据项e, 并返回删除项的元素项
// Remove removes <e> from <l> if <e> is an element of list <l>.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List) Remove(e *Element) (value interface{}) {
l.mu.Lock()
value = l.list.Remove(e)
@ -272,8 +299,8 @@ func (l *List) Remove(e *Element) (value interface{}) {
return
}
// 批量删除数据项
func (l *List) BatchRemove(es []*Element) {
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
func (l *List) Removes(es []*Element) {
l.mu.Lock()
for _, e := range es {
l.list.Remove(e)
@ -282,27 +309,63 @@ func (l *List) BatchRemove(es []*Element) {
return
}
// 删除所有数据项
// RemoveAll removes all elements from list <l>.
func (l *List) RemoveAll() {
l.mu.Lock()
l.list = list.New()
l.mu.Unlock()
}
// See RemoveAll().
func (l *List) Clear() {
l.RemoveAll()
}
// 读锁操作
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
}
// 写锁操作
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
f(l.list)
}
// Iterator is alias of IteratorAsc.
func (l *List) Iterator(f func (e *Element) bool) {
l.IteratorAsc(f)
}
// IteratorAsc iterates the list in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorAsc(f func (e *Element) bool) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i + 1, e.Next() {
if !f(e) {
break
}
}
}
l.mu.RUnlock()
}
// IteratorDesc iterates the list in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorDesc(f func (e *Element) bool) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Back(); i < length; i, e = i + 1, e.Prev() {
if !f(e) {
break
}
}
}
l.mu.RUnlock()
}

View File

@ -4,315 +4,41 @@
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
// Package gmap provides concurrent-safe/unsafe maps.
// Package gmap provides concurrent-safe/unsafe map containers.
package gmap
import "github.com/gogf/gf/g/internal/rwmutex"
type Map struct {
mu *rwmutex.RWMutex
m map[interface{}]interface{}
}
// Map based on hash table, alias of AnyAnyMap.
type Map = AnyAnyMap
type HashMap = AnyAnyMap
// New returns an empty hash map.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func New(unsafe ...bool) *Map {
return NewMap(unsafe...)
return NewAnyAnyMap(unsafe...)
}
// Alias of New. See New.
func NewMap(unsafe ...bool) *Map {
return &Map{
m : make(map[interface{}]interface{}),
mu : rwmutex.New(unsafe...),
}
}
// NewFrom returns a hash map from given map <m>.
// Notice that, the param map is a type of pointer,
// NewFrom 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 NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map {
return &Map{
m : m,
mu : rwmutex.New(unsafe...),
}
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
return NewAnyAnyMapFrom(data, unsafe...)
}
// NewFromArray returns a hash map from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map {
m := make(map[interface{}]interface{})
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = interface{}(nil)
}
}
return &Map{
m : m,
mu : rwmutex.New(unsafe...),
}
// NewHashMap returns an empty hash map.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewHashMap(unsafe ...bool) *Map {
return NewAnyAnyMap(unsafe...)
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *Map) Iterator(f func (k interface{}, v interface{}) 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 *Map) Clone(unsafe ...bool) *Map {
return NewFrom(gm.Map(), unsafe ...)
}
// Map returns a copy of the data of the hash map.
func (gm *Map) Map() map[interface{}]interface{} {
m := make(map[interface{}]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.
func (gm *Map) Set(key interface{}, val interface{}) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *Map) BatchSet(m map[interface{}]interface{}) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *Map) Get(key interface{}) interface{} {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (gm *Map) doSetWithLockCheck(key interface{}, 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()
}
gm.m[key] = value
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} {
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 <f> if not exist
// and returns this value.
func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v := gm.Get(key); v == nil {
return gm.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v := gm.Get(key); v == nil {
return gm.doSetWithLockCheck(key, f)
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *Map) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *Map) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f)
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *Map) BatchRemove(keys []interface{}) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *Map) Remove(key interface{}) interface{} {
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 *Map) Keys() []interface{} {
gm.mu.RLock()
keys := make([]interface{}, 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 *Map) Values() []interface{} {
gm.mu.RLock()
vals := make([]interface{}, 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 <key> exists, or else false.
func (gm *Map) Contains(key interface{}) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *Map) Size() int {
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 *Map) IsEmpty() bool {
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 *Map) Clear() {
gm.mu.Lock()
gm.m = make(map[interface{}]interface{})
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *Map) LockFunc(f func(m map[interface{}]interface{})) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *Map) RLockFunc(f func(m map[interface{}]interface{})) {
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 *Map) Flip() {
gm.mu.Lock()
defer gm.mu.Unlock()
n := make(map[interface{}]interface{}, len(gm.m))
for i, v := range gm.m {
n[v] = i
}
gm.m = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *Map) Merge(other *Map) {
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
}
// NewHashMapFrom 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.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewHashMapFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
return NewAnyAnyMapFrom(data, unsafe...)
}

View File

@ -0,0 +1,330 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
)
type AnyAnyMap struct {
mu *rwmutex.RWMutex
data map[interface{}]interface{}
}
// NewAnyAnyMap returns an empty hash map.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewAnyAnyMap(unsafe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[interface{}]interface{}),
}
}
// NewAnyAnyMapFrom 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 NewAnyAnyMapFrom(data map[interface{}]interface{}, unsafe...bool) *AnyAnyMap {
return &AnyAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *AnyAnyMap) Iterator(f func (k interface{}, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *AnyAnyMap) Clone(unsafe ...bool) *AnyAnyMap {
return NewFrom(m.Map(), unsafe ...)
}
// Map returns a copy of the data of the hash map.
func (m *AnyAnyMap) Map() map[interface{}]interface{} {
m.mu.RLock()
data := make(map[interface{}]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
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 *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *AnyAnyMap) Get(key interface{}) interface{} {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
m.data[key] = value
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
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 return value of callback function <f> if not exist
// and returns this value.
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
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 return value of callback function <f> if 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 *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
return gvar.New(m.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
}
return false
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Removes batch deletes values of the map by keys.
func (m *AnyAnyMap) Removes(keys []interface{}) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
}
// Keys returns all keys of the map as a slice.
func (m *AnyAnyMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, 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 *AnyAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, 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 *AnyAnyMap) Contains(key interface{}) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *AnyAnyMap) 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 *AnyAnyMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *AnyAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[interface{}]interface{})
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *AnyAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[interface{}]interface{}, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,334 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type IntAnyMap struct {
mu *rwmutex.RWMutex
data map[int]interface{}
}
// NewIntAnyMap returns an empty IntAnyMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntAnyMap(unsafe...bool) *IntAnyMap {
return &IntAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]interface{}),
}
}
// NewIntAnyMapFrom 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 NewIntAnyMapFrom(data map[int]interface{}, unsafe...bool) *IntAnyMap {
return &IntAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntAnyMap) Iterator(f func (k int, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *IntAnyMap) Clone() *IntAnyMap {
return NewIntAnyMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *IntAnyMap) Map() map[int]interface{} {
m.mu.RLock()
data := make(map[int]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *IntAnyMap) Set(key int, val interface{}) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *IntAnyMap) Sets(data map[int]interface{}) {
m.mu.Lock()
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 *IntAnyMap) Search(key int) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntAnyMap) Get(key int) (interface{}) {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
if value != nil {
m.data[key] = value
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
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 return value of callback function <f> if not exist and returns this value.
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
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 return value of callback function <f> if 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 *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
return gvar.New(m.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
}
return false
}
// Removes batch deletes values of the map by keys.
func (m *IntAnyMap) Removes(keys []int) {
m.mu.Lock()
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 *IntAnyMap) Remove(key int) interface{} {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *IntAnyMap) Keys() []int {
m.mu.RLock()
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 *IntAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, 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 *IntAnyMap) Contains(key int) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *IntAnyMap) 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 *IntAnyMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *IntAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[int]interface{})
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *IntAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]interface{}, len(m.data))
for k, v := range m.data {
n[gconv.Int(v)] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *IntAnyMap) Merge(other *IntAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,308 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntIntMap struct {
mu *rwmutex.RWMutex
data map[int]int
}
// NewIntIntMap returns an empty IntIntMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntIntMap(unsafe...bool) *IntIntMap {
return &IntIntMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]int),
}
}
// NewIntIntMapFrom 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 NewIntIntMapFrom(data map[int]int, unsafe...bool) *IntIntMap {
return &IntIntMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntIntMap) Iterator(f func (k int, v int) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *IntIntMap) Clone() *IntIntMap {
return NewIntIntMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *IntIntMap) Map() map[int]int {
m.mu.RLock()
data := make(map[int]int, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *IntIntMap) Set(key int, val int) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *IntIntMap) Sets(data map[int]int) {
m.mu.Lock()
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 *IntIntMap) Search(key int) (value int, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntIntMap) Get(key int) (int) {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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 *IntIntMap) doSetWithLockCheck(key int, value int) int {
m.mu.Lock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *IntIntMap) GetOrSet(key int, value int) int {
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 return value of callback function <f> if not exist and returns this value.
func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
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 return value of callback function <f> if 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 *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
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, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
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 *IntIntMap) Removes(keys []int) {
m.mu.Lock()
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 *IntIntMap) Remove(key int) int {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *IntIntMap) Keys() []int {
m.mu.RLock()
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 *IntIntMap) Values() []int {
m.mu.RLock()
values := make([]int, 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 *IntIntMap) Contains(key int) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *IntIntMap) 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 *IntIntMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *IntIntMap) Clear() {
m.mu.Lock()
m.data = make(map[int]int)
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *IntIntMap) RLockFunc(f func(m map[int]int)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *IntIntMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]int, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *IntIntMap) Merge(other *IntIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,309 @@
// 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 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"
)
type IntStrMap struct {
mu *rwmutex.RWMutex
data map[int]string
}
// NewIntStrMap returns an empty IntStrMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntStrMap(unsafe ...bool) *IntStrMap {
return &IntStrMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]string),
}
}
// NewIntStrMapFrom 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, unsafe ...bool) *IntStrMap {
return &IntStrMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map 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) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
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.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *IntStrMap) Map() map[int]string {
m.mu.RLock()
data := make(map[int]string, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *IntStrMap) Set(key int, val string) {
m.mu.Lock()
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()
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()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntStrMap) Get(key int) string {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and 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 return value of callback function <f> if 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 return value of callback function <f> if 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 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, then return 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>, then return 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>, then return 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 _, 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()
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) string {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *IntStrMap) Keys() []int {
m.mu.RLock()
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()
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 {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// 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 {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// 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()
}
// 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 other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,334 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type StrAnyMap struct {
mu *rwmutex.RWMutex
data map[string]interface{}
}
// NewStrAnyMap returns an empty StrAnyMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStrAnyMap(unsafe ...bool) *StrAnyMap {
return &StrAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[string]interface{}),
}
}
// NewStrAnyMapFrom 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 NewStrAnyMapFrom(data map[string]interface{}, unsafe ...bool) *StrAnyMap {
return &StrAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *StrAnyMap) Clone() *StrAnyMap {
return NewStrAnyMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *StrAnyMap) Map() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *StrAnyMap) Set(key string, val interface{}) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *StrAnyMap) Sets(data map[string]interface{}) {
m.mu.Lock()
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 *StrAnyMap) Search(key string) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrAnyMap) Get(key string) interface{} {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
value = f()
}
if value != nil {
m.data[key] = value
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
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 return value of callback function <f> if not exist
// and returns this value.
func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
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 return value of callback function <f> if 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 *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
return gvar.New(m.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
}
return false
}
// Removes batch deletes values of the map by keys.
func (m *StrAnyMap) Removes(keys []string) {
m.mu.Lock()
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 *StrAnyMap) Remove(key string) interface{} {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *StrAnyMap) Keys() []string {
m.mu.RLock()
keys := make([]string, 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 *StrAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, 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 *StrAnyMap) Contains(key string) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *StrAnyMap) 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 *StrAnyMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *StrAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[string]interface{})
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *StrAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
n[gconv.String(v)] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *StrAnyMap) Merge(other *StrAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,312 @@
// 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 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"
)
type StrIntMap struct {
mu *rwmutex.RWMutex
data map[string]int
}
// NewStrIntMap returns an empty StrIntMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStrIntMap(unsafe ...bool) *StrIntMap {
return &StrIntMap{
mu : rwmutex.New(unsafe...),
data : make(map[string]int),
}
}
// NewStrIntMapFrom 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 NewStrIntMapFrom(data map[string]int, unsafe ...bool) *StrIntMap {
return &StrIntMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *StrIntMap) Clone() *StrIntMap {
return NewStrIntMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *StrIntMap) Map() map[string]int {
m.mu.RLock()
data := make(map[string]int, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *StrIntMap) Set(key string, val int) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *StrIntMap) Sets(data map[string]int) {
m.mu.Lock()
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 *StrIntMap) Search(key string) (value int, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrIntMap) Get(key string) int {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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 *StrIntMap) doSetWithLockCheck(key string, value int) int {
m.mu.Lock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *StrIntMap) GetOrSet(key string, value int) int {
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 return value of callback function <f> if not exist
// and returns this value.
func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
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 return value of callback function <f> if 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 *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
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, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
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 *StrIntMap) Removes(keys []string) {
m.mu.Lock()
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 *StrIntMap) Remove(key string) int {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *StrIntMap) Keys() []string {
m.mu.RLock()
keys := make([]string, 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 *StrIntMap) Values() []int {
m.mu.RLock()
values := make([]int, 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 *StrIntMap) Contains(key string) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *StrIntMap) 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 *StrIntMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *StrIntMap) Clear() {
m.mu.Lock()
m.data = make(map[string]int)
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *StrIntMap) RLockFunc(f func(m map[string]int)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *StrIntMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[string]int, len(m.data))
for k, v := range m.data {
n[gconv.String(v)] = gconv.Int(k)
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *StrIntMap) Merge(other *StrIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -0,0 +1,311 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type StrStrMap struct {
mu *rwmutex.RWMutex
data map[string]string
}
// NewStrStrMap returns an empty StrStrMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStrStrMap(unsafe...bool) *StrStrMap {
return &StrStrMap{
data : make(map[string]string),
mu : rwmutex.New(unsafe...),
}
}
// NewStrStrMapFrom 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 NewStrStrMapFrom(data map[string]string, unsafe...bool) *StrStrMap {
return &StrStrMap{
mu : rwmutex.New(unsafe...),
data : data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrStrMap) Iterator(f func (k string, v string) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *StrStrMap) Clone() *StrStrMap {
return NewStrStrMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *StrStrMap) Map() map[string]string {
m.mu.RLock()
data := make(map[string]string, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *StrStrMap) Set(key string, val string) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *StrStrMap) Sets(data map[string]string) {
m.mu.Lock()
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 *StrStrMap) Search(key string) (value string, found bool) {
m.mu.RLock()
value, found = m.data[key]
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrStrMap) Get(key string) string {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// 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 *StrStrMap) doSetWithLockCheck(key string, value string) string {
m.mu.Lock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *StrStrMap) GetOrSet(key string, 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 return value of callback function <f> if not exist
// and returns this value.
func (m *StrStrMap) GetOrSetFunc(key string, 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 return value of callback function <f> if 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 *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
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, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrStrMap) SetIfNotExist(key string, 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>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrStrMap) SetIfNotExistFunc(key string, 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>, then return 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 *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
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 *StrStrMap) Removes(keys []string) {
m.mu.Lock()
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 *StrStrMap) Remove(key string) string {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *StrStrMap) Keys() []string {
m.mu.RLock()
keys := make([]string, 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 *StrStrMap) Values() []string {
m.mu.RLock()
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 *StrStrMap) Contains(key string) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *StrStrMap) 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 *StrStrMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *StrStrMap) Clear() {
m.mu.Lock()
m.data = make(map[string]string)
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *StrStrMap) RLockFunc(f func(m map[string]string)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *StrStrMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[string]string, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *StrStrMap) Merge(other *StrStrMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -1,305 +0,0 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntBoolMap struct {
m map[int]bool
mu *rwmutex.RWMutex
}
// NewIntBoolMap returns an empty IntBoolMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntBoolMap(unsafe...bool) *IntBoolMap {
return &IntBoolMap{
m : make(map[int]bool),
mu : rwmutex.New(unsafe...),
}
}
// NewIntBoolMapFrom returns an IntBoolMap object from given map <m>.
// Notice that, the param map is a type of pointer,
// there might be some concurrent-safe issues when changing the map outside.
func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap {
return &IntBoolMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// NewIntBoolMapFromArray returns an IntBoolMap from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMap {
m := make(map[int]bool)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = false
}
}
return &IntBoolMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Clone returns a new hash map with copy of current map data.
func (gm *IntBoolMap) Clone() *IntBoolMap {
return NewIntBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *IntBoolMap) Map() map[int]bool {
m := make(map[int]bool)
gm.mu.RLock()
for k, v := range gm.m {
m[k] = v
}
gm.mu.RUnlock()
return m
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) {
gm.mu.RLock()
defer gm.mu.RUnlock()
for k, v := range gm.m {
if !f(k, v) {
break
}
}
}
// Set sets key-value to the hash map.
func (gm *IntBoolMap) Set(key int, val bool) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *IntBoolMap) BatchSet(m map[int]bool) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *IntBoolMap) Get(key int) bool {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
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 <value> if not exist and returns this value.
func (gm *IntBoolMap) GetOrSet(key int, value bool) bool {
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 <f> if not exist and returns this value.
func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
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,
// or sets value with return value of callback function <f> if 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 (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
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 <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntBoolMap) SetIfNotExistFunc(key int, f func() bool) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *IntBoolMap) SetIfNotExistFuncLock(key int, f func() bool) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *IntBoolMap) BatchRemove(keys []int) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *IntBoolMap) Remove(key int) bool {
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 *IntBoolMap) Keys() []int {
gm.mu.RLock()
keys := make([]int, 0)
for key, _ := range gm.m {
keys = append(keys, key)
}
gm.mu.RUnlock()
return keys
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (gm *IntBoolMap) Contains(key int) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *IntBoolMap) Size() int {
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 *IntBoolMap) IsEmpty() bool {
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 *IntBoolMap) Clear() {
gm.mu.Lock()
gm.m = make(map[int]bool)
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *IntBoolMap) LockFunc(f func(m map[int]bool)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *IntBoolMap) Merge(other *IntBoolMap) {
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
}
}

View File

@ -1,327 +0,0 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntIntMap struct {
mu *rwmutex.RWMutex
m map[int]int
}
// NewIntIntMap returns an empty IntIntMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntIntMap(unsafe...bool) *IntIntMap {
return &IntIntMap{
m : make(map[int]int),
mu : rwmutex.New(unsafe...),
}
}
// NewIntIntMapFrom returns an IntIntMap object from given map <m>.
// Notice that, the param map is a type of pointer,
// there might be some concurrent-safe issues when changing the map outside.
func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap {
return &IntIntMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// NewIntIntMapFromArray returns an IntIntMap object from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap {
m := make(map[int]int)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = 0
}
}
return &IntIntMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *IntIntMap) Iterator(f func (k int, 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 *IntIntMap) Clone() *IntIntMap {
return NewIntIntMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *IntIntMap) Map() map[int]int {
m := make(map[int]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.
func (gm *IntIntMap) Set(key int, val int) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *IntIntMap) BatchSet(m map[int]int) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *IntIntMap) Get(key int) (int) {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (gm *IntIntMap) doSetWithLockCheck(key int, 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
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (gm *IntIntMap) GetOrSet(key int, value int) int {
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 <f> if not exist and returns this value.
func (gm *IntIntMap) GetOrSetFunc(key int, 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
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (gm *IntIntMap) GetOrSetFuncLock(key int, 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
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntIntMap) SetIfNotExist(key int, value int) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *IntIntMap) BatchRemove(keys []int) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *IntIntMap) Remove(key int) int {
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 *IntIntMap) Keys() []int {
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 *IntIntMap) Values() []int {
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 <key> exists, or else false.
func (gm *IntIntMap) Contains(key int) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *IntIntMap) Size() int {
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 *IntIntMap) IsEmpty() bool {
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 *IntIntMap) Clear() {
gm.mu.Lock()
gm.m = make(map[int]int)
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *IntIntMap) LockFunc(f func(m map[int]int)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) {
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 *IntIntMap) Flip() {
gm.mu.Lock()
defer gm.mu.Unlock()
n := make(map[int]int, len(gm.m))
for k, v := range gm.m {
n[v] = k
}
gm.m = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *IntIntMap) Merge(other *IntIntMap) {
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
}
}

View File

@ -1,318 +0,0 @@
// 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 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"
)
type IntInterfaceMap struct {
mu *rwmutex.RWMutex
m map[int]interface{}
}
// NewIntInterfaceMap returns an empty IntInterfaceMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
return &IntInterfaceMap{
m : make(map[int]interface{}),
mu : rwmutex.New(unsafe...),
}
}
// NewIntInterfaceMapFrom returns an IntInterfaceMap object from given map <m>.
// Notice that, the param map is a type of pointer,
// there might be some concurrent-safe issues when changing the map outside.
func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceMap {
return &IntInterfaceMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// NewFromArray returns a hash map from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool) *IntInterfaceMap {
m := make(map[int]interface{})
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = interface{}(nil)
}
}
return &IntInterfaceMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) 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 *IntInterfaceMap) Clone() *IntInterfaceMap {
return NewIntInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *IntInterfaceMap) Map() map[int]interface{} {
m := make(map[int]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.
func (gm *IntInterfaceMap) Set(key int, val interface{}) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *IntInterfaceMap) Get(key int) (interface{}) {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (gm *IntInterfaceMap) doSetWithLockCheck(key int, 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
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
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 <f> if not exist and returns this value.
func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
if v := gm.Get(key); v == nil {
return gm.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
if v := gm.Get(key); v == nil {
return gm.doSetWithLockCheck(key, f)
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntInterfaceMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *IntInterfaceMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f)
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *IntInterfaceMap) BatchRemove(keys []int) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *IntInterfaceMap) Remove(key int) interface{} {
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 *IntInterfaceMap) Keys() []int {
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 *IntInterfaceMap) Values() []interface{} {
gm.mu.RLock()
vals := make([]interface{}, 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 <key> exists, or else false.
func (gm *IntInterfaceMap) Contains(key int) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *IntInterfaceMap) Size() int {
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 *IntInterfaceMap) IsEmpty() bool {
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 *IntInterfaceMap) Clear() {
gm.mu.Lock()
gm.m = make(map[int]interface{})
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
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 *IntInterfaceMap) Flip() {
gm.mu.Lock()
defer gm.mu.Unlock()
n := make(map[int]interface{}, len(gm.m))
for k, v := range gm.m {
n[gconv.Int(v)] = k
}
gm.m = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *IntInterfaceMap) Merge(other *IntInterfaceMap) {
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
}
}

View File

@ -1,327 +0,0 @@
// 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 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"
)
type IntStringMap struct {
mu *rwmutex.RWMutex
m map[int]string
}
// NewIntStringMap returns an empty IntStringMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntStringMap(unsafe ...bool) *IntStringMap {
return &IntStringMap{
m: make(map[int]string),
mu: rwmutex.New(unsafe...),
}
}
// NewIntStringMapFrom returns an IntStringMap object from given map <m>.
// 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...),
}
}
// NewIntStringMapFromArray returns an IntStringMap object from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// 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...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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
}
}
}
// Clone returns a new hash map with copy of current map data.
func (gm *IntStringMap) Clone() *IntStringMap {
return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *IntStringMap) Map() map[int]string {
m := make(map[int]string)
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.
func (gm *IntStringMap) Set(key int, val string) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *IntStringMap) BatchSet(m map[int]string) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *IntStringMap) Get(key int) string {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (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
}
// GetOrSet returns the value by key,
// or set value with given <value> 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
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> 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
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (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
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntStringMap) SetIfNotExist(key int, value string) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntStringMap) SetIfNotExistFunc(key int, f func() string) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
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()
}
// Remove deletes value from map by given <key>, 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
}
// 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
}
// 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
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (gm *IntStringMap) Contains(key int) bool {
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
}
// 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
}
// 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()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *IntStringMap) LockFunc(f func(m map[int]string)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) {
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
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
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
}
}

View File

@ -0,0 +1,366 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"github.com/gogf/gf/g/container/glist"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
)
type ListMap struct {
mu *rwmutex.RWMutex
data map[interface{}]*glist.Element
list *glist.List
}
type gListMapNode struct {
key interface{}
value interface{}
}
// NewListMap returns an empty link map.
// ListMap is backed by a hash table to store values and doubly-linked list to store ordering.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewListMap(unsafe ...bool) *ListMap {
return &ListMap{
mu : rwmutex.New(unsafe...),
data : make(map[interface{}]*glist.Element),
list : glist.New(true),
}
}
// NewListMapFrom returns a link 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 NewListMapFrom(data map[interface{}]interface{}, unsafe...bool) *ListMap {
m := NewListMap(unsafe...)
m.Sets(data)
return m
}
// Iterator is alias of IteratorAsc.
func (m *ListMap) Iterator(f func (key, value interface{}) bool) {
m.IteratorAsc(f)
}
// IteratorAsc iterates the map in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorAsc(f func (key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
// IteratorDesc iterates the map in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorDesc(f func (key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorDesc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
// Clone returns a new link map with copy of current map data.
func (m *ListMap) Clone(unsafe ...bool) *ListMap {
return NewListMapFrom(m.Map(), unsafe ...)
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *ListMap) Clear() {
m.mu.Lock()
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New(true)
m.mu.Unlock()
}
// Map returns a copy of the data of the map.
func (m *ListMap) Map() map[interface{}]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
data := make(map[interface{}]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[node.key] = node.value
return true
})
m.mu.RUnlock()
return data
}
// Set sets key-value to the map.
func (m *ListMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
m.mu.Unlock()
}
// Sets batch sets key-values to the map.
func (m *ListMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
}
m.mu.Unlock()
}
// Search searches the map with given <key>.
// Second return parameter <found> is true if key was found, otherwise false.
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
found = ok
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *ListMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
}
m.mu.RUnlock()
return
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if e, ok := m.data[key]; ok {
return e.Value.(*gListMapNode).value
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
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 return value of callback function <f> if not exist
// and returns this value.
func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
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 return value of callback function <f> if not exist
// and returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the map.
func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
return gvar.New(m.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 map.
func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
}
return false
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *ListMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
}
m.mu.Unlock()
return
}
// Removes batch deletes values of the map by keys.
func (m *ListMap) Removes(keys []interface{}) {
m.mu.Lock()
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
}
}
m.mu.Unlock()
}
// Keys returns all keys of the map as a slice in ascending order.
func (m *ListMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *ListMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
values[index] = e.Value.(*gListMapNode).value
index++
return true
})
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *ListMap) Contains(key interface{}) (ok bool) {
m.mu.RLock()
_, ok = m.data[key]
m.mu.RUnlock()
return
}
// Size returns the size of the map.
func (m *ListMap) Size() (size int) {
m.mu.RLock()
size = len(m.data)
m.mu.RUnlock()
return
}
// IsEmpty checks whether the map is empty.
// It returns true if map is empty, or else false.
func (m *ListMap) IsEmpty() bool {
return m.Size() == 0
}
// Flip exchanges key-value of the map to value-key.
func (m *ListMap) Flip() {
data := m.Map()
m.Clear()
for key, value := range data {
m.Set(value, key)
}
}
// Merge merges two link maps.
// The <other> map will be merged into the map <m>.
func (m *ListMap) Merge(other *ListMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
node := (*gListMapNode)(nil)
other.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if e, ok := m.data[node.key]; !ok {
m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
} else {
e.Value = &gListMapNode{node.key, node.value}
}
return true
})
}

View File

@ -1,308 +0,0 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type StringBoolMap struct {
mu *rwmutex.RWMutex
m map[string]bool
}
// NewStringBoolMap returns an empty StringBoolMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStringBoolMap(unsafe...bool) *StringBoolMap {
return &StringBoolMap{
m : make(map[string]bool),
mu : rwmutex.New(unsafe...),
}
}
// NewStringBoolMapFrom returns an StringBoolMap object from given map <m>.
// Notice that, the param map is a type of pointer,
// there might be some concurrent-safe issues when changing the map outside.
func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap {
return &StringBoolMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// NewFromArray returns a hash map from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *StringBoolMap {
m := make(map[string]bool)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = false
}
}
return &StringBoolMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *StringBoolMap) Iterator(f func (k string, v bool) 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 *StringBoolMap) Clone() *StringBoolMap {
return NewStringBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *StringBoolMap) Map() map[string]bool {
m := make(map[string]bool)
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.
func (gm *StringBoolMap) Set(key string, val bool) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *StringBoolMap) BatchSet(m map[string]bool) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *StringBoolMap) Get(key string) bool {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
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 <value> if not exist and returns this value.
func (gm *StringBoolMap) GetOrSet(key string, value bool) bool {
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 <f> if not exist
// and returns this value.
func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
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,
// or sets value with return value of callback function <f> if 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 (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
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 <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringBoolMap) SetIfNotExistFunc(key string, f func() bool) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *StringBoolMap) SetIfNotExistFuncLock(key string, f func() bool) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *StringBoolMap) BatchRemove(keys []string) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *StringBoolMap) Remove(key string) bool {
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 *StringBoolMap) Keys() []string {
gm.mu.RLock()
keys := make([]string, 0)
for key, _ := range gm.m {
keys = append(keys, key)
}
gm.mu.RUnlock()
return keys
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (gm *StringBoolMap) Contains(key string) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *StringBoolMap) Size() int {
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 *StringBoolMap) IsEmpty() bool {
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 *StringBoolMap) Clear() {
gm.mu.Lock()
gm.m = make(map[string]bool)
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *StringBoolMap) LockFunc(f func(m map[string]bool)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *StringBoolMap) Merge(other *StringBoolMap) {
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
}
}

View File

@ -1,330 +0,0 @@
// 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 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"
)
type StringIntMap struct {
mu *rwmutex.RWMutex
m map[string]int
}
// NewStringIntMap returns an empty StringIntMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStringIntMap(unsafe ...bool) *StringIntMap {
return &StringIntMap{
m: make(map[string]int),
mu: rwmutex.New(unsafe...),
}
}
// NewStringIntMapFrom returns an StringIntMap object from given map <m>.
// 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...),
}
}
// NewStringIntMapFromArray returns an StringIntMap object from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// 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...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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
}
}
}
// Clone returns a new hash map with copy of current map data.
func (gm *StringIntMap) Clone() *StringIntMap {
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
}
// Set sets key-value to the hash map.
func (gm *StringIntMap) Set(key string, val int) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *StringIntMap) BatchSet(m map[string]int) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *StringIntMap) Get(key string) int {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (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
}
// GetOrSet returns the value by key,
// or set value with given <value> 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
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> 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
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (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
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringIntMap) SetIfNotExist(key string, value int) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringIntMap) SetIfNotExistFunc(key string, f func() int) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
return false
}
// 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()
}
// Remove deletes value from map by given <key>, 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
}
// 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
}
// 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
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (gm *StringIntMap) Contains(key string) bool {
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
}
// 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
}
// 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()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *StringIntMap) LockFunc(f func(m map[string]int)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) {
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
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
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
}
}

View File

@ -1,318 +0,0 @@
// 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 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"
)
type StringInterfaceMap struct {
mu *rwmutex.RWMutex
m map[string]interface{}
}
// NewStringInterfaceMap returns an empty StringInterfaceMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStringInterfaceMap(unsafe ...bool) *StringInterfaceMap {
return &StringInterfaceMap{
m: make(map[string]interface{}),
mu: rwmutex.New(unsafe...),
}
}
// NewStringInterfaceMapFrom returns an StringInterfaceMap object from given map <m>.
// 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...),
}
}
// NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// 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...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) 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 *StringInterfaceMap) Clone() *StringInterfaceMap {
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
}
// Set sets key-value to the hash map.
func (gm *StringInterfaceMap) Set(key string, val interface{}) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// 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()
}
// Get returns the value by given <key>.
func (gm *StringInterfaceMap) Get(key string) interface{} {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
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
}
// GetOrSet returns the value by key,
// or set value with given <value> 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
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> 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
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
if v := gm.Get(key); v == nil {
return gm.doSetWithLockCheck(key, f)
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringInterfaceMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f)
return true
}
return false
}
// 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()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *StringInterfaceMap) Remove(key string) interface{} {
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 *StringInterfaceMap) Keys() []string {
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 *StringInterfaceMap) Values() []interface{} {
gm.mu.RLock()
vals := make([]interface{}, 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 <key> exists, or else false.
func (gm *StringInterfaceMap) Contains(key string) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *StringInterfaceMap) Size() int {
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 *StringInterfaceMap) IsEmpty() bool {
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 *StringInterfaceMap) Clear() {
gm.mu.Lock()
gm.m = make(map[string]interface{})
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
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 *StringInterfaceMap) Flip() {
gm.mu.Lock()
defer gm.mu.Unlock()
n := make(map[string]interface{}, len(gm.m))
for k, v := range gm.m {
n[gconv.String(v)] = k
}
gm.m = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) {
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
}
}

View File

@ -1,329 +0,0 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
//
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type StringStringMap struct {
mu *rwmutex.RWMutex
m map[string]string
}
// NewStringStringMap returns an empty StringStringMap object.
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStringStringMap(unsafe...bool) *StringStringMap {
return &StringStringMap{
m : make(map[string]string),
mu : rwmutex.New(unsafe...),
}
}
// NewStringStringMapFrom returns an StringStringMap object from given map <m>.
// Notice that, the param map is a type of pointer,
// there might be some concurrent-safe issues when changing the map outside.
func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap {
return &StringStringMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// NewStringStringMapFromArray returns an StringStringMap object from given array.
// The param <keys> given as the keys of the map,
// and <values> as its corresponding values.
//
// If length of <keys> is greater than that of <values>,
// the corresponding overflow map values will be the default value of its type.
func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool) *StringStringMap {
m := make(map[string]string)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = ""
}
}
return &StringStringMap{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *StringStringMap) Iterator(f func (k string, 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 *StringStringMap) Clone() *StringStringMap {
return NewStringStringMapFrom(gm.Map(), !gm.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (gm *StringStringMap) Map() map[string]string {
m := make(map[string]string)
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.
func (gm *StringStringMap) Set(key string, val string) {
gm.mu.Lock()
gm.m[key] = val
gm.mu.Unlock()
}
// BatchSet batch sets key-values to the hash map.
func (gm *StringStringMap) BatchSet(m map[string]string) {
gm.mu.Lock()
for k, v := range m {
gm.m[k] = v
}
gm.mu.Unlock()
}
// Get returns the value by given <key>.
func (gm *StringStringMap) Get(key string) string {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
return val
}
// 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 (gm *StringStringMap) doSetWithLockCheck(key string, 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
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (gm *StringStringMap) GetOrSet(key string, value string) string {
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 <f> if not exist
// and returns this value.
func (gm *StringStringMap) GetOrSetFunc(key string, 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
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (gm *StringStringMap) GetOrSetFuncLock(key string, 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
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringStringMap) SetIfNotExist(key string, value string) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringStringMap) SetIfNotExistFunc(key string, f func() string) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (gm *StringStringMap) SetIfNotExistFuncLock(key string, f func() string) bool {
if !gm.Contains(key) {
gm.mu.Lock()
defer gm.mu.Unlock()
if _, ok := gm.m[key]; !ok {
gm.m[key] = f()
}
return true
}
return false
}
// BatchRemove batch deletes values of the map by keys.
func (gm *StringStringMap) BatchRemove(keys []string) {
gm.mu.Lock()
for _, key := range keys {
delete(gm.m, key)
}
gm.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (gm *StringStringMap) Remove(key string) string {
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 *StringStringMap) Keys() []string {
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 *StringStringMap) Values() []string {
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 <key> exists, or else false.
func (gm *StringStringMap) Contains(key string) bool {
gm.mu.RLock()
_, exists := gm.m[key]
gm.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (gm *StringStringMap) Size() int {
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 *StringStringMap) IsEmpty() bool {
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 *StringStringMap) Clear() {
gm.mu.Lock()
gm.m = make(map[string]string)
gm.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> and mutex.Lock.
func (gm *StringStringMap) LockFunc(f func(m map[string]string)) {
gm.mu.Lock()
defer gm.mu.Unlock()
f(gm.m)
}
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) {
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 *StringStringMap) Flip() {
gm.mu.Lock()
defer gm.mu.Unlock()
n := make(map[string]string, len(gm.m))
for k, v := range gm.m {
n[v] = k
}
gm.m = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <gm>.
func (gm *StringStringMap) Merge(other *StringStringMap) {
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
}
}

View File

@ -0,0 +1,30 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"github.com/gogf/gf/g/container/gtree"
)
// Map based on red-black tree, alias of RedBlackTree.
type TreeMap = gtree.RedBlackTree
// NewTreeMap instantiates a tree map with the custom comparator.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe...bool) *TreeMap {
return gtree.NewRedBlackTree(comparator, unsafe...)
}
// NewTreeMapFrom instantiates a tree map with the custom comparator and <data> map.
// 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.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *TreeMap {
return gtree.NewRedBlackTreeFrom(comparator, data, unsafe...)
}

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -9,9 +15,6 @@ import (
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() {
@ -45,9 +48,6 @@ func Test_Map_Basic(t *testing.T) {
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) {
@ -63,9 +63,9 @@ func Test_Map_Set_Fun(t *testing.T) {
func Test_Map_Batch(t *testing.T) {
m := gmap.New()
m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.BatchRemove([]interface{}{"key1", 1})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_Map_Iterator(t *testing.T){

View File

@ -0,0 +1,55 @@
// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/util/gutil"
"testing"
)
var hashMap = gmap.New()
var listMap = gmap.NewListMap()
var treeMap = gmap.NewTreeMap(gutil.ComparatorInt)
func Benchmark_HashMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
hashMap.Set(i, i)
}
}
func Benchmark_ListMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
listMap.Set(i, i)
}
}
func Benchmark_TreeMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
treeMap.Set(i, i)
}
}
func Benchmark_HashMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
hashMap.Get(i)
}
}
func Benchmark_ListMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
listMap.Get(i)
}
}
func Benchmark_TreeMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
treeMap.Get(i)
}
}

View File

@ -6,31 +6,21 @@
// go test *.go -bench=".*" -benchmem
package gmap
package gmap_test
import (
"testing"
"github.com/gogf/gf/g/container/gmap"
"testing"
"strconv"
)
var ibm = NewIntBoolMap()
var iim = NewIntIntMap()
var iifm = NewIntInterfaceMap()
var ism = NewIntStringMap()
var ififm = NewMap()
var sbm = NewStringBoolMap()
var sim = NewStringIntMap()
var sifm = NewStringInterfaceMap()
var ssm = NewStringStringMap()
// 写入性能测试
func Benchmark_IntBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ibm.Set(i, true)
}
}
var ififm = gmap.New()
var iim = gmap.NewIntIntMap()
var iifm = gmap.NewIntAnyMap()
var ism = gmap.NewIntStrMap()
var sim = gmap.NewStrIntMap()
var sifm = gmap.NewStrAnyMap()
var ssm = gmap.NewStrStrMap()
func Benchmark_IntIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
@ -38,56 +28,43 @@ func Benchmark_IntIntMap_Set(b *testing.B) {
}
}
func Benchmark_IntInterfaceMap_Set(b *testing.B) {
func Benchmark_IntAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Set(i, i)
}
}
func Benchmark_IntStringMap_Set(b *testing.B) {
func Benchmark_IntStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Set(i, strconv.Itoa(i))
}
}
func Benchmark_InterfaceInterfaceMap_Set(b *testing.B) {
func Benchmark_AnyAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Set(i, i)
}
}
func Benchmark_StringBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sbm.Set(strconv.Itoa(i), true)
}
}
func Benchmark_StringIntMap_Set(b *testing.B) {
func Benchmark_StrIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Set(strconv.Itoa(i), i)
}
}
func Benchmark_StringInterfaceMap_Set(b *testing.B) {
func Benchmark_StrAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Set(strconv.Itoa(i), i)
}
}
func Benchmark_StringStringMap_Set(b *testing.B) {
func Benchmark_StrStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
}
}
// 读取性能测试
func Benchmark_IntBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ibm.Get(i)
}
}
func Benchmark_IntIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
@ -95,43 +72,37 @@ func Benchmark_IntIntMap_Get(b *testing.B) {
}
}
func Benchmark_IntInterfaceMap_Get(b *testing.B) {
func Benchmark_IntAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Get(i)
}
}
func Benchmark_IntStringMap_Get(b *testing.B) {
func Benchmark_IntStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Get(i)
}
}
func Benchmark_InterfaceInterfaceMap_Get(b *testing.B) {
func Benchmark_AnyAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Get(i)
}
}
func Benchmark_StringBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sbm.Get(strconv.Itoa(i))
}
}
func Benchmark_StringIntMap_Get(b *testing.B) {
func Benchmark_StrIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Get(strconv.Itoa(i))
}
}
func Benchmark_StringInterfaceMap_Get(b *testing.B) {
func Benchmark_StrAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Get(strconv.Itoa(i))
}
}
func Benchmark_StringStringMap_Get(b *testing.B) {
func Benchmark_StrStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Get(strconv.Itoa(i))
}

View File

@ -6,75 +6,61 @@
// go test *.go -bench=".*" -benchmem
package gmap
package gmap_test
import (
"testing"
"github.com/gogf/gf/g/container/gmap"
"testing"
"strconv"
)
var ibmUnsafe = NewIntBoolMap(true)
var iimUnsafe = NewIntIntMap(true)
var iifmUnsafe = NewIntInterfaceMap(true)
var ismUnsafe = NewIntStringMap(true)
var ififmUnsafe = NewMap(true)
var sbmUnsafe = NewStringBoolMap(true)
var simUnsafe = NewStringIntMap(true)
var sifmUnsafe = NewStringInterfaceMap(true)
var ssmUnsafe = NewStringStringMap(true)
var ififmUnsafe = gmap.New(true)
var iimUnsafe = gmap.NewIntIntMap(true)
var iifmUnsafe = gmap.NewIntAnyMap(true)
var ismUnsafe = gmap.NewIntStrMap(true)
var simUnsafe = gmap.NewStrIntMap(true)
var sifmUnsafe = gmap.NewStrAnyMap(true)
var ssmUnsafe = gmap.NewStrStrMap(true)
// 写入性能测试
func Benchmark_Unsafe_IntBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ibmUnsafe.Set(i, true)
}
}
func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iimUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_IntInterfaceMap_Set(b *testing.B) {
func Benchmark_Unsafe_IntAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iifmUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_IntStringMap_Set(b *testing.B) {
func Benchmark_Unsafe_IntStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ismUnsafe.Set(i, strconv.Itoa(i))
}
}
func Benchmark_Unsafe_InterfaceInterfaceMap_Set(b *testing.B) {
func Benchmark_Unsafe_AnyAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ififmUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_StringBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sbmUnsafe.Set(strconv.Itoa(i), true)
}
}
func Benchmark_Unsafe_StringIntMap_Set(b *testing.B) {
func Benchmark_Unsafe_StrIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
simUnsafe.Set(strconv.Itoa(i), i)
}
}
func Benchmark_Unsafe_StringInterfaceMap_Set(b *testing.B) {
func Benchmark_Unsafe_StrAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sifmUnsafe.Set(strconv.Itoa(i), i)
}
}
func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) {
func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i))
}
@ -83,11 +69,6 @@ func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) {
// 读取性能测试
func Benchmark_Unsafe_IntBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ibmUnsafe.Get(i)
}
}
func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
@ -95,43 +76,37 @@ func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
}
}
func Benchmark_Unsafe_IntInterfaceMap_Get(b *testing.B) {
func Benchmark_Unsafe_IntAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iifmUnsafe.Get(i)
}
}
func Benchmark_Unsafe_IntStringMap_Get(b *testing.B) {
func Benchmark_Unsafe_IntStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ismUnsafe.Get(i)
}
}
func Benchmark_Unsafe_InterfaceInterfaceMap_Get(b *testing.B) {
func Benchmark_Unsafe_AnyAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ififmUnsafe.Get(i)
}
}
func Benchmark_Unsafe_StringBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sbmUnsafe.Get(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StringIntMap_Get(b *testing.B) {
func Benchmark_Unsafe_StrIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
simUnsafe.Get(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StringInterfaceMap_Get(b *testing.B) {
func Benchmark_Unsafe_StrAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sifmUnsafe.Get(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StringStringMap_Get(b *testing.B) {
func Benchmark_Unsafe_StrStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ssmUnsafe.Get(strconv.Itoa(i))
}

View File

@ -23,7 +23,7 @@ func Example_Normal_Basic() {
fmt.Println(m.Values())
//Batch add data
m.BatchSet(add_map)
m.Sets(add_map)
//Gets the value of the corresponding key
key3_val := m.Get("key3")
@ -43,7 +43,7 @@ func Example_Normal_Basic() {
//Batch remove keys
remove_keys := []interface{}{"key1", 1}
m.BatchRemove(remove_keys)
m.Removes(remove_keys)
fmt.Println(m.Keys())
//Contains checks whether a key exists.

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -6,15 +12,15 @@ import (
"testing"
)
func getInterface() interface{} {
func getAny() interface{} {
return 123
}
func intInterfaceCallBack(int, interface{}) bool {
func intAnyCallBack(int, interface{}) bool {
return true
}
func Test_IntInterfaceMap_Basic(t *testing.T) {
func Test_IntAnyMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntInterfaceMap()
m := gmap.NewIntAnyMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
@ -40,40 +46,37 @@ func Test_IntInterfaceMap_Basic(t *testing.T) {
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
m2 := gmap.NewIntAnyMapFrom(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()
func Test_IntAnyMap_Set_Fun(t *testing.T) {
m := gmap.NewIntAnyMap()
m.GetOrSetFunc(1, getInterface)
m.GetOrSetFuncLock(2, getInterface)
m.GetOrSetFunc(1, getAny)
m.GetOrSetFuncLock(2, getAny)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false)
gtest.Assert(m.SetIfNotExistFunc(3, getInterface), true)
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getInterface), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
}
func Test_IntInterfaceMap_Batch(t *testing.T) {
m := gmap.NewIntInterfaceMap()
func Test_IntAnyMap_Batch(t *testing.T) {
m := gmap.NewIntAnyMap()
m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3})
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
m.BatchRemove([]int{1, 2})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
}
func Test_IntInterfaceMap_Iterator(t *testing.T){
func Test_IntAnyMap_Iterator(t *testing.T){
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntInterfaceMapFrom(expect)
m := gmap.NewIntAnyMapFrom(expect)
m.Iterator(func(k int, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
@ -95,9 +98,9 @@ func Test_IntInterfaceMap_Iterator(t *testing.T){
}
func Test_IntInterfaceMap_Lock(t *testing.T){
func Test_IntAnyMap_Lock(t *testing.T){
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntInterfaceMapFrom(expect)
m := gmap.NewIntAnyMapFrom(expect)
m.LockFunc(func(m map[int]interface{}) {
gtest.Assert(m, expect)
})
@ -105,9 +108,9 @@ func Test_IntInterfaceMap_Lock(t *testing.T){
gtest.Assert(m, expect)
})
}
func Test_IntInterfaceMap_Clone(t *testing.T) {
func Test_IntAnyMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
m_clone := m.Clone()
m.Remove(1)
@ -118,9 +121,9 @@ func Test_IntInterfaceMap_Clone(t *testing.T) {
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntInterfaceMap_Merge(t *testing.T) {
m1 := gmap.NewIntInterfaceMap()
m2 := gmap.NewIntInterfaceMap()
func Test_IntAnyMap_Merge(t *testing.T) {
m1 := gmap.NewIntAnyMap()
m2 := gmap.NewIntAnyMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)

View File

@ -1,123 +0,0 @@
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.SetIfNotExistFunc(4, getBool), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock(3, getBool), true)
}
func Test_IntBoolMap_Batch(t *testing.T) {
m := gmap.NewIntBoolMap()
m.BatchSet(map[int]bool{1: true, 2: false, 3: true})
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_Iterator(t *testing.T){
expect := map[int]bool{1: true, 2: false}
m := gmap.NewIntBoolMapFrom(expect)
m.Iterator(func(k int, v bool) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v bool) bool {
i++
return true
})
m.Iterator(func(k int, v bool) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_IntBoolMap_Lock(t *testing.T){
expect := map[int]bool{1: true, 2: false}
m := gmap.NewIntBoolMapFrom(expect)
m.LockFunc(func(m map[int]bool) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[int]bool) {
gtest.Assert(m, expect)
})
}
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})
}

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -42,9 +48,6 @@ func Test_IntIntMap_Basic(t *testing.T) {
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) {
@ -65,10 +68,10 @@ func Test_IntIntMap_Set_Fun(t *testing.T) {
func Test_IntIntMap_Batch(t *testing.T) {
m := gmap.NewIntIntMap()
m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3})
m.Sets(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})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]int{3: 3})
}

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -6,15 +12,15 @@ import (
"testing"
)
func getString() string {
func getStr() string {
return "z"
}
func intStringCallBack(int, string) bool {
func intStrCallBack(int, string) bool {
return true
}
func Test_IntStringMap_Basic(t *testing.T) {
func Test_IntStrMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntStringMap()
m := gmap.NewIntStrMap()
m.Set(1, "a")
gtest.Assert(m.Get(1), "a")
@ -36,7 +42,7 @@ func Test_IntStringMap_Basic(t *testing.T) {
//反转之后不成为以下 map,flip 操作只是翻转原 map
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
m_f := gmap.NewIntStringMap()
m_f := gmap.NewIntStrMap()
m_f.Set(1, "2")
m_f.Flip()
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
@ -45,39 +51,36 @@ func Test_IntStringMap_Basic(t *testing.T) {
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"})
m2 := gmap.NewIntStrMapFrom(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()
func Test_IntStrMap_Set_Fun(t *testing.T) {
m := gmap.NewIntStrMap()
m.GetOrSetFunc(1, getString)
m.GetOrSetFuncLock(2, getString)
m.GetOrSetFunc(1, getStr)
m.GetOrSetFuncLock(2, getStr)
gtest.Assert(m.Get(1), "z")
gtest.Assert(m.Get(2), "z")
gtest.Assert(m.SetIfNotExistFunc(1, getString), false)
gtest.Assert(m.SetIfNotExistFunc(3, getString), true)
gtest.Assert(m.SetIfNotExistFunc(1, getStr), false)
gtest.Assert(m.SetIfNotExistFunc(3, getStr), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getString), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
}
func Test_IntStringMap_Batch(t *testing.T) {
m := gmap.NewIntStringMap()
func Test_IntStrMap_Batch(t *testing.T) {
m := gmap.NewIntStrMap()
m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"})
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
m.BatchRemove([]int{1, 2})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
}
func Test_IntStringMap_Iterator(t *testing.T){
func Test_IntStrMap_Iterator(t *testing.T){
expect := map[int]string{1: "a", 2: "b"}
m := gmap.NewIntStringMapFrom(expect)
m := gmap.NewIntStrMapFrom(expect)
m.Iterator(func(k int, v string) bool {
gtest.Assert(expect[k], v)
return true
@ -97,10 +100,10 @@ func Test_IntStringMap_Iterator(t *testing.T){
gtest.Assert(j, 1)
}
func Test_IntStringMap_Lock(t *testing.T){
func Test_IntStrMap_Lock(t *testing.T){
expect := map[int]string{1: "a", 2: "b", 3: "c"}
m := gmap.NewIntStringMapFrom(expect)
m := gmap.NewIntStrMapFrom(expect)
m.LockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
})
@ -109,9 +112,9 @@ func Test_IntStringMap_Lock(t *testing.T){
})
}
func Test_IntStringMap_Clone(t *testing.T) {
func Test_IntStrMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
m_clone := m.Clone()
m.Remove(1)
@ -122,9 +125,9 @@ func Test_IntStringMap_Clone(t *testing.T) {
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntStringMap_Merge(t *testing.T) {
m1 := gmap.NewIntStringMap()
m2 := gmap.NewIntStringMap()
func Test_IntStrMap_Merge(t *testing.T) {
m1 := gmap.NewIntStrMap()
m2 := gmap.NewIntStrMap()
m1.Set(1, "a")
m2.Set(2, "b")
m1.Merge(m2)

View File

@ -0,0 +1,120 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_List_Map_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewListMap()
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.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_List_Map_Set_Fun(t *testing.T) {
m := gmap.NewListMap()
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_List_Map_Batch(t *testing.T) {
m := gmap.NewListMap()
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_List_Map_Iterator(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewListMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_List_Map_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewListMapFrom(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_List_Map_Basic_Merge(t *testing.T) {
m1 := gmap.NewListMap()
m2 := gmap.NewListMap()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
}
func Test_List_Map_Order(t *testing.T) {
m := gmap.NewListMap()
m.Set("k1", "v1")
m.Set("k2", "v2")
m.Set("k3", "v3")
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
}

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -6,12 +12,12 @@ import (
"testing"
)
func stringInterfaceCallBack(string, interface{}) bool {
func stringAnyCallBack(string, interface{}) bool {
return true
}
func Test_StringInterfaceMap_Basic(t *testing.T) {
func Test_StrAnyMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringInterfaceMap()
m := gmap.NewStrAnyMap()
m.Set("a", 1)
gtest.Assert(m.Get("a"), 1)
@ -38,40 +44,37 @@ func Test_StringInterfaceMap_Basic(t *testing.T) {
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
m2 := gmap.NewStrAnyMapFrom(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()
func Test_StrAnyMap_Set_Fun(t *testing.T) {
m := gmap.NewStrAnyMap()
m.GetOrSetFunc("a", getInterface)
m.GetOrSetFuncLock("b", getInterface)
m.GetOrSetFunc("a", getAny)
m.GetOrSetFuncLock("b", getAny)
gtest.Assert(m.Get("a"), 123)
gtest.Assert(m.Get("b"), 123)
gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false)
gtest.Assert(m.SetIfNotExistFunc("c", getInterface), true)
gtest.Assert(m.SetIfNotExistFunc("a", getAny), false)
gtest.Assert(m.SetIfNotExistFunc("c", getAny), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getInterface), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getAny), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getAny), true)
}
func Test_StringInterfaceMap_Batch(t *testing.T) {
m := gmap.NewStringInterfaceMap()
func Test_StrAnyMap_Batch(t *testing.T) {
m := gmap.NewStrAnyMap()
m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3})
m.Sets(map[string]interface{}{"a": 1, "b": "2", "c": 3})
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
m.BatchRemove([]string{"a", "b"})
m.Removes([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
}
func Test_StringInterfaceMap_Iterator(t *testing.T) {
func Test_StrAnyMap_Iterator(t *testing.T) {
expect := map[string]interface{}{"a": true, "b": false}
m := gmap.NewStringInterfaceMapFrom(expect)
m := gmap.NewStrAnyMapFrom(expect)
m.Iterator(func(k string, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
@ -91,10 +94,10 @@ func Test_StringInterfaceMap_Iterator(t *testing.T) {
gtest.Assert(j, 1)
}
func Test_StringInterfaceMap_Lock(t *testing.T) {
func Test_StrAnyMap_Lock(t *testing.T) {
expect := map[string]interface{}{"a": true, "b": false}
m := gmap.NewStringInterfaceMapFrom(expect)
m := gmap.NewStrAnyMapFrom(expect)
m.LockFunc(func(m map[string]interface{}) {
gtest.Assert(m, expect)
})
@ -102,9 +105,9 @@ func Test_StringInterfaceMap_Lock(t *testing.T) {
gtest.Assert(m, expect)
})
}
func Test_StringInterfaceMap_Clone(t *testing.T) {
func Test_StrAnyMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
m := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"})
m_clone := m.Clone()
m.Remove("a")
@ -115,9 +118,9 @@ func Test_StringInterfaceMap_Clone(t *testing.T) {
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringInterfaceMap_Merge(t *testing.T) {
m1 := gmap.NewStringInterfaceMap()
m2 := gmap.NewStringInterfaceMap()
func Test_StrAnyMap_Merge(t *testing.T) {
m1 := gmap.NewStrAnyMap()
m2 := gmap.NewStrAnyMap()
m1.Set("a", 1)
m2.Set("b", "2")
m1.Merge(m2)

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -9,9 +15,9 @@ import (
func stringIntCallBack(string, int) bool {
return true
}
func Test_StringIntMap_Basic(t *testing.T) {
func Test_StrIntMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringIntMap()
m := gmap.NewStrIntMap()
m.Set("a", 1)
gtest.Assert(m.Get("a"), 1)
@ -31,7 +37,7 @@ func Test_StringIntMap_Basic(t *testing.T) {
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
m_f := gmap.NewStringIntMap()
m_f := gmap.NewStrIntMap()
m_f.Set("1", 2)
m_f.Flip()
gtest.Assert(m_f.Map(), map[string]int{"2": 1})
@ -40,15 +46,12 @@ func Test_StringIntMap_Basic(t *testing.T) {
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2})
m2 := gmap.NewStrIntMapFrom(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()
func Test_StrIntMap_Set_Fun(t *testing.T) {
m := gmap.NewStrIntMap()
m.GetOrSetFunc("a", getInt)
m.GetOrSetFuncLock("b", getInt)
@ -62,17 +65,17 @@ func Test_StringIntMap_Set_Fun(t *testing.T) {
}
func Test_StringIntMap_Batch(t *testing.T) {
m := gmap.NewStringIntMap()
func Test_StrIntMap_Batch(t *testing.T) {
m := gmap.NewStrIntMap()
m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3})
m.Sets(map[string]int{"a": 1, "b": 2, "c": 3})
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
m.BatchRemove([]string{"a", "b"})
m.Removes([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]int{"c": 3})
}
func Test_StringIntMap_Iterator(t *testing.T) {
func Test_StrIntMap_Iterator(t *testing.T) {
expect := map[string]int{"a": 1, "b": 2}
m := gmap.NewStringIntMapFrom(expect)
m := gmap.NewStrIntMapFrom(expect)
m.Iterator(func(k string, v int) bool {
gtest.Assert(expect[k], v)
return true
@ -93,10 +96,10 @@ func Test_StringIntMap_Iterator(t *testing.T) {
}
func Test_StringIntMap_Lock(t *testing.T) {
func Test_StrIntMap_Lock(t *testing.T) {
expect := map[string]int{"a": 1, "b": 2}
m := gmap.NewStringIntMapFrom(expect)
m := gmap.NewStrIntMapFrom(expect)
m.LockFunc(func(m map[string]int) {
gtest.Assert(m, expect)
})
@ -105,9 +108,9 @@ func Test_StringIntMap_Lock(t *testing.T) {
})
}
func Test_StringIntMap_Clone(t *testing.T) {
func Test_StrIntMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
m := gmap.NewStrIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
m_clone := m.Clone()
m.Remove("a")
@ -118,9 +121,9 @@ func Test_StringIntMap_Clone(t *testing.T) {
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringIntMap_Merge(t *testing.T) {
m1 := gmap.NewStringIntMap()
m2 := gmap.NewStringIntMap()
func Test_StrIntMap_Merge(t *testing.T) {
m1 := gmap.NewStrIntMap()
m2 := gmap.NewStrIntMap()
m1.Set("a", 1)
m2.Set("b", 2)
m1.Merge(m2)

View File

@ -1,3 +1,9 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
@ -6,12 +12,12 @@ import (
"testing"
)
func stringStringCallBack(string, string) bool {
func stringStrCallBack(string, string) bool {
return true
}
func Test_StringStringMap_Basic(t *testing.T) {
func Test_StrStrMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringStringMap()
m := gmap.NewStrStrMap()
m.Set("a", "a")
gtest.Assert(m.Get("a"), "a")
@ -39,39 +45,36 @@ func Test_StringStringMap_Basic(t *testing.T) {
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"})
m2 := gmap.NewStrStrMapFrom(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()
func Test_StrStrMap_Set_Fun(t *testing.T) {
m := gmap.NewStrStrMap()
m.GetOrSetFunc("a", getString)
m.GetOrSetFuncLock("b", getString)
m.GetOrSetFunc("a", getStr)
m.GetOrSetFuncLock("b", getStr)
gtest.Assert(m.Get("a"), "z")
gtest.Assert(m.Get("b"), "z")
gtest.Assert(m.SetIfNotExistFunc("a", getString), false)
gtest.Assert(m.SetIfNotExistFunc("c", getString), true)
gtest.Assert(m.SetIfNotExistFunc("a", getStr), false)
gtest.Assert(m.SetIfNotExistFunc("c", getStr), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getString), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getStr), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getStr), true)
}
func Test_StringStringMap_Batch(t *testing.T) {
m := gmap.NewStringStringMap()
func Test_StrStrMap_Batch(t *testing.T) {
m := gmap.NewStrStrMap()
m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"})
m.Sets(map[string]string{"a": "a", "b": "b", "c": "c"})
gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"})
m.BatchRemove([]string{"a", "b"})
m.Removes([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]string{"c": "c"})
}
func Test_StringStringMap_Iterator(t *testing.T) {
func Test_StrStrMap_Iterator(t *testing.T) {
expect := map[string]string{"a": "a", "b": "b"}
m := gmap.NewStringStringMapFrom(expect)
m := gmap.NewStrStrMapFrom(expect)
m.Iterator(func(k string, v string) bool {
gtest.Assert(expect[k], v)
return true
@ -91,10 +94,10 @@ func Test_StringStringMap_Iterator(t *testing.T) {
gtest.Assert(j, 1)
}
func Test_StringStringMap_Lock(t *testing.T) {
func Test_StrStrMap_Lock(t *testing.T) {
expect := map[string]string{"a": "a", "b": "b"}
m := gmap.NewStringStringMapFrom(expect)
m := gmap.NewStrStrMapFrom(expect)
m.LockFunc(func(m map[string]string) {
gtest.Assert(m, expect)
})
@ -102,9 +105,9 @@ func Test_StringStringMap_Lock(t *testing.T) {
gtest.Assert(m, expect)
})
}
func Test_StringStringMap_Clone(t *testing.T) {
func Test_StrStrMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
m := gmap.NewStrStrMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
m_clone := m.Clone()
m.Remove("a")
@ -115,9 +118,9 @@ func Test_StringStringMap_Clone(t *testing.T) {
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringStringMap_Merge(t *testing.T) {
m1 := gmap.NewStringStringMap()
m2 := gmap.NewStringStringMap()
func Test_StrStrMap_Merge(t *testing.T) {
m1 := gmap.NewStrStrMap()
m2 := gmap.NewStrStrMap()
m1.Set("a", "a")
m2.Set("b", "b")
m1.Merge(m2)

View File

@ -1,122 +0,0 @@
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.SetIfNotExistFunc("c", getBool), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getBool), true)
}
func Test_StringBoolMap_Batch(t *testing.T) {
m := gmap.NewStringBoolMap()
m.BatchSet(map[string]bool{"a": true, "b": false, "c": true})
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_Iterator(t *testing.T) {
expect := map[string]bool{"a": true, "b": false}
m := gmap.NewStringBoolMapFrom(expect)
m.Iterator(func(k string, v bool) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k string, v bool) bool {
i++
return true
})
m.Iterator(func(k string, v bool) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_StringBoolMap_Lock(t *testing.T) {
expect := map[string]bool{"a": true, "b": false}
m := gmap.NewStringBoolMapFrom(expect)
m.LockFunc(func(m map[string]bool) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[string]bool) {
gtest.Assert(m, expect)
})
}
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})
}

View File

@ -0,0 +1,103 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gutil"
"testing"
)
func Test_Tree_Map_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewTreeMap(gutil.ComparatorString)
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.NewTreeMapFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_Tree_Map_Set_Fun(t *testing.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)
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_Tree_Map_Batch(t *testing.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_Tree_Map_Iterator(t *testing.T){
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewTreeMapFrom(gutil.ComparatorString, expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_Tree_Map_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewTreeMapFrom(gutil.ComparatorString, 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())
}

View File

@ -4,7 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gpool provides a object-reusable concurrent-safe pool.
// Package gpool provides object-reusable concurrent-safe pool.
package gpool
import (
@ -16,31 +16,38 @@ import (
"time"
)
// 对象池
// Object-Reusable Pool.
type Pool struct {
list *glist.List // 可用/闲置的文件指针链表
closed *gtype.Bool // 连接池是否已关闭
Expire int64 // (毫秒)闲置最大时间,超过该时间则被系统回收
NewFunc func()(interface{}, error) // 创建对象的方法定义
ExpireFunc func(interface{}) // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时,需要定义该方法)
// 例如: net.Conn, os.File等对象都需要执行额外关闭操作
list *glist.List // Available/idle list.
closed *gtype.Bool // Whether the pool is closed.
Expire int64 // Max idle time(ms), after which it is recycled.
NewFunc func()(interface{}, error) // Callback function to create item.
ExpireFunc func(interface{}) // Expired destruction function for objects.
// This function needs to be defined when the pool object
// needs to perform additional destruction operations.
// Eg: net.Conn, os.File, etc.
}
// 对象池数据项
// Pool item.
type poolItem struct {
expire int64 // (毫秒)过期时间
value interface{} // 对象值
expire int64 // Expire time(millisecond).
value interface{} // Value.
}
// 对象创建方法类型
// Creation function for object.
type NewFunc func() (interface{}, error)
// 对象过期方法类型
// Destruction function for object.
type ExpireFunc func(interface{})
// 创建一个对象池,为保证执行效率,过期时间一旦设定之后无法修改
// expire = 0表示不过期expire < 0表示使用完立即回收expire > 0表示超时回收
// 注意过期时间单位为**毫秒**
// New returns a new object pool.
// To ensure execution efficiency, the expiration time cannot be modified once it is set.
// Expire:
// expire = 0 : not expired;
// expire < 0 : immediate recovery after use;
// expire > 0 : timeout recovery;
// Note that the expiration time unit is ** milliseconds **.
func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
r := &Pool {
list : glist.New(),
@ -55,7 +62,7 @@ func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
return r
}
// 放一个临时对象到池中
// Put puts an item to pool.
func (p *Pool) Put(value interface{}) {
item := &poolItem {
value : value,
@ -68,12 +75,12 @@ func (p *Pool) Put(value interface{}) {
p.list.PushBack(item)
}
// 清空对象池
// Clear clears pool, which means it will remove all items from pool.
func (p *Pool) Clear() {
p.list.RemoveAll()
}
// 从池中获得一个临时对象
// Get picks an item from pool.
func (p *Pool) Get() (interface{}, error) {
for !p.closed.Val() {
if r := p.list.PopFront(); r != nil {
@ -91,17 +98,17 @@ func (p *Pool) Get() (interface{}, error) {
return nil, errors.New("pool is empty")
}
// 查询当前池中的对象数量
// Size returns the count of available items of pool.
func (p *Pool) Size() int {
return p.list.Len()
}
// 关闭池
// Close closes the pool.
func (p *Pool) Close() {
p.closed.Set(true)
}
// 超时检测循环
// checkExpire secondly removes expired items from pool.
func (p *Pool) checkExpire() {
if p.closed.Val() {
gtimer.Exit()

View File

@ -36,8 +36,9 @@ const (
gDEFAULT_QUEUE_SIZE = 10000
)
// New returns a queue object.
// Param <limit> is optional and it is not limited by default.
// New returns an empty queue object.
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited by default.
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib chan.
func New(limit...int) *Queue {
q := &Queue {
closed : make(chan struct{}, 0),
@ -84,7 +85,7 @@ func (q *Queue) startAsyncLoop() {
}
// Push pushes the data <v> into the queue.
// Note that it would panics if the Push method is called after the queue is closed.
// Note that it would panics if Push is called after the queue is closed.
func (q *Queue) Push(v interface{}) {
if q.limit > 0 {
q.C <- v
@ -95,14 +96,14 @@ func (q *Queue) Push(v interface{}) {
}
// Pop pops an item from the queue in FIFO way.
// Note that it would return nil immediately if the Pop method is called after the queue is closed.
// Note that it would return nil immediately if Pop is called after the queue is closed.
func (q *Queue) Pop() interface{} {
return <- q.C
}
// Close closes the queue.
// Notice: It would notify all goroutines exit immediately,
// which are blocked reading by Pop method).
// Notice: It would notify all goroutines return immediately,
// which are being blocked reading by Pop method.
func (q *Queue) Close() {
close(q.C)
close(q.events)

View File

@ -14,11 +14,12 @@ import (
)
type Ring struct {
mu *rwmutex.RWMutex // 互斥锁
ring *ring.Ring // 底层环形数据结构
len *gtype.Int // 数据大小(已使用的大小)
cap *gtype.Int // 总长度(分配的环大小,包括未使用的数据项数量)
dirty *gtype.Bool // 标记环是否脏了(需要重新计算大小,当环大小发生改变时做标记)
mu *rwmutex.RWMutex
ring *ring.Ring // Underlying ring.
len *gtype.Int // Length(already used size).
cap *gtype.Int // Capability(>=len).
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated.
// It's marked dirty when the size of ring changes.
}
func New(cap int, unsafe...bool) *Ring {
@ -31,7 +32,7 @@ func New(cap int, unsafe...bool) *Ring {
}
}
// 返回当前环指向的数据项值
// Val returns the item's value of current position.
func (r *Ring) Val() interface{} {
r.mu.RLock()
v := r.ring.Value
@ -39,19 +40,19 @@ func (r *Ring) Val() interface{} {
return v
}
// 返回当前环已有数据项大小
// Len returns the size of ring.
func (r *Ring) Len() int {
r.checkAndUpdateLenAndCap()
return r.len.Val()
}
// 返回当前环总大小(包含未使用长度)
// Cap returns the capacity of ring.
func (r *Ring) Cap() int {
r.checkAndUpdateLenAndCap()
return r.cap.Val()
}
// 检测并执行len和cap的更新(两者必须一起更新)
// Checks and updates the len and cap of ring when ring is dirty.
func (r *Ring) checkAndUpdateLenAndCap() {
if !r.dirty.Val() {
return
@ -73,7 +74,7 @@ func (r *Ring) checkAndUpdateLenAndCap() {
r.dirty.Set(false)
}
// 当前位置设置数据项值
// Set sets value to the item of current position.
func (r *Ring) Set(value interface{}) *Ring {
r.mu.Lock()
if r.ring.Value == nil {
@ -84,7 +85,7 @@ func (r *Ring) Set(value interface{}) *Ring {
return r
}
// Set & Next
// Put sets <value> to current item of ring and moves position to next item.
func (r *Ring) Put(value interface{}) *Ring {
r.mu.Lock()
if r.ring.Value == nil {
@ -96,7 +97,8 @@ func (r *Ring) Put(value interface{}) *Ring {
return r
}
// 环往后(n > 0)或者往前(n < 0)移动n个元素
// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
// in the ring and returns that ring element. r must not be empty.
func (r *Ring) Move(n int) *Ring {
r.mu.Lock()
r.ring = r.ring.Move(n)
@ -104,7 +106,7 @@ func (r *Ring) Move(n int) *Ring {
return r
}
// 环往前移动1个元素
// Prev returns the previous ring element. r must not be empty.
func (r *Ring) Prev() *Ring {
r.mu.Lock()
r.ring = r.ring.Prev()
@ -112,7 +114,7 @@ func (r *Ring) Prev() *Ring {
return r
}
// 环往后移动1个元素
// Next returns the next ring element. r must not be empty.
func (r *Ring) Next() *Ring {
r.mu.Lock()
r.ring = r.ring.Next()
@ -120,11 +122,22 @@ func (r *Ring) Next() *Ring {
return r
}
// 连接两个环,两个环的大小和位置都有可能会发生改变。
// 1、链接将环r与环s连接使得r.Next()成为s并返回r.Next()的原始值。r一定不能为空。
// 2、如果r和s指向同一个环则链接它们会从环中移除r和s之间的元素。
// 删除的元素形成子环,结果是对该子环的引用(如果没有删除元素结果仍然是r.Next()的原始值而不是nil)。
// 3、如果r和s指向不同的环则链接它们会创建一个单独的环并在r之后插入s的元素。 结果指向插入后s的最后一个元素后面的元素。
// Link connects ring r with ring s such that r.Next()
// becomes s and returns the original value for r.Next().
// r must not be empty.
//
// If r and s point to the same ring, linking
// them removes the elements between r and s from the ring.
// The removed elements form a subring and the result is a
// reference to that subring (if no elements were removed,
// the result is still the original value for r.Next(),
// and not nil).
//
// If r and s point to different rings, linking
// them creates a single ring with the elements of s inserted
// after r. The result points to the element following the
// last element of s after insertion.
//
func (r *Ring) Link(s *Ring) *Ring {
r.mu.Lock()
s.mu.Lock()
@ -136,7 +149,10 @@ func (r *Ring) Link(s *Ring) *Ring {
return r
}
// 删除环中当前位置往后的n个数据项
// Unlink removes n % r.Len() elements from the ring r, starting
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
// The result is the removed subring. r must not be empty.
//
func (r *Ring) Unlink(n int) *Ring {
r.mu.Lock()
r.ring = r.ring.Unlink(n)
@ -145,7 +161,9 @@ func (r *Ring) Unlink(n int) *Ring {
return r
}
// 读锁遍历往后只读遍历回调函数返回true表示继续遍历否则退出遍历
// RLockIteratorNext iterates and locks reading forward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
@ -159,7 +177,9 @@ func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
}
}
// 读锁遍历往前只读遍历回调函数返回true表示继续遍历否则退出遍历
// RLockIteratorPrev iterates and locks reading backward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
@ -173,7 +193,9 @@ func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
}
}
// 写锁遍历往后写遍历回调函数返回true表示继续遍历否则退出遍历
// LockIteratorNext iterates and locks writing forward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
@ -187,7 +209,9 @@ func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
}
}
// 写锁遍历往前写遍历回调函数返回true表示继续遍历否则退出遍历
// LockIteratorPrev iterates and locks writing backward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
@ -201,7 +225,7 @@ func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
}
}
// 从当前位置,往后只读完整遍历,返回非空数据项值构成的数组
// SliceNext returns a copy of all item values as slice forward from current position.
func (r *Ring) SliceNext() []interface{} {
s := make([]interface{}, 0)
r.mu.RLock()
@ -217,7 +241,7 @@ func (r *Ring) SliceNext() []interface{} {
return s
}
// 从当前位置,往前只读完整遍历,返回非空数据项值构成的数组
// SlicePrev returns a copy of all item values as slice backward from current position.
func (r *Ring) SlicePrev() []interface{} {
s := make([]interface{}, 0)
r.mu.RLock()

View File

@ -125,19 +125,17 @@ func (set *Set) String() string {
}
// LockFunc locks writing with callback function <f>.
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) {
set.mu.Lock()
defer set.mu.Unlock()
f(set.m)
return set
}
// RLockFunc locks reading with callback function <f>.
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
return set
}
// Equal checks whether the two sets equal.

View File

@ -119,19 +119,17 @@ func (set *IntSet) String() string {
}
// LockFunc locks writing with callback function <f>.
func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
set.mu.Lock()
defer set.mu.Unlock()
f(set.m)
return set
}
// RLockFunc locks reading with callback function <f>.
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
return set
}
// Equal checks whether the two sets equal.

View File

@ -120,19 +120,17 @@ func (set *StringSet) String() string {
}
// LockFunc locks writing with callback function <f>.
func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
func (set *StringSet) LockFunc(f func(m map[string]struct{})) {
set.mu.Lock()
defer set.mu.Unlock()
f(set.m)
return set
}
// RLockFunc locks reading with callback function <f>.
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
return set
}
// Equal checks whether the two sets equal.

View File

@ -1,8 +1,8 @@
// Copyright 2019 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 gm file,
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
// Package gtree provides concurrent-safe/unsafe tree containers.
package gtree

View File

@ -0,0 +1,701 @@
// Copyright 2019 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 gtree
import (
"fmt"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
)
// AVLTree holds elements of the AVL tree.
type AVLTree struct {
mu *rwmutex.RWMutex
root *AVLTreeNode
comparator func(v1, v2 interface{}) int
size int
}
// AVLTreeNode is a single element within the tree.
type AVLTreeNode struct {
Key interface{}
Value interface{}
parent *AVLTreeNode
children [2]*AVLTreeNode
b int8
}
// NewAVLTree instantiates an AVL tree with the custom comparator.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *AVLTree {
return &AVLTree{
mu : rwmutex.New(unsafe...),
comparator: comparator,
}
}
// NewAVLTreeFrom instantiates an AVL tree with the custom comparator and data map.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *AVLTree {
tree := NewAVLTree(comparator, unsafe...)
for k, v := range data {
tree.put(k, v, nil, &tree.root)
}
return tree
}
// Clone returns a new tree with a copy of current tree.
func (tree *AVLTree) Clone(unsafe ...bool) *AVLTree {
newTree := NewAVLTree(tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
}
// Set inserts node into the tree.
func (tree *AVLTree) Set(key interface{}, value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.put(key, value, nil, &tree.root)
}
// Sets batch sets key-values to the tree.
func (tree *AVLTree) Sets(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for key, value := range data {
tree.put(key, value, nil, &tree.root)
}
}
// Search searches the tree with given <key>.
// Second return parameter <found> is true if key was found, otherwise false.
func (tree *AVLTree) Search(key interface{}) (value interface{}, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.doSearch(key)
}
// doSearch searches the tree with given <key>.
// Second return parameter <found> is true if key was found, otherwise false.
func (tree *AVLTree) doSearch(key interface{}) (value interface{}, found bool) {
n := tree.root
for n != nil {
cmp := tree.comparator(key, n.Key)
switch {
case cmp == 0: return n.Value, true
case cmp < 0: n = n.children[0]
case cmp > 0: n = n.children[1]
}
}
return nil, false
}
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
func (tree *AVLTree) Get(key interface{}) (value interface{}) {
value, _ = tree.Search(key)
return
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
tree.mu.Lock()
defer tree.mu.Unlock()
if v, ok := tree.doSearch(key); ok {
return v
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
tree.put(key, value, nil, &tree.root)
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
func (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (tree *AVLTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (tree *AVLTree) GetVar(key interface{}) *gvar.Var {
return gvar.New(tree.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (tree *AVLTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(tree.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (tree *AVLTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (tree *AVLTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (tree *AVLTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f)
return true
}
return false
}
// Contains checks whether <key> exists in the tree.
func (tree *AVLTree) Contains(key interface{}) bool {
_, ok := tree.Search(key)
return ok
}
// Remove remove the node from the tree by key.
// Key should adhere to the comparator's type assertion, otherwise method panics.
func (tree *AVLTree) Remove(key interface{}) (value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
value, _ = tree.remove(key, &tree.root)
return
}
// Removes batch deletes values of the tree by <keys>.
func (tree *AVLTree) Removes(keys []interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for _, key := range keys {
tree.remove(key, &tree.root)
}
}
// IsEmpty returns true if tree does not contain any nodes.
func (tree *AVLTree) IsEmpty() bool {
return tree.Size() == 0
}
// Size returns number of nodes in the tree.
func (tree *AVLTree) Size() int {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.size
}
// Keys returns all keys in asc order.
func (tree *AVLTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
index++
return true
})
return keys
}
// Values returns all values in asc order based on the key.
func (tree *AVLTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
return true
})
return values
}
// Left returns the minimum element of the AVL tree
// or nil if the tree is empty.
func (tree *AVLTree) Left() *AVLTreeNode {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(0)
if tree.mu.IsSafe() {
return &AVLTreeNode {
Key : node.Key,
Value : node.Value,
}
}
return node
}
// Right returns the maximum element of the AVL tree
// or nil if the tree is empty.
func (tree *AVLTree) Right() *AVLTreeNode {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(1)
if tree.mu.IsSafe() {
return &AVLTreeNode {
Key : node.Key,
Value : node.Value,
}
}
return node
}
// Floor Finds floor node of the input key, return the floor node or nil if no ceiling is found.
// Second return parameter is true if floor was found, otherwise false.
//
// Floor node is defined as the largest node that is smaller than or equal to the given node.
// A floor node may not be found, either because the tree is empty, or because
// all nodes in the tree is larger than the given node.
//
// Key should adhere to the comparator's type assertion, otherwise method panics.
func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found = false
n := tree.root
for n != nil {
c := tree.comparator(key, n.Key)
switch {
case c == 0: return n, true
case c < 0: n = n.children[0]
case c > 0:
floor, found = n, true
n = n.children[1]
}
}
if found {
return
}
return nil, false
}
// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling is found.
// Second return parameter is true if ceiling was found, otherwise false.
//
// Ceiling node is defined as the smallest node that is larger than or equal to the given node.
// A ceiling node may not be found, either because the tree is empty, or because
// all nodes in the tree is smaller than the given node.
//
// Key should adhere to the comparator's type assertion, otherwise method panics.
func (tree *AVLTree) Ceiling(key interface{}) (floor *AVLTreeNode, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found = false
n := tree.root
for n != nil {
c := tree.comparator(key, n.Key)
switch {
case c == 0: return n, true
case c > 0: n = n.children[1]
case c < 0:
floor, found = n, true
n = n.children[0]
}
}
if found {
return
}
return nil, false
}
// Clear removes all nodes from the tree.
func (tree *AVLTree) Clear() {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
}
// String returns a string representation of container
func (tree *AVLTree) String() string {
tree.mu.RLock()
defer tree.mu.RUnlock()
str := "AVLTree\n"
if tree.size != 0 {
output(tree.root, "", true, &str)
}
return str
}
// Print prints the tree to stdout.
func (tree *AVLTree) Print() {
fmt.Println(tree.String())
}
// Map returns all key-value items as map.
func (tree *AVLTree) Map() map[interface{}]interface{} {
m := make(map[interface{}]interface{}, tree.Size())
tree.IteratorAsc(func(key, value interface{}) bool {
m[key] = value
return true
})
return m
}
// Flip exchanges key-value of the tree to value-key.
// Note that you should guarantee the value is the same type as key,
// or else the comparator would panic.
//
// If the type of value is different with key, you pass the new <comparator>.
func (tree *AVLTree) Flip(comparator...func(v1, v2 interface{}) int) {
t := (*AVLTree)(nil)
if len(comparator) > 0 {
t = NewAVLTree(comparator[0], !tree.mu.IsSafe())
} else {
t = NewAVLTree(tree.comparator, !tree.mu.IsSafe())
}
tree.IteratorAsc(func(key, value interface{}) bool {
t.put(value, key, nil, &t.root)
return true
})
tree.mu.Lock()
tree.root = t.root
tree.size = t.size
tree.mu.Unlock()
}
// Iterator is alias of IteratorAsc.
func (tree *AVLTree) Iterator(f func (key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorAsc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(0)
for node != nil {
if !f(node.Key, node.Value) {
return
}
node = node.Next()
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorDesc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(1)
for node != nil {
if !f(node.Key, node.Value) {
return
}
node = node.Prev()
}
}
func (tree *AVLTree) put(key interface{}, value interface{}, p *AVLTreeNode, qp **AVLTreeNode) bool {
q := *qp
if q == nil {
tree.size++
*qp = &AVLTreeNode{Key: key, Value: value, parent: p}
return true
}
c := tree.comparator(key, q.Key)
if c == 0 {
q.Key = key
q.Value = value
return false
}
if c < 0 {
c = -1
} else {
c = 1
}
a := (c + 1) / 2
if tree.put(key, value, q, &q.children[a]) {
return putFix(int8(c), qp)
}
return false
}
func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{}, fix bool) {
q := *qp
if q == nil {
return nil, false
}
c := tree.comparator(key, q.Key)
if c == 0 {
tree.size--
value = q.Value
fix = true
if q.children[1] == nil {
if q.children[0] != nil {
q.children[0].parent = q.parent
}
*qp = q.children[0]
return
}
if removeMin(&q.children[1], &q.Key, &q.Value) {
return value, removeFix(-1, qp)
}
return
}
if c < 0 {
c = -1
} else {
c = 1
}
a := (c + 1) / 2
value, fix = tree.remove(key, &q.children[a])
if fix {
return value, removeFix(int8(-c), qp)
}
return nil, false
}
func removeMin(qp **AVLTreeNode, minKey *interface{}, minVal *interface{}) bool {
q := *qp
if q.children[0] == nil {
*minKey = q.Key
*minVal = q.Value
if q.children[1] != nil {
q.children[1].parent = q.parent
}
*qp = q.children[1]
return true
}
fix := removeMin(&q.children[0], minKey, minVal)
if fix {
return removeFix(1, qp)
}
return false
}
func putFix(c int8, t **AVLTreeNode) bool {
s := *t
if s.b == 0 {
s.b = c
return true
}
if s.b == -c {
s.b = 0
return false
}
if s.children[(c+1)/2].b == c {
s = singleRotate(c, s)
} else {
s = doubleRotate(c, s)
}
*t = s
return false
}
func removeFix(c int8, t **AVLTreeNode) bool {
s := *t
if s.b == 0 {
s.b = c
return false
}
if s.b == -c {
s.b = 0
return true
}
a := (c + 1) / 2
if s.children[a].b == 0 {
s = rotate(c, s)
s.b = -c
*t = s
return false
}
if s.children[a].b == c {
s = singleRotate(c, s)
} else {
s = doubleRotate(c, s)
}
*t = s
return true
}
func singleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
s.b = 0
s = rotate(c, s)
s.b = 0
return s
}
func doubleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
a := (c + 1) / 2
r := s.children[a]
s.children[a] = rotate(-c, s.children[a])
p := rotate(c, s)
switch {
default:
s.b = 0
r.b = 0
case p.b == c:
s.b = -c
r.b = 0
case p.b == -c:
s.b = 0
r.b = c
}
p.b = 0
return p
}
func rotate(c int8, s *AVLTreeNode) *AVLTreeNode {
a := (c + 1) / 2
r := s.children[a]
s.children[a] = r.children[a^1]
if s.children[a] != nil {
s.children[a].parent = s
}
r.children[a^1] = s
r.parent = s.parent
s.parent = r
return r
}
func (tree *AVLTree) bottom(d int) *AVLTreeNode {
n := tree.root
if n == nil {
return nil
}
for c := n.children[d]; c != nil; c = n.children[d] {
n = c
}
return n
}
// Prev returns the previous element in an inorder
// walk of the AVL tree.
func (node *AVLTreeNode) Prev() *AVLTreeNode {
return node.walk1(0)
}
// Next returns the next element in an inorder
// walk of the AVL tree.
func (node *AVLTreeNode) Next() *AVLTreeNode {
return node.walk1(1)
}
func (node *AVLTreeNode) walk1(a int) *AVLTreeNode {
if node == nil {
return nil
}
n := node
if n.children[a] != nil {
n = n.children[a]
for n.children[a^1] != nil {
n = n.children[a^1]
}
return n
}
p := n.parent
for p != nil && p.children[a] == n {
n = p
p = p.parent
}
return p
}
func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
if node.children[1] != nil {
newPrefix := prefix
if isTail {
newPrefix += "│ "
} else {
newPrefix += " "
}
output(node.children[1], newPrefix, false, str)
}
*str += prefix
if isTail {
*str += "└── "
} else {
*str += "┌── "
}
*str += fmt.Sprintf("%v\n", node.Key)
if node.children[0] != nil {
newPrefix := prefix
if isTail {
newPrefix += " "
} else {
newPrefix += "│ "
}
output(node.children[0], newPrefix, true, str)
}
}

View File

@ -0,0 +1,844 @@
// Copyright 2019 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 gtree
import (
"bytes"
"fmt"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
"strings"
)
// BTree holds elements of the B-tree.
type BTree struct {
mu *rwmutex.RWMutex
root *BTreeNode
comparator func(v1, v2 interface{}) int
size int // Total number of keys in the tree
m int // order (maximum number of children)
}
// BTreeNode is a single element within the tree.
type BTreeNode struct {
Parent *BTreeNode
Entries []*BTreeEntry // Contained keys in node
Children []*BTreeNode // Children nodes
}
// BTreeEntry represents the key-value pair contained within nodes.
type BTreeEntry struct {
Key interface{}
Value interface{}
}
// NewBTree instantiates a B-tree with <m> (maximum number of children) and a custom key comparator.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
// Note that the <m> must be greater or equal than 3, or else it panics.
func NewBTree(m int, comparator func(v1, v2 interface{}) int, unsafe...bool) *BTree {
if m < 3 {
panic("Invalid order, should be at least 3")
}
return &BTree{
comparator : comparator,
mu : rwmutex.New(unsafe...),
m : m,
}
}
// NewBTreeFrom instantiates a B-tree with <m> (maximum number of children), a custom key comparator and data map.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *BTree {
tree := NewBTree(m, comparator, unsafe...)
for k, v := range data {
tree.doSet(k, v)
}
return tree
}
// Clone returns a new tree with a copy of current tree.
func (tree *BTree) Clone(unsafe ...bool) *BTree {
newTree := NewBTree(tree.m, tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
}
// Set inserts key-value item into the tree.
func (tree *BTree) Set(key interface{}, value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.doSet(key, value)
}
// doSet inserts key-value pair node into the tree.
// If key already exists, then its value is updated with the new value.
func (tree *BTree) doSet(key interface{}, value interface{}) {
entry := &BTreeEntry{Key: key, Value: value}
if tree.root == nil {
tree.root = &BTreeNode{Entries: []*BTreeEntry{entry}, Children: []*BTreeNode{}}
tree.size++
return
}
if tree.insert(tree.root, entry) {
tree.size++
}
}
// Sets batch sets key-values to the tree.
func (tree *BTree) Sets(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for k, v := range data {
tree.doSet(k, v)
}
}
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
func (tree *BTree) Get(key interface{}) (value interface{}) {
value, _ = tree.Search(key)
return
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
tree.mu.Lock()
defer tree.mu.Unlock()
if entry := tree.doSearch(key); entry != nil {
return entry.Value
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
tree.doSet(key, value)
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
func (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (tree *BTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (tree *BTree) GetVar(key interface{}) *gvar.Var {
return gvar.New(tree.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (tree *BTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(tree.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (tree *BTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (tree *BTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (tree *BTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f)
return true
}
return false
}
// Contains checks whether <key> exists in the tree.
func (tree *BTree) Contains(key interface{}) bool {
_, ok := tree.Search(key)
return ok
}
// Remove remove the node from the tree by key.
// Key should adhere to the comparator's type assertion, otherwise method panics.
func (tree *BTree) doRemove(key interface{}) (value interface{}) {
node, index, found := tree.searchRecursively(tree.root, key)
if found {
value = node.Entries[index].Value
tree.delete(node, index)
tree.size--
}
return
}
// Remove removes the node from the tree by <key>.
func (tree *BTree) Remove(key interface{}) (value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
return tree.doRemove(key)
}
// Removes batch deletes values of the tree by <keys>.
func (tree *BTree) Removes(keys []interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for _, key := range keys {
tree.doRemove(key)
}
}
// Empty returns true if tree does not contain any nodes
func (tree *BTree) IsEmpty() bool {
return tree.Size() == 0
}
// Size returns number of nodes in the tree.
func (tree *BTree) Size() int {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.size
}
// Keys returns all keys in asc order.
func (tree *BTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
index++
return true
})
return keys
}
// Values returns all values in asc order based on the key.
func (tree *BTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
return true
})
return values
}
// Map returns all key-value items as map.
func (tree *BTree) Map() map[interface{}]interface{} {
m := make(map[interface{}]interface{}, tree.Size())
tree.IteratorAsc(func(key, value interface{}) bool {
m[key] = value
return true
})
return m
}
// Clear removes all nodes from the tree.
func (tree *BTree) Clear() {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
}
// Height returns the height of the tree.
func (tree *BTree) Height() int {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.root.height()
}
// Left returns the left-most (min) entry or nil if tree is empty.
func (tree *BTree) Left() *BTreeEntry {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.left(tree.root)
return node.Entries[0]
}
// Right returns the right-most (max) entry or nil if tree is empty.
func (tree *BTree) Right() *BTreeEntry {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.right(tree.root)
return node.Entries[len(node.Entries) - 1]
}
// String returns a string representation of container (for debugging purposes)
func (tree *BTree) String() string {
tree.mu.RLock()
defer tree.mu.RUnlock()
var buffer bytes.Buffer
if _, err := buffer.WriteString("BTree\n"); err != nil {
}
if tree.size != 0 {
tree.output(&buffer, tree.root, 0, true)
}
return buffer.String()
}
// Search searches the tree with given <key>.
// Second return parameter <found> is true if key was found, otherwise false.
func (tree *BTree) Search(key interface{}) (value interface{}, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node, index, found := tree.searchRecursively(tree.root, key)
if found {
return node.Entries[index].Value, true
}
return nil, false
}
// Search searches the tree with given <key> without mutex.
// It returns the entry if found or otherwise nil.
func (tree *BTree) doSearch(key interface{}) *BTreeEntry {
node, index, found := tree.searchRecursively(tree.root, key)
if found {
return node.Entries[index]
}
return nil
}
// Print prints the tree to stdout.
func (tree *BTree) Print() {
fmt.Println(tree.String())
}
// Iterator is alias of IteratorAsc.
func (tree *BTree) Iterator(f func (key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorAsc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.left(tree.root)
if node == nil {
return
}
entry := node.Entries[0]
loop:
if entry == nil {
return
}
if !f(entry.Key, entry.Value) {
return
}
// Find current entry position in current node
e, _ := tree.search(node, entry.Key)
// Try to go down to the child right of the current entry
if e + 1 < len(node.Children) {
node = node.Children[e + 1]
// Try to go down to the child left of the current node
for len(node.Children) > 0 {
node = node.Children[0]
}
// Return the left-most entry
entry = node.Entries[0]
goto loop
}
// Above assures that we have reached a leaf node, so return the next entry in current node (if any)
if e + 1 < len(node.Entries) {
entry = node.Entries[e + 1]
goto loop
}
// Reached leaf node and there are no entries to the right of the current entry, so go up to the parent
for node.Parent != nil {
node = node.Parent
// Find next entry position in current node (note: search returns the first equal or bigger than entry)
e, _ := tree.search(node, entry.Key)
// Check that there is a next entry position in current node
if e < len(node.Entries) {
entry = node.Entries[e]
goto loop
}
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorDesc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.right(tree.root)
if node == nil {
return
}
entry := node.Entries[len(node.Entries) - 1]
loop:
if entry == nil {
return
}
if !f(entry.Key, entry.Value) {
return
}
// Find current entry position in current node
e, _ := tree.search(node, entry.Key)
// Try to go down to the child left of the current entry
if e < len(node.Children) {
node = node.Children[e]
// Try to go down to the child right of the current node
for len(node.Children) > 0 {
node = node.Children[len(node.Children) - 1]
}
// Return the right-most entry
entry = node.Entries[len(node.Entries) - 1]
goto loop
}
// Above assures that we have reached a leaf node, so return the previous entry in current node (if any)
if e - 1 >= 0 {
entry = node.Entries[e - 1]
goto loop
}
// Reached leaf node and there are no entries to the left of the current entry, so go up to the parent
for node.Parent != nil {
node = node.Parent
// Find previous entry position in current node (note: search returns the first equal or bigger than entry)
e, _ := tree.search(node, entry.Key)
// Check that there is a previous entry position in current node
if e - 1 >= 0 {
entry = node.Entries[e - 1]
goto loop
}
}
}
func (tree *BTree) output(buffer *bytes.Buffer, node *BTreeNode, level int, isTail bool) {
for e := 0; e < len(node.Entries)+1; e++ {
if e < len(node.Children) {
tree.output(buffer, node.Children[e], level+1, true)
}
if e < len(node.Entries) {
if _, err := buffer.WriteString(strings.Repeat(" ", level)); err != nil {
}
if _, err := buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n"); err != nil {
}
}
}
}
func (node *BTreeNode) height() int {
h := 0
n := node
for ; n != nil; n = n.Children[0] {
h++
if len(n.Children) == 0 {
break
}
}
return h
}
func (tree *BTree) isLeaf(node *BTreeNode) bool {
return len(node.Children) == 0
}
func (tree *BTree) isFull(node *BTreeNode) bool {
return len(node.Entries) == tree.maxEntries()
}
func (tree *BTree) shouldSplit(node *BTreeNode) bool {
return len(node.Entries) > tree.maxEntries()
}
func (tree *BTree) maxChildren() int {
return tree.m
}
func (tree *BTree) minChildren() int {
return (tree.m + 1) / 2 // ceil(m/2)
}
func (tree *BTree) maxEntries() int {
return tree.maxChildren() - 1
}
func (tree *BTree) minEntries() int {
return tree.minChildren() - 1
}
func (tree *BTree) middle() int {
// "-1" to favor right nodes to have more keys when splitting
return (tree.m - 1) / 2
}
// search searches only within the single node among its entries
func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bool) {
low, mid, high := 0, 0, len(node.Entries) - 1
for low <= high {
mid = (high + low) / 2
compare := tree.comparator(key, node.Entries[mid].Key)
switch {
case compare > 0: low = mid + 1
case compare < 0: high = mid - 1
case compare == 0: return mid, true
}
}
return low, false
}
// searchRecursively searches recursively down the tree starting at the startNode
func (tree *BTree) searchRecursively(startNode *BTreeNode, key interface{}) (node *BTreeNode, index int, found bool) {
if tree.size == 0 {
return nil, -1, false
}
node = startNode
for {
index, found = tree.search(node, key)
if found {
return node, index, true
}
if tree.isLeaf(node) {
return nil, -1, false
}
node = node.Children[index]
}
}
func (tree *BTree) insert(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
if tree.isLeaf(node) {
return tree.insertIntoLeaf(node, entry)
}
return tree.insertIntoInternal(node, entry)
}
func (tree *BTree) insertIntoLeaf(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
insertPosition, found := tree.search(node, entry.Key)
if found {
node.Entries[insertPosition] = entry
return false
}
// Insert entry's key in the middle of the node
node.Entries = append(node.Entries, nil)
copy(node.Entries[insertPosition+1:], node.Entries[insertPosition:])
node.Entries[insertPosition] = entry
tree.split(node)
return true
}
func (tree *BTree) insertIntoInternal(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
insertPosition, found := tree.search(node, entry.Key)
if found {
node.Entries[insertPosition] = entry
return false
}
return tree.insert(node.Children[insertPosition], entry)
}
func (tree *BTree) split(node *BTreeNode) {
if !tree.shouldSplit(node) {
return
}
if node == tree.root {
tree.splitRoot()
return
}
tree.splitNonRoot(node)
}
func (tree *BTree) splitNonRoot(node *BTreeNode) {
middle := tree.middle()
parent := node.Parent
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[:middle]...), Parent: parent}
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[middle+1:]...), Parent: parent}
// Move children from the node to be split into left and right nodes
if !tree.isLeaf(node) {
left.Children = append([]*BTreeNode(nil), node.Children[:middle+1]...)
right.Children = append([]*BTreeNode(nil), node.Children[middle+1:]...)
setParent(left.Children, left)
setParent(right.Children, right)
}
insertPosition, _ := tree.search(parent, node.Entries[middle].Key)
// Insert middle key into parent
parent.Entries = append(parent.Entries, nil)
copy(parent.Entries[insertPosition+1:], parent.Entries[insertPosition:])
parent.Entries[insertPosition] = node.Entries[middle]
// Set child left of inserted key in parent to the created left node
parent.Children[insertPosition] = left
// Set child right of inserted key in parent to the created right node
parent.Children = append(parent.Children, nil)
copy(parent.Children[insertPosition+2:], parent.Children[insertPosition+1:])
parent.Children[insertPosition+1] = right
tree.split(parent)
}
func (tree *BTree) splitRoot() {
middle := tree.middle()
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[:middle]...)}
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[middle+1:]...)}
// Move children from the node to be split into left and right nodes
if !tree.isLeaf(tree.root) {
left.Children = append([]*BTreeNode(nil), tree.root.Children[:middle+1]...)
right.Children = append([]*BTreeNode(nil), tree.root.Children[middle+1:]...)
setParent(left.Children, left)
setParent(right.Children, right)
}
// Root is a node with one entry and two children (left and right)
newRoot := &BTreeNode{
Entries: []*BTreeEntry{tree.root.Entries[middle]},
Children: []*BTreeNode{left, right},
}
left.Parent = newRoot
right.Parent = newRoot
tree.root = newRoot
}
func setParent(nodes []*BTreeNode, parent *BTreeNode) {
for _, node := range nodes {
node.Parent = parent
}
}
func (tree *BTree) left(node *BTreeNode) *BTreeNode {
if tree.size == 0 {
return nil
}
current := node
for {
if tree.isLeaf(current) {
return current
}
current = current.Children[0]
}
}
func (tree *BTree) right(node *BTreeNode) *BTreeNode {
if tree.size == 0 {
return nil
}
current := node
for {
if tree.isLeaf(current) {
return current
}
current = current.Children[len(current.Children)-1]
}
}
// leftSibling returns the node's left sibling and child index (in parent) if it exists, otherwise (nil,-1)
// key is any of keys in node (could even be deleted).
func (tree *BTree) leftSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
if node.Parent != nil {
index, _ := tree.search(node.Parent, key)
index--
if index >= 0 && index < len(node.Parent.Children) {
return node.Parent.Children[index], index
}
}
return nil, -1
}
// rightSibling returns the node's right sibling and child index (in parent) if it exists, otherwise (nil,-1)
// key is any of keys in node (could even be deleted).
func (tree *BTree) rightSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
if node.Parent != nil {
index, _ := tree.search(node.Parent, key)
index++
if index < len(node.Parent.Children) {
return node.Parent.Children[index], index
}
}
return nil, -1
}
// delete deletes an entry in node at entries' index
// ref.: https://en.wikipedia.org/wiki/B-tree#Deletion
func (tree *BTree) delete(node *BTreeNode, index int) {
// deleting from a leaf node
if tree.isLeaf(node) {
deletedKey := node.Entries[index].Key
tree.deleteEntry(node, index)
tree.rebalance(node, deletedKey)
if len(tree.root.Entries) == 0 {
tree.root = nil
}
return
}
// deleting from an internal node
leftLargestNode := tree.right(node.Children[index]) // largest node in the left sub-tree (assumed to exist)
leftLargestEntryIndex := len(leftLargestNode.Entries) - 1
node.Entries[index] = leftLargestNode.Entries[leftLargestEntryIndex]
deletedKey := leftLargestNode.Entries[leftLargestEntryIndex].Key
tree.deleteEntry(leftLargestNode, leftLargestEntryIndex)
tree.rebalance(leftLargestNode, deletedKey)
}
// rebalance rebalances the tree after deletion if necessary and returns true, otherwise false.
// Note that we first delete the entry and then call rebalance, thus the passed deleted key as reference.
func (tree *BTree) rebalance(node *BTreeNode, deletedKey interface{}) {
// check if rebalancing is needed
if node == nil || len(node.Entries) >= tree.minEntries() {
return
}
// try to borrow from left sibling
leftSibling, leftSiblingIndex := tree.leftSibling(node, deletedKey)
if leftSibling != nil && len(leftSibling.Entries) > tree.minEntries() {
// rotate right
node.Entries = append([]*BTreeEntry{node.Parent.Entries[leftSiblingIndex]}, node.Entries...) // prepend parent's separator entry to node's entries
node.Parent.Entries[leftSiblingIndex] = leftSibling.Entries[len(leftSibling.Entries)-1]
tree.deleteEntry(leftSibling, len(leftSibling.Entries)-1)
if !tree.isLeaf(leftSibling) {
leftSiblingRightMostChild := leftSibling.Children[len(leftSibling.Children)-1]
leftSiblingRightMostChild.Parent = node
node.Children = append([]*BTreeNode{leftSiblingRightMostChild}, node.Children...)
tree.deleteChild(leftSibling, len(leftSibling.Children)-1)
}
return
}
// try to borrow from right sibling
rightSibling, rightSiblingIndex := tree.rightSibling(node, deletedKey)
if rightSibling != nil && len(rightSibling.Entries) > tree.minEntries() {
// rotate left
node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1]) // append parent's separator entry to node's entries
node.Parent.Entries[rightSiblingIndex-1] = rightSibling.Entries[0]
tree.deleteEntry(rightSibling, 0)
if !tree.isLeaf(rightSibling) {
rightSiblingLeftMostChild := rightSibling.Children[0]
rightSiblingLeftMostChild.Parent = node
node.Children = append(node.Children, rightSiblingLeftMostChild)
tree.deleteChild(rightSibling, 0)
}
return
}
// merge with siblings
if rightSibling != nil {
// merge with right sibling
node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1])
node.Entries = append(node.Entries, rightSibling.Entries...)
deletedKey = node.Parent.Entries[rightSiblingIndex-1].Key
tree.deleteEntry(node.Parent, rightSiblingIndex-1)
tree.appendChildren(node.Parent.Children[rightSiblingIndex], node)
tree.deleteChild(node.Parent, rightSiblingIndex)
} else if leftSibling != nil {
// merge with left sibling
entries := append([]*BTreeEntry(nil), leftSibling.Entries...)
entries = append(entries, node.Parent.Entries[leftSiblingIndex])
node.Entries = append(entries, node.Entries...)
deletedKey = node.Parent.Entries[leftSiblingIndex].Key
tree.deleteEntry(node.Parent, leftSiblingIndex)
tree.prependChildren(node.Parent.Children[leftSiblingIndex], node)
tree.deleteChild(node.Parent, leftSiblingIndex)
}
// make the merged node the root if its parent was the root and the root is empty
if node.Parent == tree.root && len(tree.root.Entries) == 0 {
tree.root = node
node.Parent = nil
return
}
// parent might underflow, so try to rebalance if necessary
tree.rebalance(node.Parent, deletedKey)
}
func (tree *BTree) prependChildren(fromNode *BTreeNode, toNode *BTreeNode) {
children := append([]*BTreeNode(nil), fromNode.Children...)
toNode.Children = append(children, toNode.Children...)
setParent(fromNode.Children, toNode)
}
func (tree *BTree) appendChildren(fromNode *BTreeNode, toNode *BTreeNode) {
toNode.Children = append(toNode.Children, fromNode.Children...)
setParent(fromNode.Children, toNode)
}
func (tree *BTree) deleteEntry(node *BTreeNode, index int) {
copy(node.Entries[index:], node.Entries[index+1:])
node.Entries[len(node.Entries)-1] = nil
node.Entries = node.Entries[:len(node.Entries)-1]
}
func (tree *BTree) deleteChild(node *BTreeNode, index int) {
if index >= len(node.Children) {
return
}
copy(node.Children[index:], node.Children[index+1:])
node.Children[len(node.Children)-1] = nil
node.Children = node.Children[:len(node.Children)-1]
}

View File

@ -0,0 +1,832 @@
// Copyright 2019 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 gtree
import (
"fmt"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
)
type color bool
const (
black, red color = true, false
)
// RedBlackTree holds elements of the red-black tree.
type RedBlackTree struct {
mu *rwmutex.RWMutex
root *RedBlackTreeNode
size int
comparator func(v1, v2 interface{}) int
}
// RedBlackTreeNode is a single element within the tree.
type RedBlackTreeNode struct {
Key interface{}
Value interface{}
color color
left *RedBlackTreeNode
right *RedBlackTreeNode
parent *RedBlackTreeNode
}
// NewRedBlackTree instantiates a red-black tree with the custom comparator.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *RedBlackTree {
return &RedBlackTree {
mu : rwmutex.New(unsafe...),
comparator: comparator,
}
}
// NewRedBlackTreeFrom instantiates a red-black tree with the custom comparator and <data> map.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *RedBlackTree {
tree := NewRedBlackTree(comparator, unsafe...)
for k, v := range data {
tree.doSet(k, v)
}
return tree
}
// Clone returns a new tree with a copy of current tree.
func (tree *RedBlackTree) Clone(unsafe ...bool) *RedBlackTree {
newTree := NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
}
// Set inserts key-value item into the tree.
func (tree *RedBlackTree) Set(key interface{}, value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.doSet(key, value)
}
// Sets batch sets key-values to the tree.
func (tree *RedBlackTree) Sets(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for k, v := range data {
tree.doSet(k, v)
}
}
// doSet inserts key-value item into the tree without mutex.
func (tree *RedBlackTree) doSet(key interface{}, value interface{}) {
insertedNode := (*RedBlackTreeNode)(nil)
if tree.root == nil {
// Assert key is of comparator's type for initial tree
tree.comparator(key, key)
tree.root = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = tree.root
} else {
node := tree.root
loop := true
for loop {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0:
//node.Key = key
node.Value = value
return
case compare < 0:
if node.left == nil {
node.left = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.left
loop = false
} else {
node = node.left
}
case compare > 0:
if node.right == nil {
node.right = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.right
loop = false
} else {
node = node.right
}
}
}
insertedNode.parent = node
}
tree.insertCase1(insertedNode)
tree.size++
}
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
func (tree *RedBlackTree) Get(key interface{}) (value interface{}) {
value, _ = tree.Search(key)
return
}
// 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.
//
// When setting value, if <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the hash map,
// and its return value will be set to the map with <key>.
//
// It returns value with given <key>.
func (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
tree.mu.Lock()
defer tree.mu.Unlock()
if node := tree.doSearch(key); node != nil {
return node.Value
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
tree.doSet(key, value)
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
func (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if 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 (tree *RedBlackTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
func (tree *RedBlackTree) GetVar(key interface{}) *gvar.Var {
return gvar.New(tree.Get(key), true)
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
func (tree *RedBlackTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(tree.GetOrSet(key, value), true)
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
func (tree *RedBlackTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFunc(key, f), true)
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
func (tree *RedBlackTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return 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 (tree *RedBlackTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
tree.doSetWithLockCheck(key, f)
return true
}
return false
}
// Contains checks whether <key> exists in the tree.
func (tree *RedBlackTree) Contains(key interface{}) bool {
_, ok := tree.Search(key)
return ok
}
// doRemove removes the node from the tree by <key> without mutex.
func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) {
child := (*RedBlackTreeNode)(nil)
node := tree.doSearch(key)
if node == nil {
return
}
value = node.Value
if node.left != nil && node.right != nil {
p := node.left.maximumNode()
node.Key = p.Key
node.Value = p.Value
node = p
}
if node.left == nil || node.right == nil {
if node.right == nil {
child = node.left
} else {
child = node.right
}
if node.color == black {
node.color = tree.nodeColor(child)
tree.deleteCase1(node)
}
tree.replaceNode(node, child)
if node.parent == nil && child != nil {
child.color = black
}
}
tree.size--
return
}
// Remove removes the node from the tree by <key>.
func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
return tree.doRemove(key)
}
// Removes batch deletes values of the tree by <keys>.
func (tree *RedBlackTree) Removes(keys []interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
for _, key := range keys {
tree.doRemove(key)
}
}
// IsEmpty returns true if tree does not contain any nodes.
func (tree *RedBlackTree) IsEmpty() bool {
return tree.Size() == 0
}
// Size returns number of nodes in the tree.
func (tree *RedBlackTree) Size() int {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.size
}
// Keys returns all keys in asc order.
func (tree *RedBlackTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
index++
return true
})
return keys
}
// Values returns all values in asc order based on the key.
func (tree *RedBlackTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
return true
})
return values
}
// Map returns all key-value items as map.
func (tree *RedBlackTree) Map() map[interface{}]interface{} {
m := make(map[interface{}]interface{}, tree.Size())
tree.IteratorAsc(func(key, value interface{}) bool {
m[key] = value
return true
})
return m
}
// Left returns the left-most (min) node or nil if tree is empty.
func (tree *RedBlackTree) Left() *RedBlackTreeNode {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.leftNode()
if tree.mu.IsSafe() {
return &RedBlackTreeNode{
Key : node.Key,
Value : node.Value,
}
}
return node
}
// Right returns the right-most (max) node or nil if tree is empty.
func (tree *RedBlackTree) Right() *RedBlackTreeNode {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.rightNode()
if tree.mu.IsSafe() {
return &RedBlackTreeNode{
Key : node.Key,
Value : node.Value,
}
}
return node
}
// leftNode returns the left-most (min) node or nil if tree is empty.
func (tree *RedBlackTree) leftNode() *RedBlackTreeNode {
p := (*RedBlackTreeNode)(nil)
n := tree.root
for n != nil {
p = n
n = n.left
}
return p
}
// rightNode returns the right-most (max) node or nil if tree is empty.
func (tree *RedBlackTree) rightNode() *RedBlackTreeNode {
p := (*RedBlackTreeNode)(nil)
n := tree.root
for n != nil {
p = n
n = n.right
}
return p
}
// Floor Finds floor node of the input <key>, return the floor node or nil if no floor is found.
//
// Floor node is defined as the largest node that its key is smaller than or equal to the given <key>.
// A floor node may not be found, either because the tree is empty, or because
// all nodes in the tree are larger than the given node.
func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found := false
node := tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0:
return node
case compare < 0:
node = node.left
case compare > 0:
floor, found = node, true
node = node.right
}
}
if found {
return floor
}
return nil
}
// Ceiling finds ceiling node of the input <key>, return the ceiling node or nil if no ceiling is found.
//
// Ceiling node is defined as the smallest node that its key is larger than or equal to the given <key>.
// A ceiling node may not be found, either because the tree is empty, or because
// all nodes in the tree are smaller than the given node.
func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found := false
node := tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0:
return node
case compare < 0:
ceiling, found = node, true
node = node.left
case compare > 0:
node = node.right
}
}
if found {
return ceiling
}
return nil
}
// Iterator is alias of IteratorAsc.
func (tree *RedBlackTree) Iterator(f func (key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.leftNode()
if node == nil {
return
}
loop:
if node == nil {
return
}
if !f(node.Key, node.Value) {
return
}
if node.right != nil {
node = node.right
for node.left != nil {
node = node.left
}
goto loop
}
if node.parent != nil {
old := node
for node.parent != nil {
node = node.parent
if tree.comparator(old.Key, node.Key) <= 0 {
goto loop
}
}
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.rightNode()
if node == nil {
return
}
loop:
if node == nil {
return
}
if !f(node.Key, node.Value) {
return
}
if node.left != nil {
node = node.left
for node.right != nil {
node = node.right
}
goto loop
}
if node.parent != nil {
old := node
for node.parent != nil {
node = node.parent
if tree.comparator(old.Key, node.Key) >= 0 {
goto loop
}
}
}
}
// Clear removes all nodes from the tree.
func (tree *RedBlackTree) Clear() {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
}
// String returns a string representation of container.
func (tree *RedBlackTree) String() string {
tree.mu.RLock()
defer tree.mu.RUnlock()
str := "RedBlackTree\n"
if tree.size != 0 {
tree.output(tree.root, "", true, &str)
}
return str
}
// Print prints the tree to stdout.
func (tree *RedBlackTree) Print() {
fmt.Println(tree.String())
}
// Search searches the tree with given <key>.
// Second return parameter <found> is true if key was found, otherwise false.
func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.doSearch(key)
if node != nil {
return node.Value, true
}
return nil, false
}
// Flip exchanges key-value of the tree to value-key.
// Note that you should guarantee the value is the same type as key,
// or else the comparator would panic.
//
// If the type of value is different with key, you pass the new <comparator>.
func (tree *RedBlackTree) Flip(comparator...func(v1, v2 interface{}) int) {
t := (*RedBlackTree)(nil)
if len(comparator) > 0 {
t = NewRedBlackTree(comparator[0], !tree.mu.IsSafe())
} else {
t = NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
}
tree.IteratorAsc(func(key, value interface{}) bool {
t.doSet(value, key)
return true
})
tree.mu.Lock()
tree.root = t.root
tree.size = t.size
tree.mu.Unlock()
}
func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail bool, str *string) {
if node.right != nil {
newPrefix := prefix
if isTail {
newPrefix += "│ "
} else {
newPrefix += " "
}
tree.output(node.right, newPrefix, false, str)
}
*str += prefix
if isTail {
*str += "└── "
} else {
*str += "┌── "
}
*str += fmt.Sprintf("%v\n", node.Key)
if node.left != nil {
newPrefix := prefix
if isTail {
newPrefix += " "
} else {
newPrefix += "│ "
}
tree.output(node.left, newPrefix, true, str)
}
}
// doSearch searches the tree with given <key> without mutex.
// It returns the node if found or otherwise nil.
func (tree *RedBlackTree) doSearch(key interface{}) *RedBlackTreeNode {
node := tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0: return node
case compare < 0: node = node.left
case compare > 0: node = node.right
}
}
return nil
}
func (node *RedBlackTreeNode) grandparent() *RedBlackTreeNode {
if node != nil && node.parent != nil {
return node.parent.parent
}
return nil
}
func (node *RedBlackTreeNode) uncle() *RedBlackTreeNode {
if node == nil || node.parent == nil || node.parent.parent == nil {
return nil
}
return node.parent.sibling()
}
func (node *RedBlackTreeNode) sibling() *RedBlackTreeNode {
if node == nil || node.parent == nil {
return nil
}
if node == node.parent.left {
return node.parent.right
}
return node.parent.left
}
func (tree *RedBlackTree) rotateLeft(node *RedBlackTreeNode) {
right := node.right
tree.replaceNode(node, right)
node.right = right.left
if right.left != nil {
right.left.parent = node
}
right.left = node
node.parent = right
}
func (tree *RedBlackTree) rotateRight(node *RedBlackTreeNode) {
left := node.left
tree.replaceNode(node, left)
node.left = left.right
if left.right != nil {
left.right.parent = node
}
left.right = node
node.parent = left
}
func (tree *RedBlackTree) replaceNode(old *RedBlackTreeNode, new *RedBlackTreeNode) {
if old.parent == nil {
tree.root = new
} else {
if old == old.parent.left {
old.parent.left = new
} else {
old.parent.right = new
}
}
if new != nil {
new.parent = old.parent
}
}
func (tree *RedBlackTree) insertCase1(node *RedBlackTreeNode) {
if node.parent == nil {
node.color = black
} else {
tree.insertCase2(node)
}
}
func (tree *RedBlackTree) insertCase2(node *RedBlackTreeNode) {
if tree.nodeColor(node.parent) == black {
return
}
tree.insertCase3(node)
}
func (tree *RedBlackTree) insertCase3(node *RedBlackTreeNode) {
uncle := node.uncle()
if tree.nodeColor(uncle) == red {
node.parent.color = black
uncle.color = black
node.grandparent().color = red
tree.insertCase1(node.grandparent())
} else {
tree.insertCase4(node)
}
}
func (tree *RedBlackTree) insertCase4(node *RedBlackTreeNode) {
grandparent := node.grandparent()
if node == node.parent.right && node.parent == grandparent.left {
tree.rotateLeft(node.parent)
node = node.left
} else if node == node.parent.left && node.parent == grandparent.right {
tree.rotateRight(node.parent)
node = node.right
}
tree.insertCase5(node)
}
func (tree *RedBlackTree) insertCase5(node *RedBlackTreeNode) {
node.parent.color = black
grandparent := node.grandparent()
grandparent.color = red
if node == node.parent.left && node.parent == grandparent.left {
tree.rotateRight(grandparent)
} else if node == node.parent.right && node.parent == grandparent.right {
tree.rotateLeft(grandparent)
}
}
func (node *RedBlackTreeNode) maximumNode() *RedBlackTreeNode {
if node == nil {
return nil
}
for node.right != nil {
return node.right
}
return node
}
func (tree *RedBlackTree) deleteCase1(node *RedBlackTreeNode) {
if node.parent == nil {
return
}
tree.deleteCase2(node)
}
func (tree *RedBlackTree) deleteCase2(node *RedBlackTreeNode) {
sibling := node.sibling()
if tree.nodeColor(sibling) == red {
node.parent.color = red
sibling.color = black
if node == node.parent.left {
tree.rotateLeft(node.parent)
} else {
tree.rotateRight(node.parent)
}
}
tree.deleteCase3(node)
}
func (tree *RedBlackTree) deleteCase3(node *RedBlackTreeNode) {
sibling := node.sibling()
if tree.nodeColor(node.parent) == black &&
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.left) == black &&
tree.nodeColor(sibling.right) == black {
sibling.color = red
tree.deleteCase1(node.parent)
} else {
tree.deleteCase4(node)
}
}
func (tree *RedBlackTree) deleteCase4(node *RedBlackTreeNode) {
sibling := node.sibling()
if tree.nodeColor(node.parent) == red &&
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.left) == black &&
tree.nodeColor(sibling.right) == black {
sibling.color = red
node.parent.color = black
} else {
tree.deleteCase5(node)
}
}
func (tree *RedBlackTree) deleteCase5(node *RedBlackTreeNode) {
sibling := node.sibling()
if node == node.parent.left &&
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.left) == red &&
tree.nodeColor(sibling.right) == black {
sibling.color = red
sibling.left.color = black
tree.rotateRight(sibling)
} else if node == node.parent.right &&
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.right) == red &&
tree.nodeColor(sibling.left) == black {
sibling.color = red
sibling.right.color = black
tree.rotateLeft(sibling)
}
tree.deleteCase6(node)
}
func (tree *RedBlackTree) deleteCase6(node *RedBlackTreeNode) {
sibling := node.sibling()
sibling.color = tree.nodeColor(node.parent)
node.parent.color = black
if node == node.parent.left && tree.nodeColor(sibling.right) == red {
sibling.right.color = black
tree.rotateLeft(node.parent)
} else if tree.nodeColor(sibling.left) == red {
sibling.left.color = black
tree.rotateRight(node.parent)
}
}
func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
if node == nil {
return black
}
return node.color
}

View File

@ -0,0 +1,103 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gtree_test
import (
"github.com/gogf/gf/g/container/gtree"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gutil"
"testing"
)
func Test_AVLTree_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gtree.NewAVLTree(gutil.ComparatorString)
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 := gtree.NewAVLTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_AVLTree_Set_Fun(t *testing.T) {
m := gtree.NewAVLTree(gutil.ComparatorString)
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_AVLTree_Batch(t *testing.T) {
m := gtree.NewAVLTree(gutil.ComparatorString)
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_AVLTree_Iterator(t *testing.T){
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_AVLTree_Clone(t *testing.T) {
//clone 方法是深克隆
m := gtree.NewAVLTreeFrom(gutil.ComparatorString, 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())
}

View File

@ -0,0 +1,99 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gtree_test
import (
"github.com/gogf/gf/g/container/gtree"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gutil"
"testing"
)
func Test_BTree_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gtree.NewBTree(3, gutil.ComparatorString)
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.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gtree.NewBTreeFrom(3, gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_BTree_Set_Fun(t *testing.T) {
m := gtree.NewBTree(3, gutil.ComparatorString)
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_BTree_Batch(t *testing.T) {
m := gtree.NewBTree(3, gutil.ComparatorString)
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_BTree_Iterator(t *testing.T){
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_BTree_Clone(t *testing.T) {
//clone 方法是深克隆
m := gtree.NewBTreeFrom(3, gutil.ComparatorString, 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())
}

View File

@ -0,0 +1,106 @@
// Copyright 2017-2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gtree_test
import (
"github.com/gogf/gf/g/container/gtree"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gutil"
"testing"
)
func getValue() interface{} {
return 3
}
func Test_RedBlackTree_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gtree.NewRedBlackTree(gutil.ComparatorString)
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 := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_RedBlackTree_Set_Fun(t *testing.T) {
m := gtree.NewRedBlackTree(gutil.ComparatorString)
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_RedBlackTree_Batch(t *testing.T) {
m := gtree.NewRedBlackTree(gutil.ComparatorString)
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_RedBlackTree_Iterator(t *testing.T){
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_RedBlackTree_Clone(t *testing.T) {
//clone 方法是深克隆
m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, 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())
}

View File

@ -33,7 +33,7 @@ func (t *Bool) Clone() *Bool {
return NewBool(t.Val())
}
// Set atomically stores value into t.valueue and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Bool) Set(value bool) (old bool) {
if value {
old = atomic.SwapInt32(&t.value, 1) == 1

View File

@ -30,7 +30,7 @@ func (t *Byte) Clone() *Byte {
return NewByte(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Byte) Set(value byte) (old byte) {
return byte(atomic.SwapInt32(&t.value, int32(value)))
}
@ -40,7 +40,7 @@ func (t *Byte) Val() byte {
return byte(atomic.LoadInt32(&t.value))
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Byte) Add(delta int) (new byte) {
return byte(atomic.AddInt32(&t.value, int32(delta)))
}

View File

@ -27,7 +27,7 @@ func (t *Bytes) Clone() *Bytes {
return NewBytes(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> cannot be nil.
func (t *Bytes) Set(value []byte) (old []byte) {
old = t.Val()

View File

@ -32,7 +32,7 @@ func (t *Float32) Clone() *Float32 {
return NewFloat32(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Float32) Set(value float32) (old float32) {
return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value)))
}
@ -42,7 +42,7 @@ func (t *Float32) Val() float32 {
return math.Float32frombits(atomic.LoadUint32(&t.value))
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Float32) Add(delta float32) (new float32) {
for {
old := math.Float32frombits(t.value)

View File

@ -32,7 +32,7 @@ func (t *Float64) Clone() *Float64 {
return NewFloat64(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Float64) Set(value float64) (old float64) {
return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value)))
}
@ -42,7 +42,7 @@ func (t *Float64) Val() float64 {
return math.Float64frombits(atomic.LoadUint64(&t.value))
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Float64) Add(delta float64) (new float64) {
for {
old := math.Float64frombits(t.value)

View File

@ -30,7 +30,7 @@ func (t *Int) Clone() *Int {
return NewInt(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Int) Set(value int) (old int) {
return int(atomic.SwapInt64(&t.value, int64(value)))
}
@ -40,7 +40,7 @@ func (t *Int) Val() int {
return int(atomic.LoadInt64(&t.value))
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Int) Add(delta int) (new int) {
return int(atomic.AddInt64(&t.value, int64(delta)))
}

View File

@ -30,7 +30,7 @@ func (t *Int32) Clone() *Int32 {
return NewInt32(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Int32) Set(value int32) (old int32) {
return atomic.SwapInt32(&t.value, value)
}
@ -40,7 +40,7 @@ func (t *Int32) Val() int32 {
return atomic.LoadInt32(&t.value)
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Int32) Add(delta int32) (new int32) {
return atomic.AddInt32(&t.value, delta)
}

View File

@ -30,7 +30,7 @@ func (t *Int64) Clone() *Int64 {
return NewInt64(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Int64) Set(value int64) (old int64) {
return atomic.SwapInt64(&t.value, value)
}
@ -40,7 +40,7 @@ func (t *Int64) Val() int64 {
return atomic.LoadInt64(&t.value)
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Int64) Add(delta int64) int64 {
return atomic.AddInt64(&t.value, delta)
}

View File

@ -29,7 +29,7 @@ func (t *Interface) Clone() *Interface {
return NewInterface(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> cannot be nil.
func (t *Interface) Set(value interface{}) (old interface{}) {
old = t.Val()

View File

@ -29,7 +29,7 @@ func (t *String) Clone() *String {
return NewString(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *String) Set(value string) (old string) {
old = t.Val()
t.value.Store(value)

View File

@ -30,7 +30,7 @@ func (t *Uint) Clone() *Uint {
return NewUint(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Uint) Set(value uint) (old uint) {
return uint(atomic.SwapUint64(&t.value, uint64(value)))
}
@ -40,7 +40,7 @@ func (t *Uint) Val() uint {
return uint(atomic.LoadUint64(&t.value))
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Uint) Add(delta uint) (new uint) {
return uint(atomic.AddUint64(&t.value, uint64(delta)))
}

View File

@ -30,7 +30,7 @@ func (t *Uint32) Clone() *Uint32 {
return NewUint32(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Uint32) Set(value uint32) (old uint32) {
return atomic.SwapUint32(&t.value, value)
}
@ -40,7 +40,7 @@ func (t *Uint32) Val() uint32 {
return atomic.LoadUint32(&t.value)
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Uint32) Add(delta uint32) (new uint32) {
return atomic.AddUint32(&t.value, delta)
}

View File

@ -30,7 +30,7 @@ func (t *Uint64) Clone() *Uint64 {
return NewUint64(t.Val())
}
// Set atomically stores value into t.value and returns the previous t.value value.
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (t *Uint64) Set(value uint64) (old uint64) {
return atomic.SwapUint64(&t.value, value)
}
@ -40,7 +40,7 @@ func (t *Uint64) Val() uint64 {
return atomic.LoadUint64(&t.value)
}
// Add atomically adds delta to t.value and returns the new value.
// Add atomically adds <delta> to t.value and returns the new value.
func (t *Uint64) Add(delta uint64) (new uint64) {
return atomic.AddUint64(&t.value, delta)
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright 2018-2019 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,
@ -15,11 +15,13 @@ import (
)
type Var struct {
value interface{} // 变量值
safe bool // 当为true时, value为 *gtype.Interface 类型
value interface{} // Underlying value.
safe bool // Concurrent safe or not.
}
// 创建一个动态变量value参数可以为nil
// New returns a new Var with given <value>.
// The param <unsafe> used to specify whether using Var in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func New(value interface{}, unsafe...bool) *Var {
v := &Var{}
if len(unsafe) == 0 || !unsafe[0] {
@ -31,16 +33,7 @@ func New(value interface{}, unsafe...bool) *Var {
return v
}
// 创建一个只读动态变量value参数可以为nil
func NewRead(value interface{}, unsafe...bool) VarRead {
return VarRead(New(value, unsafe...))
}
// 返回动态变量的只读接口
func (v *Var) ReadOnly() VarRead {
return VarRead(v)
}
// Set sets <value> to <v>, and returns the old value.
func (v *Var) Set(value interface{}) (old interface{}) {
if v.safe {
old = v.value.(*gtype.Interface).Set(value)
@ -51,6 +44,7 @@ func (v *Var) Set(value interface{}) (old interface{}) {
return
}
// Val returns the current value of <v>.
func (v *Var) Val() interface{} {
if v.safe {
return v.value.(*gtype.Interface).Val()
@ -59,11 +53,38 @@ func (v *Var) Val() interface{} {
}
}
// Val() 别名
// See Val().
func (v *Var) Interface() interface{} {
return v.Val()
}
// Time converts and returns <v> as time.Time.
// The param <format> specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) Time(format...string) time.Time {
return gconv.Time(v.Val(), format...)
}
// TimeDuration converts and returns <v> as time.Duration.
// If value of <v> is string, then it uses time.ParseDuration for conversion.
func (v *Var) Duration() time.Duration {
return gconv.Duration(v.Val())
}
// GTime converts and returns <v> as *gtime.Time.
// The param <format> specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) GTime(format...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}
// Struct maps value of <v> to <objPointer>.
// The param <objPointer> should be a pointer to a struct instance.
// The param <attrMapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error {
return gconv.Struct(v.Val(), objPointer, attrMapping...)
}
func (v *Var) IsNil() bool { return v.Val() == nil }
func (v *Var) Bytes() []byte { return gconv.Bytes(v.Val()) }
func (v *Var) String() string { return gconv.String(v.Val()) }
@ -88,19 +109,3 @@ func (v *Var) Ints() []int { return gconv.Ints(v.Val()) }
func (v *Var) Floats() []float64 { return gconv.Floats(v.Val()) }
func (v *Var) Strings() []string { return gconv.Strings(v.Val()) }
func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) }
func (v *Var) Time(format...string) time.Time {
return gconv.Time(v.Val(), format...)
}
func (v *Var) TimeDuration() time.Duration {
return gconv.TimeDuration(v.Val())
}
func (v *Var) GTime(format...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}
// 将变量转换为对象,注意 objPointer 参数必须为struct指针
func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error {
return gconv.Struct(v.Val(), objPointer, attrMapping...)
}

View File

@ -1,42 +0,0 @@
// Copyright 2018 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 gvar
import (
"github.com/gogf/gf/g/os/gtime"
"time"
)
// 只读变量接口
type VarRead interface {
Val() interface{}
IsNil() bool
Bytes() []byte
String() string
Bool() bool
Int() int
Int8() int8
Int16() int16
Int32() int32
Int64() int64
Uint() uint
Uint8() uint8
Uint16() uint16
Uint32() uint32
Uint64() uint64
Float32() float32
Float64() float64
Interface() interface{}
Ints() []int
Floats() []float64
Strings() []string
Interfaces() []interface{}
Time(format ...string) time.Time
TimeDuration() time.Duration
GTime(format...string) *gtime.Time
Struct(objPointer interface{}, attrMapping ...map[string]string) error
}

View File

@ -1,3 +1,9 @@
// Copyright 2019 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 gvar_test
import (
@ -9,22 +15,6 @@ import (
"github.com/gogf/gf/g/test/gtest"
)
func TestReadOnly(t *testing.T) {
gtest.Case(t, func() {
obj := gvar.New(nil, true)
var result string
switch obj.ReadOnly().(type) {
case gvar.VarRead:
result = "yes"
default:
result = "no"
}
gtest.Assert(result, "yes")
})
}
func TestSet(t *testing.T) {
gtest.Case(t, func() {
objOne := gvar.New("old", true)

View File

@ -20,7 +20,6 @@ import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"time"
)
@ -158,7 +157,7 @@ const (
var (
// Instance map.
instances = gmap.NewStringInterfaceMap()
instances = gmap.NewStrAnyMap()
)
// New creates ORM DB object with global configurations.

View File

@ -15,9 +15,9 @@ import (
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"reflect"
"strings"
"time"
)
// 格式化SQL查询条件
@ -137,6 +137,10 @@ func convertParam(value interface{}) interface{} {
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time 类型
if _, ok := value.(time.Time); ok {
return value
}
return gconv.String(value)
}
return value

View File

@ -13,7 +13,6 @@ import (
"errors"
"fmt"
"github.com/gogf/gf/g/util/gconv"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"reflect"
)

View File

@ -8,8 +8,9 @@
package gdb
import (
"fmt"
"database/sql"
"database/sql"
"fmt"
_ "github.com/gogf/gf/third/github.com/gf-third/mysql"
)
// 数据库链接对象
@ -26,7 +27,7 @@ func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
}
if db, err := sql.Open("mysql", source); err == nil {
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
} else {
return nil, err

View File

@ -10,7 +10,6 @@ import (
"database/sql"
"fmt"
"github.com/gogf/gf/g/text/gregex"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"reflect"
)

View File

@ -6,15 +6,18 @@
// Package gredis provides convenient client for redis server.
//
// Redis客户端.
// Redis中文手册请参考http://redisdoc.com/
// Redis官方命令请参考:https://redis.io/commands
// Redis Client.
//
// Redis Commands Official: https://redis.io/commands
//
// Redis Chinese Documentation: http://redisdoc.com/
package gredis
import (
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
"time"
)
@ -23,44 +26,44 @@ const (
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
)
// Redis客户端(管理连接池)
// Redis client.
type Redis struct {
pool *redis.Pool // 底层连接池
group string // 配置分组
config Config // 配置对象
pool *redis.Pool // Underlying connection pool.
group string // Configuration group.
config Config // Configuration.
}
// Redis连接对象(连接池中的单个连接)
type Conn redis.Conn
// Redis connection.
type Conn struct {
redis.Conn
}
// Redis服务端但节点连接配置信息
// Redis configuration.
type Config struct {
Host string // 地址
Port int // 端口
Db int // 数据库
Pass string // 授权密码
MaxIdle int // 最大允许空闲存在的连接数(默认为0表示不存在闲置连接)
MaxActive int // 最大连接数量限制(默认为0表示不限制)
IdleTimeout time.Duration // 连接最大空闲时间(默认为60秒,不允许设置为0)
MaxConnLifetime time.Duration // 连接最长存活时间(默认为60秒,不允许设置为0)
Host string
Port int
Db int
Pass string // Password for AUTH.
MaxIdle int // Maximum number of connections allowed to be idle (default is 0 means no idle connection)
MaxActive int // Maximum number of connections limit (default is 0 means no limit)
IdleTimeout time.Duration // Maximum idle time for connection (default is 60 seconds, not allowed to be set to 0)
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 60 seconds, not allowed to be set to 0)
}
// Redis链接池统计信息
// Pool statistics.
type PoolStats struct {
redis.PoolStats
}
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
// 连接池Map
pools = gmap.NewStringInterfaceMap()
// Instance map
instances = gmap.NewStrAnyMap()
// Pool map.
pools = gmap.NewStrAnyMap()
)
// New creates a redis client object with given configuration.
// Redis client maintains a connection pool automatically.
//
// 创建redis操作对象底层根据配置信息公用的连接池连接池单例
func New(config Config) *Redis {
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
@ -79,20 +82,20 @@ func New(config Config) *Redis {
if err != nil {
return nil, err
}
// 密码设置
// AUTH
if len(config.Pass) > 0 {
if _, err := c.Do("AUTH", config.Pass); err != nil {
return nil, err
}
}
// 数据库设置
// DB
if _, err := c.Do("SELECT", config.Db); err != nil {
return nil, err
}
return c, nil
},
// 在被应用从连接池中获取出来之后,用以测试连接是否可用,
// 如果返回error那么关闭该连接对象重新创建新的连接。
// After the conn is taken from the connection pool, to test if the connection is available,
// If error is returned then it closes the connection object and recreate a new connection.
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
@ -104,9 +107,7 @@ func New(config Config) *Redis {
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// return redis instance with default group.
//
// 获取指定分组名称的Redis单例对象底层根据配置信息公用的连接池连接池单例
// it returns a redis instance with default group.
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
@ -128,85 +129,73 @@ func Instance(name ...string) *Redis {
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It always not necessary to call Close manually.
//
// 关闭redis管理对象将会关闭底层的连接池。
// 往往没必要手动调用,跟随进程销毁即可。
// It is not necessary to call Close manually.
func (r *Redis) Close() error {
if r.group != "" {
// 如果是单例对象那么需要从单例对象Map中删除
// If it is an instance object, it needs to remove it from the instance Map.
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
}
// See GetConn.
func (r *Redis) Conn() Conn {
return r.GetConn()
// Conn returns a raw underlying connection object,
// which expose more methods to communicate with server.
// **You should call Close function manually if you do not use this connection any further.**
func (r *Redis) Conn() *Conn {
return &Conn{ r.pool.Get() }
}
// GetConn returns a raw connection object,
// which expose more methods communication with server.
// **You should call Close function manually if you do not use this connection any further.**
//
// 获得一个原生的redis连接对象用于自定义连接操作
// 但是需要注意的是如果不再使用该连接对象时需要手动Close连接否则会造成连接数超限。
func (r *Redis) GetConn() Conn {
return r.pool.Get().(Conn)
// Alias of Conn, see Conn.
func (r *Redis) GetConn() *Conn {
return r.Conn()
}
// SetMaxIdle sets the MaxIdle attribute of the connection pool.
//
// 设置属性 - MaxIdle
func (r *Redis) SetMaxIdle(value int) {
r.pool.MaxIdle = value
}
// SetMaxIdle sets the MaxActive attribute of the connection pool.
//
// 设置属性 - MaxActive
// SetMaxActive sets the MaxActive attribute of the connection pool.
func (r *Redis) SetMaxActive(value int) {
r.pool.MaxActive = value
}
// SetMaxIdle sets the IdleTimeout attribute of the connection pool.
//
// 设置属性 - IdleTimeout
// SetIdleTimeout sets the IdleTimeout attribute of the connection pool.
func (r *Redis) SetIdleTimeout(value time.Duration) {
r.pool.IdleTimeout = value
}
// SetMaxIdle sets the MaxConnLifetime attribute of the connection pool.
//
// 设置属性 - MaxConnLifetime
// SetMaxConnLifetime sets the MaxConnLifetime attribute of the connection pool.
func (r *Redis) SetMaxConnLifetime(value time.Duration) {
r.pool.MaxConnLifetime = value
}
// Stats returns pool's statistics.
//
// 获取当前连接池统计信息。
func (r *Redis) Stats() *PoolStats {
return &PoolStats{r.pool.Stats()}
}
// Do sends a command to the server and returns the received reply.
// Do automatically get a connection from pool, and close it when reply received.
//
// 执行同步命令自动从连接池中获取连接使用完毕后关闭连接丢回连接池开发者不用自行Close.
// It does not really "close" the connection, but drop it back to the connection pool.
func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
conn := r.pool.Get()
conn := &Conn{ r.pool.Get() }
defer conn.Close()
return conn.Do(command, args...)
}
// DoVar returns value from Do as gvar.Var.
func (r *Redis) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
v, err := r.Do(command, args...)
return gvar.New(v, true), err
}
// Deprecated.
// Send writes the command to the client's output buffer.
//
// 执行异步命令 - Send
func (r *Redis) Send(command string, args ...interface{}) error {
conn := r.pool.Get()
conn := &Conn{ r.pool.Get() }
defer conn.Close()
return conn.Send(command, args...)
}

View File

@ -14,7 +14,7 @@ const (
)
var (
// Configuration groups.
configs = gmap.NewStringInterfaceMap()
configs = gmap.NewStrAnyMap()
)
// SetConfig sets the global configuration for specified group.

View File

@ -0,0 +1,21 @@
// Copyright 2019 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 gredis
import "github.com/gogf/gf/g/container/gvar"
// DoVar returns value from Do as gvar.Var.
func (c *Conn) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
v, err := c.Do(command, args...)
return gvar.New(v, true), err
}
// ReceiveVar receives a single reply as gvar.Var from the Redis server.
func (c *Conn) ReceiveVar() (*gvar.Var, error) {
v, err := c.Receive()
return gvar.New(v, true), err
}

View File

@ -71,7 +71,7 @@ func Test_Stats(t *testing.T) {
redis.SetIdleTimeout(500*time.Millisecond)
redis.SetMaxConnLifetime(500*time.Millisecond)
array := make([]gredis.Conn, 0)
array := make([]*gredis.Conn, 0)
for i := 0; i < 10; i++ {
array = append(array, redis.Conn())
}

View File

@ -5,6 +5,8 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gbinary provides useful API for handling binary/bytes data.
//
// 注意gbinary模块统一使用LittleEndian进行编码。
package gbinary
import (

View File

@ -8,22 +8,12 @@
package gjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
"github.com/gogf/gf/g/encoding/gyaml"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"reflect"
"strconv"
"strings"
"time"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"reflect"
"strconv"
"strings"
)
const (
@ -40,330 +30,6 @@ type Json struct {
// when the hierarchical data key contains separator char.
}
// New creates a Json object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Json object
// in un-concurrent-safe context, which is false in default.
func New(data interface{}, unsafe...bool) *Json {
j := (*Json)(nil)
switch data.(type) {
case map[string]interface{}, []interface{}, nil:
j = &Json {
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
case string, []byte:
j, _ = LoadContent(gconv.Bytes(data))
default:
v := (interface{})(nil)
if m := gconv.Map(data); m != nil {
v = m
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false,
}
} else {
v = gconv.Interfaces(data)
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false,
}
}
}
j.mu = rwmutex.New(unsafe...)
return j
}
// NewUnsafe creates a un-concurrent-safe Json object.
func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 {
return New(data[0], true)
}
return New(nil, true)
}
// Valid checks whether <data> is a valid JSON data type.
func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data))
}
// Encode encodes <value> to JSON data type of bytes.
func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// Decode decodes <data>(string/[]byte) to golang variable.
func Decode(data interface{}) (interface{}, error) {
var value interface{}
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
return nil, err
} else {
return value, nil
}
}
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
// The <v> should be a pointer type.
func DecodeTo(data interface{}, v interface{}) error {
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
decoder.UseNumber()
return decoder.Decode(v)
}
// DecodeToJson codes <data>(string/[]byte) to a Json object.
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
if v, err := Decode(gconv.Bytes(data)); err != nil {
return nil, err
} else {
return New(v, unsafe...), nil
}
}
// Load loads content from specified file <path>,
// and creates a Json object from its content.
func Load(path string, unsafe...bool) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), unsafe...)
}
// LoadContent creates a Json object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
var err error
var result interface{}
b := gconv.Bytes(data)
t := "json"
// auto check data type
if json.Valid(b) {
t = "json"
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
t = "xml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
t = "yml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
t = "toml"
} else {
return nil, errors.New("unsupported data type")
}
// convert to json type data
switch t {
case "json", ".json":
// ok
case "xml", ".xml":
// TODO UseNumber
b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml":
// TODO UseNumber
b, err = gyaml.ToJson(b)
case "toml", ".toml":
// TODO UseNumber
b, err = gtoml.ToJson(b)
default:
err = errors.New("nonsupport type " + t)
}
if err != nil {
return nil, err
}
if result == nil {
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
}
}
return New(result, unsafe...), nil
}
// SetSplitChar sets the separator char for hierarchical data access.
func (j *Json) SetSplitChar(char byte) {
j.mu.Lock()
j.c = char
j.mu.Unlock()
}
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (j *Json) SetViolenceCheck(enabled bool) {
j.mu.Lock()
j.vc = enabled
j.mu.Unlock()
}
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <v> should be a pointer type.
func (j *Json) GetToVar(pattern string, v interface{}) error {
r := j.Get(pattern)
if r != nil {
if t, err := Encode(r); err == nil {
return DecodeTo(t, v)
} else {
return err
}
} else {
v = nil
}
return nil
}
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (j *Json) GetMap(pattern string) map[string]interface{} {
result := j.Get(pattern)
if result != nil {
return gconv.Map(result)
}
return nil
}
// GetJson gets the value by specified <pattern>,
// and converts it to a Json object.
func (j *Json) GetJson(pattern string) *Json {
result := j.Get(pattern)
if result != nil {
return New(result)
}
return nil
}
// GetJsons gets the value by specified <pattern>,
// and converts it to a slice of Json object.
func (j *Json) GetJsons(pattern string) []*Json {
array := j.GetArray(pattern)
if len(array) > 0 {
jsons := make([]*Json, len(array))
for i := 0; i < len(array); i++ {
jsons[i] = New(array[i], !j.mu.IsSafe())
}
return jsons
}
return nil
}
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (j *Json) GetArray(pattern string) []interface{} {
return gconv.Interfaces(j.Get(pattern))
}
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (j *Json) GetString(pattern string) string {
return gconv.String(j.Get(pattern))
}
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (j *Json) GetStrings(pattern string) []string {
return gconv.Strings(j.Get(pattern))
}
// See GetArray.
func (j *Json) GetInterfaces(pattern string) []interface{} {
return gconv.Interfaces(j.Get(pattern))
}
func (j *Json) GetTime(pattern string, format ... string) time.Time {
return gconv.Time(j.Get(pattern), format...)
}
func (j *Json) GetTimeDuration(pattern string) time.Duration {
return gconv.TimeDuration(j.Get(pattern))
}
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (j *Json) GetBool(pattern string) bool {
return gconv.Bool(j.Get(pattern))
}
func (j *Json) GetInt(pattern string) int {
return gconv.Int(j.Get(pattern))
}
func (j *Json) GetInt8(pattern string) int8 {
return gconv.Int8(j.Get(pattern))
}
func (j *Json) GetInt16(pattern string) int16 {
return gconv.Int16(j.Get(pattern))
}
func (j *Json) GetInt32(pattern string) int32 {
return gconv.Int32(j.Get(pattern))
}
func (j *Json) GetInt64(pattern string) int64 {
return gconv.Int64(j.Get(pattern))
}
func (j *Json) GetInts(pattern string) []int {
return gconv.Ints(j.Get(pattern))
}
func (j *Json) GetUint(pattern string) uint {
return gconv.Uint(j.Get(pattern))
}
func (j *Json) GetUint8(pattern string) uint8 {
return gconv.Uint8(j.Get(pattern))
}
func (j *Json) GetUint16(pattern string) uint16 {
return gconv.Uint16(j.Get(pattern))
}
func (j *Json) GetUint32(pattern string) uint32 {
return gconv.Uint32(j.Get(pattern))
}
func (j *Json) GetUint64(pattern string) uint64 {
return gconv.Uint64(j.Get(pattern))
}
func (j *Json) GetFloat32(pattern string) float32 {
return gconv.Float32(j.Get(pattern))
}
func (j *Json) GetFloat64(pattern string) float64 {
return gconv.Float64(j.Get(pattern))
}
func (j *Json) GetFloats(pattern string) []float64 {
return gconv.Floats(j.Get(pattern))
}
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <objPointer>.
// The <objPointer> should be the pointer to an object.
func (j *Json) GetToStruct(pattern string, objPointer interface{}) error {
return gconv.Struct(j.Get(pattern), objPointer)
}
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Set(pattern string, value interface{}) error {
return j.setValue(pattern, value, false)
}
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Remove(pattern string) error {
return j.setValue(pattern, nil, true)
}
// Set <value> by <pattern>.
// Notice:
// 1. If value is nil and removed is true, means deleting this value;
@ -576,69 +242,6 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
return pointer
}
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
func (j *Json) Get(pattern...string) interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
queryPattern := ""
if len(pattern) > 0 {
queryPattern = pattern[0]
}
var result *interface{}
if j.vc {
result = j.getPointerByPattern(queryPattern)
} else {
result = j.getPointerByPatternWithoutViolenceCheck(queryPattern)
}
if result != nil {
return *result
}
return nil
}
// Contains checks whether the value by specified <pattern> exist.
func (j *Json) Contains(pattern...string) bool {
return j.Get(pattern...) != nil
}
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (j *Json) Len(pattern string) int {
p := j.getPointerByPattern(pattern)
if p != nil {
switch (*p).(type) {
case map[string]interface{}:
return len((*p).(map[string]interface{}))
case []interface{}:
return len((*p).([]interface{}))
default:
return -1
}
}
return -1
}
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (j *Json) Append(pattern string, value interface{}) error {
p := j.getPointerByPattern(pattern)
if p == nil {
return j.Set(fmt.Sprintf("%s.0", pattern), value)
}
switch (*p).(type) {
case []interface{}:
return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value)
}
return fmt.Errorf("invalid variable type of %s", pattern)
}
// Get a pointer to the value by specified <pattern>.
func (j *Json) getPointerByPattern(pattern string) *interface{} {
if j.vc {
@ -729,111 +332,3 @@ func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interfac
}
return nil
}
// ToMap converts current Json object to map[string]interface{}.
// It returns nil if fails.
func (j *Json) ToMap() map[string]interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
switch (*(j.p)).(type) {
case map[string]interface{}:
return (*(j.p)).(map[string]interface{})
default:
return nil
}
}
// ToArray converts current Json object to []interface{}.
// It returns nil if fails.
func (j *Json) ToArray() []interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
switch (*(j.p)).(type) {
case []interface{}:
return (*(j.p)).([]interface{})
default:
return nil
}
}
func (j *Json) ToXml(rootTag...string) ([]byte, error) {
return gxml.Encode(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlString(rootTag...string) (string, error) {
b, e := j.ToXml(rootTag...)
return string(b), e
}
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
b, e := j.ToXmlIndent(rootTag...)
return string(b), e
}
func (j *Json) ToJson() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return Encode(*(j.p))
}
func (j *Json) ToJsonString() (string, error) {
b, e := j.ToJson()
return string(b), e
}
func (j *Json) ToJsonIndent() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return json.MarshalIndent(*(j.p), "", "\t")
}
func (j *Json) ToJsonIndentString() (string, error) {
b, e := j.ToJsonIndent()
return string(b), e
}
func (j *Json) ToYaml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gyaml.Encode(*(j.p))
}
func (j *Json) ToYamlString() (string, error) {
b, e := j.ToYaml()
return string(b), e
}
func (j *Json) ToToml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gtoml.Encode(*(j.p))
}
func (j *Json) ToTomlString() (string, error) {
b, e := j.ToToml()
return string(b), e
}
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (j *Json) ToStruct(objPointer interface{}) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Struct(*(j.p), objPointer)
}
// Dump prints current Json object with more manually readable.
func (j *Json) Dump() error {
j.mu.RLock()
defer j.mu.RUnlock()
if b, err := j.ToJsonIndent(); err != nil {
return err
} else {
fmt.Println(string(b))
}
return nil
}

View File

@ -0,0 +1,311 @@
// 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 gjson
import (
"fmt"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/util/gconv"
"time"
)
// Val returns the json value.
func (j *Json) Value() interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
return *(j.p)
}
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
//
// It returns a default value specified by <def> if value for <pattern> is not found.
func (j *Json) Get(pattern string, def...interface{}) interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
var result *interface{}
if j.vc {
result = j.getPointerByPattern(pattern)
} else {
result = j.getPointerByPatternWithoutViolenceCheck(pattern)
}
if result != nil {
return *result
}
if len(def) > 0 {
return def[0]
}
return nil
}
// GetVar returns a *gvar.Var with value by given <pattern>.
func (j *Json) GetVar(pattern string, def...interface{}) *gvar.Var {
return gvar.New(j.Get(pattern, def...), true)
}
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{} {
result := j.Get(pattern, def...)
if result != nil {
return gconv.Map(result)
}
return nil
}
// GetJson gets the value by specified <pattern>,
// and converts it to a un-concurrent-safe Json object.
func (j *Json) GetJson(pattern string, def...interface{}) *Json {
result := j.Get(pattern, def...)
if result != nil {
return New(result, true)
}
return nil
}
// GetJsons gets the value by specified <pattern>,
// and converts it to a slice of un-concurrent-safe Json object.
func (j *Json) GetJsons(pattern string, def...interface{}) []*Json {
array := j.GetArray(pattern, def...)
if len(array) > 0 {
jsonSlice := make([]*Json, len(array))
for i := 0; i < len(array); i++ {
jsonSlice[i] = New(array[i], true)
}
return jsonSlice
}
return nil
}
// GetJsonMap gets the value by specified <pattern>,
// and converts it to a map of un-concurrent-safe Json object.
func (j *Json) GetJsonMap(pattern string, def...interface{}) map[string]*Json {
m := j.GetMap(pattern, def...)
if len(m) > 0 {
jsonMap := make(map[string]*Json, len(m))
for k, v := range m {
jsonMap[k] = New(v, true)
}
return jsonMap
}
return nil
}
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (j *Json) GetArray(pattern string, def...interface{}) []interface{} {
return gconv.Interfaces(j.Get(pattern, def...))
}
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (j *Json) GetString(pattern string, def...interface{}) string {
return gconv.String(j.Get(pattern, def...))
}
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (j *Json) GetBool(pattern string, def...interface{}) bool {
return gconv.Bool(j.Get(pattern, def...))
}
func (j *Json) GetInt(pattern string, def...interface{}) int {
return gconv.Int(j.Get(pattern, def...))
}
func (j *Json) GetInt8(pattern string, def...interface{}) int8 {
return gconv.Int8(j.Get(pattern, def...))
}
func (j *Json) GetInt16(pattern string, def...interface{}) int16 {
return gconv.Int16(j.Get(pattern, def...))
}
func (j *Json) GetInt32(pattern string, def...interface{}) int32 {
return gconv.Int32(j.Get(pattern, def...))
}
func (j *Json) GetInt64(pattern string, def...interface{}) int64 {
return gconv.Int64(j.Get(pattern, def...))
}
func (j *Json) GetUint(pattern string, def...interface{}) uint {
return gconv.Uint(j.Get(pattern, def...))
}
func (j *Json) GetUint8(pattern string, def...interface{}) uint8 {
return gconv.Uint8(j.Get(pattern, def...))
}
func (j *Json) GetUint16(pattern string, def...interface{}) uint16 {
return gconv.Uint16(j.Get(pattern, def...))
}
func (j *Json) GetUint32(pattern string, def...interface{}) uint32 {
return gconv.Uint32(j.Get(pattern, def...))
}
func (j *Json) GetUint64(pattern string, def...interface{}) uint64 {
return gconv.Uint64(j.Get(pattern, def...))
}
func (j *Json) GetFloat32(pattern string, def...interface{}) float32 {
return gconv.Float32(j.Get(pattern, def...))
}
func (j *Json) GetFloat64(pattern string, def...interface{}) float64 {
return gconv.Float64(j.Get(pattern, def...))
}
func (j *Json) GetFloats(pattern string, def...interface{}) []float64 {
return gconv.Floats(j.Get(pattern, def...))
}
func (j *Json) GetInts(pattern string, def...interface{}) []int {
return gconv.Ints(j.Get(pattern, def...))
}
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (j *Json) GetStrings(pattern string, def...interface{}) []string {
return gconv.Strings(j.Get(pattern, def...))
}
// See GetArray.
func (j *Json) GetInterfaces(pattern string, def...interface{}) []interface{} {
return gconv.Interfaces(j.Get(pattern, def...))
}
func (j *Json) GetTime(pattern string, format... string) time.Time {
return gconv.Time(j.Get(pattern), format...)
}
func (j *Json) GetDuration(pattern string, def...interface{}) time.Duration {
return gconv.Duration(j.Get(pattern, def...))
}
func (j *Json) GetGTime(pattern string, format... string) *gtime.Time {
return gconv.GTime(j.Get(pattern), format...)
}
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Set(pattern string, value interface{}) error {
return j.setValue(pattern, value, false)
}
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Remove(pattern string) error {
return j.setValue(pattern, nil, true)
}
// Contains checks whether the value by specified <pattern> exist.
func (j *Json) Contains(pattern string) bool {
return j.Get(pattern) != nil
}
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (j *Json) Len(pattern string) int {
p := j.getPointerByPattern(pattern)
if p != nil {
switch (*p).(type) {
case map[string]interface{}:
return len((*p).(map[string]interface{}))
case []interface{}:
return len((*p).([]interface{}))
default:
return -1
}
}
return -1
}
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (j *Json) Append(pattern string, value interface{}) error {
p := j.getPointerByPattern(pattern)
if p == nil {
return j.Set(fmt.Sprintf("%s.0", pattern), value)
}
switch (*p).(type) {
case []interface{}:
return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value)
}
return fmt.Errorf("invalid variable type of %s", pattern)
}
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <pointer> should be a pointer type.
func (j *Json) GetToVar(pattern string, pointer interface{}) error {
r := j.Get(pattern)
if r != nil {
if t, err := Encode(r); err == nil {
return DecodeTo(t, pointer)
} else {
return err
}
} else {
pointer = nil
}
return nil
}
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <objPointer>.
// The <objPointer> should be the pointer to an object.
func (j *Json) GetToStruct(pattern string, pointer interface{}) error {
return gconv.Struct(j.Get(pattern), pointer)
}
// ToMap converts current Json object to map[string]interface{}.
// It returns nil if fails.
func (j *Json) ToMap() map[string]interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Map(*(j.p))
}
// ToArray converts current Json object to []interface{}.
// It returns nil if fails.
func (j *Json) ToArray() []interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Interfaces(*(j.p))
}
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (j *Json) ToStruct(pointer interface{}) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Struct(*(j.p), pointer)
}
// Dump prints current Json object with more manually readable.
func (j *Json) Dump() error {
j.mu.RLock()
defer j.mu.RUnlock()
if b, err := j.ToJsonIndent(); err != nil {
return err
} else {
fmt.Println(string(b))
}
return nil
}

View File

@ -0,0 +1,21 @@
// 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 gjson
// SetSplitChar sets the separator char for hierarchical data access.
func (j *Json) SetSplitChar(char byte) {
j.mu.Lock()
j.c = char
j.mu.Unlock()
}
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (j *Json) SetViolenceCheck(enabled bool) {
j.mu.Lock()
j.vc = enabled
j.mu.Unlock()
}

View File

@ -0,0 +1,76 @@
// 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 gjson
import (
"encoding/json"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
"github.com/gogf/gf/g/encoding/gyaml"
)
func (j *Json) ToXml(rootTag...string) ([]byte, error) {
return gxml.Encode(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlString(rootTag...string) (string, error) {
b, e := j.ToXml(rootTag...)
return string(b), e
}
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
b, e := j.ToXmlIndent(rootTag...)
return string(b), e
}
func (j *Json) ToJson() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return Encode(*(j.p))
}
func (j *Json) ToJsonString() (string, error) {
b, e := j.ToJson()
return string(b), e
}
func (j *Json) ToJsonIndent() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return json.MarshalIndent(*(j.p), "", "\t")
}
func (j *Json) ToJsonIndentString() (string, error) {
b, e := j.ToJsonIndent()
return string(b), e
}
func (j *Json) ToYaml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gyaml.Encode(*(j.p))
}
func (j *Json) ToYamlString() (string, error) {
b, e := j.ToYaml()
return string(b), e
}
func (j *Json) ToToml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gtoml.Encode(*(j.p))
}
func (j *Json) ToTomlString() (string, error) {
b, e := j.ToToml()
return string(b), e
}

View File

@ -0,0 +1,182 @@
// 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 gjson provides convenient API for JSON/XML/YAML/TOML data handling.
package gjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
"github.com/gogf/gf/g/encoding/gyaml"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"reflect"
)
// New creates a Json object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Json object
// in un-concurrent-safe context, which is false in default.
func New(data interface{}, unsafe...bool) *Json {
j := (*Json)(nil)
switch data.(type) {
case string, []byte:
if r, err := LoadContent(gconv.Bytes(data)); err == nil {
j = r
} else {
j = &Json {
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
}
default:
rv := reflect.ValueOf(data)
kind := rv.Kind()
switch kind {
case reflect.Slice: fallthrough
case reflect.Array:
i := interface{}(nil)
i = gconv.Interfaces(data)
j = &Json {
p : &i,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
case reflect.Map: fallthrough
case reflect.Struct:
i := interface{}(nil)
i = gconv.Map(data)
j = &Json {
p : &i,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
default:
j = &Json {
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
}
}
j.mu = rwmutex.New(unsafe...)
return j
}
// NewUnsafe creates a un-concurrent-safe Json object.
func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 {
return New(data[0], true)
}
return New(nil, true)
}
// Valid checks whether <data> is a valid JSON data type.
func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data))
}
// Encode encodes <value> to JSON data type of bytes.
func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// Decode decodes <data>(string/[]byte) to golang variable.
func Decode(data interface{}) (interface{}, error) {
var value interface{}
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
return nil, err
} else {
return value, nil
}
}
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
// The <v> should be a pointer type.
func DecodeTo(data interface{}, v interface{}) error {
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
decoder.UseNumber()
return decoder.Decode(v)
}
// DecodeToJson codes <data>(string/[]byte) to a Json object.
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
if v, err := Decode(gconv.Bytes(data)); err != nil {
return nil, err
} else {
return New(v, unsafe...), nil
}
}
// Load loads content from specified file <path>,
// and creates a Json object from its content.
func Load(path string, unsafe...bool) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), unsafe...)
}
// LoadContent creates a Json object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
var err error
var result interface{}
b := gconv.Bytes(data)
t := "json"
// auto check data type
if json.Valid(b) {
t = "json"
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
t = "xml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
t = "yml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
t = "toml"
} else {
return nil, errors.New("unsupported data type")
}
// convert to json type data
switch t {
case "json", ".json":
// ok
case "xml", ".xml":
// TODO UseNumber
b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml":
// TODO UseNumber
b, err = gyaml.ToJson(b)
case "toml", ".toml":
// TODO UseNumber
b, err = gtoml.ToJson(b)
default:
err = errors.New("nonsupport type " + t)
}
if err != nil {
return nil, err
}
if result == nil {
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
}
}
return New(result, unsafe...), nil
}

View File

@ -0,0 +1,63 @@
// 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 gjson
//func MarshalOrdered(value interface{}) ([]byte, error) {
// buffer := bytes.NewBuffer(nil)
// rv := reflect.ValueOf(value)
// kind := rv.Kind()
// if kind == reflect.Ptr {
// rv = rv.Elem()
// kind = rv.Kind()
// }
// switch kind {
// case reflect.Slice: fallthrough
// case reflect.Array:
// buffer.WriteByte('[')
// length := rv.Len()
// for i := 0; i < length; i++ {
// if p, err := MarshalOrdered(rv.Index(i).Interface()); err != nil {
// return nil, err
// } else {
// buffer.Write(p)
// if i < length - 1 {
// buffer.WriteByte(',')
// }
// }
// }
// buffer.WriteByte(']')
// case reflect.Map: fallthrough
// case reflect.Struct:
// m := gconv.Map(value, "json")
// keys := make([]string, len(m))
// index := 0
// for key := range m {
// keys[index] = key
// index++
// }
// sort.Strings(keys)
// buffer.WriteByte('{')
// for i, key := range keys {
// if p, err := MarshalOrdered(m[key]); err != nil {
// return nil, err
// } else {
// buffer.WriteString(fmt.Sprintf(`"%s":%s`, key, string(p)))
// if i < index - 1 {
// buffer.WriteByte(',')
// }
// }
// }
// buffer.WriteByte('}')
// default:
// if p, err := json.Marshal(value); err != nil {
// return nil, err
// } else {
// buffer.Write(p)
// }
// }
// return buffer.Bytes(), nil
//}

View File

@ -225,29 +225,78 @@ func Test_Len(t *testing.T) {
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}
func TestJson_ToJson(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New("1")
s, e := p.ToJsonString()
gtest.Assert(e, nil)
gtest.Assert(s, "1")
})
gtest.Case(t, func() {
p := gjson.New("a")
s, e := p.ToJsonString()
gtest.Assert(e, nil)
gtest.Assert(s, `"a"`)
})
}
func TestJson_Default(t *testing.T) {
gtest.Case(t, func() {
j := gjson.New(nil)
gtest.AssertEQ(j.Get("no", 100), 100)
gtest.AssertEQ(j.GetString("no", 100), "100")
gtest.AssertEQ(j.GetBool("no", "on"), true)
gtest.AssertEQ(j.GetInt("no", 100), 100)
gtest.AssertEQ(j.GetInt8("no", 100), int8(100))
gtest.AssertEQ(j.GetInt16("no", 100), int16(100))
gtest.AssertEQ(j.GetInt32("no", 100), int32(100))
gtest.AssertEQ(j.GetInt64("no", 100), int64(100))
gtest.AssertEQ(j.GetUint("no", 100), uint(100))
gtest.AssertEQ(j.GetUint8("no", 100), uint8(100))
gtest.AssertEQ(j.GetUint16("no", 100), uint16(100))
gtest.AssertEQ(j.GetUint32("no", 100), uint32(100))
gtest.AssertEQ(j.GetUint64("no", 100), uint64(100))
gtest.AssertEQ(j.GetFloat32("no", 123.456), float32(123.456))
gtest.AssertEQ(j.GetFloat64("no", 123.456), float64(123.456))
gtest.AssertEQ(j.GetArray("no", g.Slice{1,2,3}), g.Slice{1,2,3})
gtest.AssertEQ(j.GetInts("no", g.Slice{1,2,3}), g.SliceInt{1,2,3})
gtest.AssertEQ(j.GetFloats("no", g.Slice{1,2,3}), []float64{1,2,3})
gtest.AssertEQ(j.GetMap("no", g.Map{"k":"v"}), g.Map{"k":"v"})
gtest.AssertEQ(j.GetVar("no", 123.456).Float64(), float64(123.456))
gtest.AssertEQ(j.GetJson("no", g.Map{"k":"v"}).Get("k"), "v")
gtest.AssertEQ(j.GetJsons("no", g.Slice{
g.Map{"k1":"v1"},
g.Map{"k2":"v2"},
g.Map{"k3":"v3"},
})[0].Get("k1"), "v1")
gtest.AssertEQ(j.GetJsonMap("no", g.Map{
"m1" : g.Map{"k1":"v1"},
"m2" : g.Map{"k2":"v2"},
})["m2"].Get("k2"), "v2")
})
}

View File

@ -8,283 +8,10 @@
package gparser
import (
"github.com/gogf/gf/g/encoding/gjson"
"time"
"github.com/gogf/gf/g/encoding/gjson"
)
type Parser struct {
json *gjson.Json
}
// New creates a Parser object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Parser object
// in un-concurrent-safe context, which is false in default.
func New(value interface{}, unsafe...bool) *Parser {
return &Parser{gjson.New(value, unsafe...)}
}
// NewUnsafe creates a un-concurrent-safe Parser object.
func NewUnsafe (value...interface{}) *Parser {
if len(value) > 0 {
return &Parser{gjson.New(value[0], false)}
}
return &Parser{gjson.New(nil, false)}
}
// Load loads content from specified file <path>,
// and creates a Parser object from its content.
func Load (path string, unsafe...bool) (*Parser, error) {
if j, e := gjson.Load(path, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}
// LoadContent creates a Parser object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent (data []byte, unsafe...bool) (*Parser, error) {
if j, e := gjson.LoadContent(data, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}
// SetSplitChar sets the separator char for hierarchical data access.
func (p *Parser) SetSplitChar(char byte) {
p.json.SetSplitChar(char)
}
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (p *Parser) SetViolenceCheck(check bool) {
p.json.SetViolenceCheck(check)
}
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <v> should be a pointer type.
func (p *Parser) GetToVar(pattern string, v interface{}) error {
return p.json.GetToVar(pattern, v)
}
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (p *Parser) GetMap(pattern string) map[string]interface{} {
return p.json.GetMap(pattern)
}
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (p *Parser) GetArray(pattern string) []interface{} {
return p.json.GetArray(pattern)
}
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (p *Parser) GetString(pattern string) string {
return p.json.GetString(pattern)
}
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (p *Parser) GetStrings(pattern string) []string {
return p.json.GetStrings(pattern)
}
func (p *Parser) GetInterfaces(pattern string) []interface{} {
return p.json.GetInterfaces(pattern)
}
func (p *Parser) GetTime(pattern string, format ... string) time.Time {
return p.json.GetTime(pattern, format...)
}
func (p *Parser) GetTimeDuration(pattern string) time.Duration {
return p.json.GetTimeDuration(pattern)
}
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (p *Parser) GetBool(pattern string) bool {
return p.json.GetBool(pattern)
}
func (p *Parser) GetInt(pattern string) int {
return p.json.GetInt(pattern)
}
func (p *Parser) GetInt8(pattern string) int8 {
return p.json.GetInt8(pattern)
}
func (p *Parser) GetInt16(pattern string) int16 {
return p.json.GetInt16(pattern)
}
func (p *Parser) GetInt32(pattern string) int32 {
return p.json.GetInt32(pattern)
}
func (p *Parser) GetInt64(pattern string) int64 {
return p.json.GetInt64(pattern)
}
func (p *Parser) GetInts(pattern string) []int {
return p.json.GetInts(pattern)
}
func (p *Parser) GetUint(pattern string) uint {
return p.json.GetUint(pattern)
}
func (p *Parser) GetUint8(pattern string) uint8 {
return p.json.GetUint8(pattern)
}
func (p *Parser) GetUint16(pattern string) uint16 {
return p.json.GetUint16(pattern)
}
func (p *Parser) GetUint32(pattern string) uint32 {
return p.json.GetUint32(pattern)
}
func (p *Parser) GetUint64(pattern string) uint64 {
return p.json.GetUint64(pattern)
}
func (p *Parser) GetFloat32(pattern string) float32 {
return p.json.GetFloat32(pattern)
}
func (p *Parser) GetFloat64(pattern string) float64 {
return p.json.GetFloat64(pattern)
}
func (p *Parser) GetFloats(pattern string) []float64 {
return p.json.GetFloats(pattern)
}
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <objPointer>.
// The <objPointer> should be the pointer to an object.
func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error {
return p.json.GetToStruct(pattern, objPointer)
}
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Set(pattern string, value interface{}) error {
return p.json.Set(pattern, value)
}
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (p *Parser) Len(pattern string) int {
return p.json.Len(pattern)
}
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (p *Parser) Append(pattern string, value interface{}) error {
return p.json.Append(pattern, value)
}
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Remove(pattern string) error {
return p.json.Remove(pattern)
}
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
func (p *Parser) Get(pattern...string) interface{} {
return p.json.Get(pattern...)
}
// ToMap converts current object values to map[string]interface{}.
// It returns nil if fails.
func (p *Parser) ToMap() map[string]interface{} {
return p.json.ToMap()
}
// ToArray converts current object values to []interface{}.
// It returns nil if fails.
func (p *Parser) ToArray() []interface{} {
return p.json.ToArray()
}
func (p *Parser) ToXml(rootTag...string) ([]byte, error) {
return p.json.ToXml(rootTag...)
}
func (p *Parser) ToXmlIndent(rootTag...string) ([]byte, error) {
return p.json.ToXmlIndent(rootTag...)
}
func (p *Parser) ToJson() ([]byte, error) {
return p.json.ToJson()
}
func (p *Parser) ToJsonIndent() ([]byte, error) {
return p.json.ToJsonIndent()
}
func (p *Parser) ToYaml() ([]byte, error) {
return p.json.ToYaml()
}
func (p *Parser) ToToml() ([]byte, error) {
return p.json.ToToml()
}
// Dump prints current Json object with more manually readable.
func (p *Parser) Dump() error {
return p.json.Dump()
}
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (p *Parser) ToStruct(o interface{}) error {
return p.json.ToStruct(o)
}
func VarToXml(value interface{}, rootTag...string) ([]byte, error) {
return New(value).ToXml(rootTag...)
}
func VarToXmlIndent(value interface{}, rootTag...string) ([]byte, error) {
return New(value).ToXmlIndent(rootTag...)
}
func VarToJson(value interface{}) ([]byte, error) {
return New(value).ToJson()
}
func VarToJsonIndent(value interface{}) ([]byte, error) {
return New(value).ToJsonIndent()
}
func VarToYaml(value interface{}) ([]byte, error) {
return New(value).ToYaml()
}
func VarToToml(value interface{}) ([]byte, error) {
return New(value).ToToml()
}
func VarToStruct(value interface{}, obj interface{}) error {
return New(value).ToStruct(obj)
}

View File

@ -0,0 +1,201 @@
// 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://gitee.com/johng/gp.
package gparser
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gtime"
"time"
)
// Val returns the value.
func (p *Parser) Value() interface{} {
return p.json.Value()
}
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
//
// It returns a default value specified by <def> if value for <pattern> is not found.
func (p *Parser) Get(pattern string, def...interface{}) interface{} {
return p.json.Get(pattern, def...)
}
// GetVar returns a *gvar.Var with value by given <pattern>.
func (p *Parser) GetVar(pattern string, def...interface{}) *gvar.Var {
return p.json.GetVar(pattern, def...)
}
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (p *Parser) GetMap(pattern string, def...interface{}) map[string]interface{} {
return p.json.GetMap(pattern, def...)
}
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (p *Parser) GetArray(pattern string, def...interface{}) []interface{} {
return p.json.GetArray(pattern, def...)
}
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (p *Parser) GetString(pattern string, def...interface{}) string {
return p.json.GetString(pattern, def...)
}
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (p *Parser) GetBool(pattern string, def...interface{}) bool {
return p.json.GetBool(pattern, def...)
}
func (p *Parser) GetInt(pattern string, def...interface{}) int {
return p.json.GetInt(pattern, def...)
}
func (p *Parser) GetInt8(pattern string, def...interface{}) int8 {
return p.json.GetInt8(pattern, def...)
}
func (p *Parser) GetInt16(pattern string, def...interface{}) int16 {
return p.json.GetInt16(pattern, def...)
}
func (p *Parser) GetInt32(pattern string, def...interface{}) int32 {
return p.json.GetInt32(pattern, def...)
}
func (p *Parser) GetInt64(pattern string, def...interface{}) int64 {
return p.json.GetInt64(pattern, def...)
}
func (p *Parser) GetInts(pattern string, def...interface{}) []int {
return p.json.GetInts(pattern, def...)
}
func (p *Parser) GetUint(pattern string, def...interface{}) uint {
return p.json.GetUint(pattern, def...)
}
func (p *Parser) GetUint8(pattern string, def...interface{}) uint8 {
return p.json.GetUint8(pattern, def...)
}
func (p *Parser) GetUint16(pattern string, def...interface{}) uint16 {
return p.json.GetUint16(pattern, def...)
}
func (p *Parser) GetUint32(pattern string, def...interface{}) uint32 {
return p.json.GetUint32(pattern, def...)
}
func (p *Parser) GetUint64(pattern string, def...interface{}) uint64 {
return p.json.GetUint64(pattern, def...)
}
func (p *Parser) GetFloat32(pattern string, def...interface{}) float32 {
return p.json.GetFloat32(pattern, def...)
}
func (p *Parser) GetFloat64(pattern string, def...interface{}) float64 {
return p.json.GetFloat64(pattern, def...)
}
func (p *Parser) GetFloats(pattern string, def...interface{}) []float64 {
return p.json.GetFloats(pattern, def...)
}
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (p *Parser) GetStrings(pattern string, def...interface{}) []string {
return p.json.GetStrings(pattern, def...)
}
func (p *Parser) GetInterfaces(pattern string, def...interface{}) []interface{} {
return p.json.GetInterfaces(pattern, def...)
}
func (p *Parser) GetTime(pattern string, format...string) time.Time {
return p.json.GetTime(pattern, format...)
}
func (p *Parser) GetDuration(pattern string, def...interface{}) time.Duration {
return p.json.GetDuration(pattern, def...)
}
func (p *Parser) GetGTime(pattern string, format...string) *gtime.Time {
return p.json.GetGTime(pattern, format...)
}
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <v> should be a pointer type.
func (p *Parser) GetToVar(pattern string, pointer interface{}) error {
return p.json.GetToVar(pattern, pointer)
}
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <pointer>.
// The <pointer> should be the pointer to a struct.
func (p *Parser) GetToStruct(pattern string, pointer interface{}) error {
return p.json.GetToStruct(pattern, pointer)
}
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Set(pattern string, value interface{}) error {
return p.json.Set(pattern, value)
}
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (p *Parser) Len(pattern string) int {
return p.json.Len(pattern)
}
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (p *Parser) Append(pattern string, value interface{}) error {
return p.json.Append(pattern, value)
}
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Remove(pattern string) error {
return p.json.Remove(pattern)
}
// ToMap converts current object values to map[string]interface{}.
// It returns nil if fails.
func (p *Parser) ToMap() map[string]interface{} {
return p.json.ToMap()
}
// ToArray converts current object values to []interface{}.
// It returns nil if fails.
func (p *Parser) ToArray() []interface{} {
return p.json.ToArray()
}
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (p *Parser) ToStruct(pointer interface{}) error {
return p.json.ToStruct(pointer)
}
// Dump prints current Json object with more manually readable.
func (p *Parser) Dump() error {
return p.json.Dump()
}

View File

@ -0,0 +1,17 @@
// 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://gitee.com/johng/gp.
package gparser
// SetSplitChar sets the separator char for hierarchical data access.
func (p *Parser) SetSplitChar(char byte) {
p.json.SetSplitChar(char)
}
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (p *Parser) SetViolenceCheck(check bool) {
p.json.SetViolenceCheck(check)
}

View File

@ -0,0 +1,76 @@
// 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://gitee.com/johng/gp.
package gparser
func (p *Parser) ToXml(rootTag...string) ([]byte, error) {
return p.json.ToXml(rootTag...)
}
func (p *Parser) ToXmlIndent(rootTag...string) ([]byte, error) {
return p.json.ToXmlIndent(rootTag...)
}
func (p *Parser) ToJson() ([]byte, error) {
return p.json.ToJson()
}
func (p *Parser) ToJsonString() (string, error) {
return p.json.ToJsonString()
}
func (p *Parser) ToJsonIndent() ([]byte, error) {
return p.json.ToJsonIndent()
}
func (p *Parser) ToJsonIndentString() (string, error) {
return p.json.ToJsonIndentString()
}
func (p *Parser) ToYaml() ([]byte, error) {
return p.json.ToYaml()
}
func (p *Parser) ToToml() ([]byte, error) {
return p.json.ToToml()
}
func VarToXml(value interface{}, rootTag...string) ([]byte, error) {
return New(value).ToXml(rootTag...)
}
func VarToXmlIndent(value interface{}, rootTag...string) ([]byte, error) {
return New(value).ToXmlIndent(rootTag...)
}
func VarToJson(value interface{}) ([]byte, error) {
return New(value).ToJson()
}
func VarToJsonString(value interface{}) (string, error) {
return New(value).ToJsonString()
}
func VarToJsonIndent(value interface{}) ([]byte, error) {
return New(value).ToJsonIndent()
}
func VarToJsonIndentString(value interface{}) (string, error) {
return New(value).ToJsonIndentString()
}
func VarToYaml(value interface{}) ([]byte, error) {
return New(value).ToYaml()
}
func VarToToml(value interface{}) ([]byte, error) {
return New(value).ToToml()
}
func VarToStruct(value interface{}, obj interface{}) error {
return New(value).ToStruct(obj)
}

View File

@ -0,0 +1,49 @@
// 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://gitee.com/johng/gp.
package gparser
import (
"github.com/gogf/gf/g/encoding/gjson"
)
// New creates a Parser object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Parser object
// in un-concurrent-safe context, which is false in default.
func New(value interface{}, unsafe...bool) *Parser {
return &Parser{gjson.New(value, unsafe...)}
}
// NewUnsafe creates a un-concurrent-safe Parser object.
func NewUnsafe(value...interface{}) *Parser {
if len(value) > 0 {
return &Parser{gjson.New(value[0], false)}
}
return &Parser{gjson.New(nil, false)}
}
// Load loads content from specified file <path>,
// and creates a Parser object from its content.
func Load(path string, unsafe...bool) (*Parser, error) {
if j, e := gjson.Load(path, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}
// LoadContent creates a Parser object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent(data []byte, unsafe...bool) (*Parser, error) {
if j, e := gjson.LoadContent(data, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}

View File

@ -28,7 +28,7 @@ const (
)
// 单例对象存储器
var instances = gmap.NewStringInterfaceMap()
var instances = gmap.NewStrAnyMap()
// 获取单例对象
func Get(key string) interface{} {
@ -201,10 +201,10 @@ func Redis(name...string) *gredis.Redis {
redisConfig.MaxActive = gconv.Int(v)
}
if v, ok := parse["idleTimeout"]; ok {
redisConfig.IdleTimeout = gconv.TimeDuration(v)*time.Second
redisConfig.IdleTimeout = gconv.Duration(v)*time.Second
}
if v, ok := parse["maxConnLifetime"]; ok {
redisConfig.MaxConnLifetime = gconv.TimeDuration(v)*time.Second
redisConfig.MaxConnLifetime = gconv.Duration(v)*time.Second
}
addConfigMonitor(key, config)
return gredis.New(redisConfig)
@ -225,7 +225,7 @@ func Redis(name...string) *gredis.Redis {
glog.Errorfln(`configuration for redis not found for group "%s"`, group)
}
} else {
glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath())
glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())
}
return nil
})
@ -238,7 +238,7 @@ func Redis(name...string) *gredis.Redis {
// 添加对单例对象的配置文件inotify监控
func addConfigMonitor(key string, config *gcfg.Config) {
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空对象单例缓存
if path := config.GetFilePath(); path != "" {
if path := config.FilePath(); path != "" {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
instances.Remove(key)
})
@ -246,7 +246,7 @@ func addConfigMonitor(key string, config *gcfg.Config) {
}
// 模板内置方法config
func funcConfig(pattern string, file...string) string {
func funcConfig(pattern string, file...interface{}) string {
return Config().GetString(pattern, file...)
}

View File

@ -38,8 +38,8 @@ func View(name...string) *gview.View {
}
// Config returns an instance of config object with specified name.
func Config(file...string) *gcfg.Config {
return gins.Config(file...)
func Config(name...string) *gcfg.Config {
return gins.Config(name...)
}
// Database returns an instance of database ORM object with specified configuration group name.

View File

@ -32,7 +32,7 @@ func init() {
// 规则:
// 1、命令行参数以小写字母格式使用: gf.包名.变量名 传递;
// 2、环境变量参数以大写字母格式使用: GF_包名_变量名 传递;
func Get(key string, def...interface{}) gvar.VarRead {
func Get(key string, def...interface{}) *gvar.Var {
value := interface{}(nil)
if len(def) > 0 {
value = def[0]

Some files were not shown because too many files have changed in this diff Show More