Compare commits

...

127 Commits

Author SHA1 Message Date
c02f502bd8 fix issue in gqueue.Size 2019-06-15 18:30:09 +08:00
d5d6b8c303 comment unused gbtree.BTree.isFull function 2019-06-15 18:03:07 +08:00
fe152dfa63 comment updates for gtree; fix issue in gtree.AVLTree.Remove 2019-06-15 16:53:36 +08:00
1b4a879eda Merge pull request #182 from jroam/master 2019-06-14 23:09:33 +08:00
c5aa493d24 README updates 2019-06-14 20:42:15 +08:00
a88363e34c Merge branch '测试glist' 2019-06-14 14:58:33 +08:00
0f1261d0e3 完成glist测试率 2019-06-14 14:58:00 +08:00
c41d11df9f Merge pull request #25 from gogf/master
fix更新
2019-06-14 10:50:05 +08:00
1e680c7a8b fix issue in gcache 2019-06-14 10:44:56 +08:00
e981143ead fix issue in gspath 2019-06-14 10:35:12 +08:00
4de6881c89 Merge pull request #24 from gogf/master
日常更新
2019-06-14 10:00:18 +08:00
46c42ec249 add unit test cases for cmdenv/gcmd 2019-06-13 22:58:58 +08:00
d068c1418e Merge branch 'master' into develop 2019-06-13 22:40:20 +08:00
41db3a32f4 Merge pull request #180 from jroam/master 2019-06-13 22:37:19 +08:00
b3a00becf3 sort_int恢复成主库代码 2019-06-13 22:18:45 +08:00
d1d8cd8482 用gofmt格式化代码 2019-06-13 21:44:14 +08:00
1228907d59 comment updates for internal 2019-06-13 21:14:46 +08:00
57b54414d6 merge master 2019-06-13 21:00:14 +08:00
548a0c47af README updates 2019-06-13 20:52:36 +08:00
4934564b7b Merge pull request #179 from hailaz/master 2019-06-13 20:32:07 +08:00
7348d14fef shut error printing in unit test cases for gcfg/gview 2019-06-13 20:29:40 +08:00
9cddb7ed9a improve unit test cases for gparser 2019-06-13 20:21:18 +08:00
8c84de3f73 improve unit test cases for gparser 2019-06-13 20:20:43 +08:00
96529a4c1c update unit test cases for gjson/gcfg/gcompress/gparser 2019-06-13 19:41:43 +08:00
c7a729fe06 Merge branch 'master' of https://github.com/gogf/gf 2019-06-13 19:29:19 +08:00
23d404f681 add glog.Expose for glog; add Trim operation for glog.Logger.Write; fix issue in gjson.Remove for slice 2019-06-13 19:29:09 +08:00
887aeee2d4 Merge pull request #178 from piaohao/master 2019-06-13 19:09:55 +08:00
622dbfda31 完成garray测试覆盖率,达90%. 2019-06-13 17:02:30 +08:00
80cf3e833b 非test文件 恢复 原样 2019-06-13 12:41:20 +08:00
d305d25935 gparse测试用例 2019-06-13 12:30:35 +08:00
81502cfb6d gjson,gredis测试用例编写 2019-06-13 11:58:43 +08:00
5950a3fcc3 gpool unit test. 2019-06-13 11:42:51 +08:00
55f5e6d7aa improve gcharset 2019-06-12 23:50:37 +08:00
e2070e785c Merge pull request #23 from gogf/master
bug更新
2019-06-12 22:58:08 +08:00
905d5abed6 Merge pull request #171 from hailaz/master 2019-06-12 22:56:39 +08:00
211e06d04d Merge branch 'master' of https://github.com/gogf/gf into gogf-master 2019-06-12 22:56:23 +08:00
7aaf9e9228 fix issue in garray.Contains/New*ArrayFromCopy 2019-06-12 22:45:13 +08:00
a901e7177c 添加garray测试未完成 2019-06-12 22:37:17 +08:00
afc2bcfb28 gjson测试用例完善 2019-06-12 22:14:31 +08:00
8da204fbd8 remove test file of gchan 2019-06-12 21:10:49 +08:00
5aa3212fe1 gofmt geg/third 2019-06-12 21:06:57 +08:00
17d49510c4 add example for gcharset; fix issue in gxml 2019-06-12 21:02:00 +08:00
4af8ae1470 Merge branch 'master' of https://github.com/gogf/gf 2019-06-12 20:50:27 +08:00
3a5c660693 refract gcharset 2019-06-12 20:49:40 +08:00
9e65100a06 gcompress,gcfg,gparser模块测试用例编写 2019-06-12 19:22:02 +08:00
399e47c548 修改测试 2019-06-12 18:14:54 +08:00
abdf8e696c gtype unit test use AssertEQ replace Assert. 2019-06-12 17:52:30 +08:00
4a2e217625 Merge pull request #175 from 2892931976/gf-chj 2019-06-12 17:47:54 +08:00
52d0280137 Merge pull request #157 from wenzi1/master 2019-06-12 17:42:11 +08:00
5be9765eb7 add TODO for gfile.Search 2019-06-12 17:40:50 +08:00
cdb9488752 edit tests 2019-06-12 17:14:08 +08:00
0388113870 Merge branch 'master' of https://github.com/gogf/gf into gogf-master 2019-06-12 17:04:11 +08:00
c6dfb4d4f8 add some test 2019-06-12 16:52:02 +08:00
4a40b58b63 框架中增加字符集转换的标准库 2019-06-12 12:12:00 +08:00
407068a0bf add ghash basic test 2019-06-12 11:33:30 +08:00
165330ec68 gchan unit test 2019-06-12 11:21:10 +08:00
6e7d08fbfb fix issue in garray.Search 2019-06-12 10:11:54 +08:00
1ae77f56e5 添加garray测试代码 2019-06-11 23:32:08 +08:00
fccac04980 donator updates 2019-06-11 21:19:32 +08:00
469f9c7ce5 comment update for gflock 2019-06-11 21:14:48 +08:00
d6d37248f6 框架中增加字符集转换的标准库 2019-06-11 21:09:03 +08:00
cb1084b770 Merge branch 'master' of https://github.com/gogf/gf into develop 2019-06-11 20:59:17 +08:00
e6d4459992 comment update for gcache 2019-06-11 20:57:43 +08:00
1afb5a4bc5 框架中增加字符集转换的标准库 2019-06-11 19:41:26 +08:00
c124f172b2 Merge pull request #170 from piaohao/master 2019-06-11 19:32:57 +08:00
dbd4a7c1d4 框架中增加字符集转换的标准库 2019-06-11 19:28:24 +08:00
a4d30ef206 Merge branch 'qiangg_comment' into develop 2019-06-11 18:40:56 +08:00
0a616173ef add hash function for gview.ParseContent to improve performance in consurrent usage 2019-06-11 18:39:54 +08:00
597f210f85 去掉自动版本号 2019-06-11 18:11:47 +08:00
24bd83feb0 添加一些garray测试代码。 2019-06-11 18:11:17 +08:00
3855786905 gtype unit test 2019-06-11 17:49:29 +08:00
1f670a1ab2 gspath模块及gview模块测试用例编写 2019-06-11 17:49:05 +08:00
d97fda794c 框架中增加字符集转换的标准库 2019-06-11 17:25:30 +08:00
c034d25299 gspath模块及gview模块测试用例编写 2019-06-11 17:24:26 +08:00
dd6152fe8a 使用encoding库做字符集转换 2019-06-11 16:03:09 +08:00
485fe572ff 完成gset模块测试覆盖率90%以上。 2019-06-11 10:51:00 +08:00
15bf5d9a4d Merge pull request #2 from gogf/master
同步主线
2019-06-11 10:37:53 +08:00
08aa7c4e4c comment update for gcache 2019-06-10 23:54:40 +08:00
442c658be0 增加gset的测试支持
覆盖率达到85.1%
2019-06-10 23:38:19 +08:00
5aa8ce1c6b Merge branch 'master' into qiangg_comment 2019-06-10 23:09:32 +08:00
ad8ece68c6 Merge pull request #166 from zhongdalu/master 2019-06-10 23:03:27 +08:00
74525ba8f7 Merge pull request #164 from goflyfox/master
add gregex,gaes,gcrc32 test
2019-06-10 23:01:26 +08:00
46d46afaaf add gaes test:update assert type 2019-06-10 22:42:46 +08:00
13eb1150a5 fix issue in gfile.MainPkgPath 2019-06-10 21:32:40 +08:00
f98db6d21c comments for gcache update 2019-06-10 21:23:49 +08:00
zdl
c695dfd92e delete go version 2019-06-10 20:52:06 +08:00
zdl
ffd78d76e1 添加 genv 测试 1 2019-06-10 20:44:30 +08:00
zdl
e479c41667 添加 genv 测试 2019-06-10 20:24:11 +08:00
9f8c481992 Merge pull request #167 from piaohao/master 2019-06-10 20:21:41 +08:00
3320d12994 gcache测试用例完善 2019-06-10 19:58:00 +08:00
be6f522cf3 add gaes test4:optimize Encrypt Decrypt test 2019-06-10 19:53:07 +08:00
zdl
b0b6871bbb 添加 genv 测试 2019-06-10 19:50:45 +08:00
59ae6217cd add gaes test3: add encrypt assert content 2019-06-10 19:37:02 +08:00
334cd7ad51 add gregex test 2019-06-10 18:59:42 +08:00
501c3680d9 add gaes test 2019-06-10 17:26:32 +08:00
ebcc81c1ee Merge pull request #19 from gogf/master
日常更新
2019-06-10 16:12:51 +08:00
6814372a89 add gcrc test2 2019-06-10 15:55:43 +08:00
zdl
d5d14b7efc test gset TestSet_New 2019-06-10 15:49:43 +08:00
3a72686774 add gcrc test 2019-06-10 15:05:11 +08:00
aa73c5ed53 version updates 2019-06-10 09:01:19 +08:00
cc0a385c22 RELEASE updates 2019-06-09 11:56:58 +08:00
0b8c9713e6 improve grpool 2019-06-09 10:37:35 +08:00
e400a94ffb improving grpool 2019-06-09 10:33:16 +08:00
136ad3b0b5 add more unit test cases for hook feature of ghttp.Server 2019-06-08 19:38:46 +08:00
f9826104d8 improve gconv 2019-06-08 19:25:20 +08:00
c0c97b76fb update lead string position for glog 2019-06-07 23:00:34 +08:00
a908e4d4b1 fix issue in session initialization for ghttp.Server 2019-06-07 22:27:37 +08:00
e5c255200c 框架中增加字符集转换的标准库 2019-06-06 15:19:18 +08:00
00db4f5ed9 优先使用标准库的左字符集转换,标准库不支持的使用mahonia做转换 2019-06-06 15:11:32 +08:00
29ead3ff3e Merge pull request #4 from gogf/master
update
2019-06-06 14:52:12 +08:00
055074246e add TryLockFunc/TryRLockFunc for gmlock.Mutex 2019-06-05 23:50:24 +08:00
4bbe51fb4b add Close function for gtcp.Server/gudp.Server 2019-06-05 22:12:22 +08:00
841224372b update comment for gmlock 2019-06-05 21:58:27 +08:00
d1f0fa1a47 fix issue in concurrent safety for gview.Parse; rename glog.*fln functions to glog.*f 2019-06-05 20:22:57 +08:00
6fecf8bb01 update example 2019-06-05 18:41:11 +08:00
6d44f02a38 fix issue in internal/empty 2019-06-05 18:40:26 +08:00
1458e486d7 v1.6.17 2019-06-04 23:39:33 +08:00
dc29822e69 improve gtcp; add tls support for gtcp 2019-06-04 23:33:46 +08:00
dae7722da1 Merge branch 'master' into develop 2019-06-04 18:35:06 +08:00
5d3c154b45 improve gtcp 2019-06-04 18:26:32 +08:00
00a8ef63b6 improve gtcp 2019-06-03 23:53:59 +08:00
6ac437a3a5 merge master 2019-06-03 21:33:16 +08:00
fe74818a37 Merge pull request #3 from gogf/master
update
2019-05-29 16:32:20 +08:00
cf324c5d8c Merge pull request #2 from gogf/master
update
2019-05-23 13:05:52 +08:00
48deaa5f57 improving package feature for gtcp 2019-05-20 08:59:16 +08:00
a4fa163333 Merge pull request #1 from gogf/master
update
2019-05-16 15:08:08 +08:00
428d7ec94a Merge pull request #1 from gogf/master
同步主库
2019-04-10 09:54:16 +08:00
416 changed files with 240382 additions and 89507 deletions

View File

@ -13,6 +13,7 @@
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00
|[arden](https://github.com/arden)|alipay|¥10.00
|潘兄|wechat|¥100.00
|Fly的狐狸|wechat|¥100.00
|土豆相公|alipay|¥66.60
|上海金保证网络科技|bank|¥2000.00

View File

@ -11,11 +11,7 @@
</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.
`GF(GoFrame)` is a modular, full-featured and production-ready 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

View File

@ -11,7 +11,7 @@
</div>
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
`GF(Go Frame)`是一款模块化、高性能、生产级Go应用开发框架。提供了常用的核心开发组件缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
并发安全容器等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、服务注册、配置管理、模板引擎等等支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。

View File

@ -1,3 +1,63 @@
# `v1.7.0`
## 新功能/改进
1. 重构改进`glog`模块:
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
- 增加日志内容的异步输出特性https://goframe.org/os/glog/async
- 增加日志输出内容的`Json`格式支持https://goframe.org/os/glog/json
- 增加`Flags`额外特性支持包括文件行号打印、自定义时间格式、异步输出等特性控制https://goframe.org/os/glog/flags
- 增加`Writer`接口支持,便于开发者进行自定义的日志功能扩展,或者与第三方服务/模块对接集成https://goframe.org/os/glog/writer
- 修改`SetStdPrint`方法名为`SetStdoutPrint`
- 修改链式方法`StdPrint`方法名为`Stdout`
- 标记淘汰`*fln`日志输出方法,`*f`方法支持自动的换行输出
- 新增更多的链式方法支持https://goframe.org/os/glog/chain
1. 重构改进`gmap`模块:
- 增加更多数据格式支持:`HashMap`/`ListMap`/`TreeMap`
- 简化类型名称,如`gmap.StringInterfaceMap`简化为`gmap.StrAnyMap`
- 改进`Map/Keys/Values`方法以提高性能
- 修改`BatchSet`/`BatchRemove`方法名为`Sets`/`Removes`
- 新增更多功能方法支持https://goframe.org/container/gmap/index
1. 改进`gtime`时间模块:
- 增加并完善更多的类`PHP`时间格式支持
- 新增更多功能方法,如`FormatTo`/`LayoutTo`等等
- 详见开发文档https://goframe.org/os/gtime/index
1. 改进`gdb`数据库模块:
- 增加对继承结构体的数据转换支持https://goframe.org/database/gdb/senior
- 新增`GetLastSql`方法用以在调试模式下获取最近一条执行的SQL语句
- 其他的细节处理改进
1. 改进`gtcp`通信模块:
- 完善处理细节,提高通信性能;
- 增加`TLS`服务端/客户端通信支持https://goframe.org/net/gtcp/tls
- 增加简单协议支持,便于开发者封包/解包,并解决粘包/半包问题https://goframe.org/net/gtcp/conn/pkg
- TCP服务端增加`Close`方法
- 更多细节查看开发文档https://goframe.org/net/gtcp/index
1. 改进`gconv`类型转换模块
- 修改`gconv.TimeDuration`转换方法名称为`gconv.Duration`
- 新增`gconv.StructDeep`及`gconv.MapDeep`方法,支持递归转换
- 详见开发文档https://goframe.org/util/gconv/struct
1. 改进`ghttp`模块:
- 日志输出增加`http/https`字段https://goframe.org/net/ghttp/logs
- 新增`ghttp.Server.SetKeepAlive`设置方法,用以开启/关闭`KeepAlive`特性
- 增加`ghttp.Request.GetUrl`方法用以获取当前完整的URL请求地址
- `ghttp.Client`客户端支持开发者自定义`Transport`属性,`ghttp.Client.Post`方法支持`浏览器模式`https://goframe.org/net/ghttp/client
1. 新增`gtree`树形数据结构容器支持https://goframe.org/container/gtree/index
1. 改进`gudp`通信模块具体请参考开发文档https://goframe.org/net/gudp/index
1. 改进`gcfg`配置管理模块,所有`Get*`方法增加默认值支持https://goframe.org/os/gcfg/index
1. `gredis`模块新增`DoVar`/`ReceiveVar`方法以便于开发者对执行结果进行灵活的数据格式转换https://goframe.org/database/gredis/index
1. `gcache`模块`BatchSet`/`BatchRemove`方法名修改为`Sets`/`Removes`
1. 改进`gjson`/`gparser`模块增加更多方法https://goframe.org/encoding/gjson/index
1. 改进`gfile.MainPkgPath`方法,以支持不同平台的开发环境;
1. 改进`grpool`协程池模块提高执行性能https://goframe.org/os/grpool/index
1. 改进`TryCatch`方法,当开发者不传递`Catch`参数时,默认抑制并忽略错误的处理
1. 改进`gmlock`模块,增加`TryLockFunc`/`TryRLockFunc`方法,并且为`gmlock.Mutex`高级互斥锁对象增加`TryLockFunc`/`TryRLockFunc`方法
1. 去除`gvar.VarRead`接口类型支持
## Bug Fix
1. 解决`gdb`模块与其他第三方`ORM`模块同时使用的冲突;
1. 修复`gcron.AddOnce`方法的细节逻辑问题;
1. 修复内部`empty`模块的`IsEmpty`方法对结构体属性的空校验错误;
1. 修复`gview`模板引擎的并发安全问题;
1. 修复`ghttp.Server`的SESSION初始化过期时间问题
# `v1.6.0` (2019-04-09)
## 新功能/改进

View File

@ -47,7 +47,7 @@
1. gtimer增加DelayAdd*方法返回Entry对象以便DelayAdd*的定时任务也能进行状态控制gcron同理需要改进
1. 改进gdb对pgsql/mssql/oracle的支持使用方法覆盖的方式改进操作而不是完全依靠正则替换的方式
1. gdb的Cache缓存功能增加可自定义缓存接口以便支持外部缓存功能缓存接口可以通过io.ReadWriter接口实现
1. grpool增加支持阻塞添加任务接口

View File

@ -22,14 +22,14 @@ type IntArray struct {
}
// NewIntArray creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArray(unsafe...bool) *IntArray {
return NewIntArraySize(0, 0, unsafe...)
}
// NewIntArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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))
@ -110,7 +110,7 @@ func (a *IntArray) Sum() (sum int) {
}
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *IntArray) Sort(reverse...bool) *IntArray {
a.mu.Lock()

View File

@ -22,7 +22,7 @@ type Array struct {
}
// New creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
@ -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 in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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

@ -23,14 +23,14 @@ type StringArray struct {
}
// NewStringArray creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArray(unsafe...bool) *StringArray {
return NewStringArraySize(0, 0, unsafe...)
}
// NewStringArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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 in un-concurrent-safety,
// The parameter <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))
@ -111,7 +111,7 @@ func (a *StringArray) Sum() (sum int) {
}
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *StringArray) Sort(reverse...bool) *StringArray {
a.mu.Lock()

View File

@ -26,14 +26,14 @@ type SortedIntArray struct {
}
// NewSortedIntArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArray(unsafe...bool) *SortedIntArray {
return NewSortedIntArraySize(0, unsafe...)
}
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <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 {
@ -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 in un-concurrent-safety,
// The parameter <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,15 +63,12 @@ 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 in un-concurrent-safety,
// The parameter <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))
copy(newArray, array)
return &SortedIntArray{
mu : rwmutex.New(unsafe...),
array : newArray,
}
return NewSortedIntArrayFrom(newArray, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
@ -84,7 +81,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
}
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
@ -280,17 +277,23 @@ func (a *SortedIntArray) Slice() []int {
// Contains checks whether a value exists in the array.
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) == 0
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedIntArray) Search(value int) (index int) {
index, _ = a.binSearch(value, true)
return
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If <result> equals to 0, it means the value at <index> is equals to <value>.
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2

View File

@ -26,8 +26,8 @@ type SortedArray struct {
}
// NewSortedArray creates and returns an empty sorted 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,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety, which is false in default.
// The parameter <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;
@ -36,19 +36,19 @@ func NewSortedArray(comparator func(v1, v2 interface{}) int, unsafe...bool) *Sor
}
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
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),
mu : rwmutex.New(unsafe...),
unique : gtype.NewBool(),
array : make([]interface{}, 0, cap),
comparator : comparator,
}
}
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, unsafe...)
@ -60,15 +60,12 @@ func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{})
}
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
func NewSortedArrayFromCopy(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return &SortedArray{
mu : rwmutex.New(unsafe...),
array : newArray,
}
return NewSortedArrayFrom(newArray, comparator, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
@ -83,7 +80,7 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
}
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
@ -281,17 +278,23 @@ func (a *SortedArray) Slice() []interface{} {
// Contains checks whether a value exists in the array.
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) == 0
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedArray) Search(value interface{}) (index int) {
index, _ = a.binSearch(value, true)
return
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If <result> equals to 0, it means the value at <index> is equals to <value>.
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
if len(a.array) == 0 {
return -1, -2

View File

@ -27,14 +27,14 @@ type SortedStringArray struct {
}
// NewSortedStringArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArray(unsafe...bool) *SortedStringArray {
return NewSortedStringArraySize(0, unsafe...)
}
// NewSortedStringArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <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 {
@ -48,7 +48,7 @@ func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
}
// NewSortedStringArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// The parameter <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,15 +58,12 @@ 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 in un-concurrent-safety,
// The parameter <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))
copy(newArray, array)
return &SortedStringArray{
mu : rwmutex.New(unsafe...),
array : newArray,
}
return NewSortedStringArrayFrom(newArray, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
@ -79,7 +76,7 @@ func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
}
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedStringArray) Sort() *SortedStringArray {
a.mu.Lock()
@ -275,17 +272,23 @@ func (a *SortedStringArray) Slice() []string {
// Contains checks whether a value exists in the array.
func (a *SortedStringArray) Contains(value string) bool {
return a.Search(value) == 0
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedStringArray) Search(value string) (index int) {
index, _ = a.binSearch(value, true)
return
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If <result> equals to 0, it means the value at <index> is equals to <value>.
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2

View File

@ -9,35 +9,35 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"testing"
"github.com/gogf/gf/g/container/garray"
"testing"
)
var (
sortedIntArray = garray.NewSortedIntArray()
sortedIntArray = garray.NewSortedIntArray()
)
func BenchmarkSortedIntArray_Add(b *testing.B) {
b.N = 1000
for i := 0; i < b.N; i++ {
sortedIntArray.Add(i)
}
b.N = 1000
for i := 0; i < b.N; i++ {
sortedIntArray.Add(i)
}
}
func BenchmarkSortedIntArray_Search(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.Search(i)
}
for i := 0; i < b.N; i++ {
sortedIntArray.Search(i)
}
}
func BenchmarkSortedIntArray_PopLeft(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
}
func BenchmarkSortedIntArray_PopRight(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
}

View File

@ -9,75 +9,86 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"strings"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"strings"
"testing"
)
func Test_IntArray_Unique(t *testing.T) {
expect := []int{1, 2, 3, 4, 5, 6}
array := garray.NewIntArray()
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
array.Unique()
gtest.Assert(array.Slice(), expect)
expect := []int{1, 2, 3, 4, 5, 6}
array := garray.NewIntArray()
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
array.Unique()
gtest.Assert(array.Slice(), expect)
}
func Test_SortedIntArray1(t *testing.T) {
expect := []int{0,1,2,3,4,5,6,7,8,9,10}
array := garray.NewSortedIntArray()
for i := 10; i > -1; i-- {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 10; i > -1; i-- {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedIntArray2(t *testing.T) {
expect := []int{0,1,2,3,4,5,6,7,8,9,10}
array := garray.NewSortedIntArray()
for i := 0; i <= 10; i++ {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 0; i <= 10; i++ {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedStringArray1(t *testing.T) {
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
array := garray.NewSortedStringArray()
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedStringArray()
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedStringArray2(t *testing.T) {
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
array := garray.NewSortedStringArray()
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedStringArray()
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedArray1(t *testing.T) {
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedArray2(t *testing.T) {
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
func TestNewFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
array1 := garray.NewFromCopy(a1)
gtest.AssertIN(array1.PopRands(2), a1)
gtest.Assert(len(array1.PopRands(1)), 1)
gtest.Assert(len(array1.PopRands(9)), 3)
})
}

View File

@ -9,193 +9,602 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_IntArray_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestIntArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []int{0, 1, 2, 3}
expect2 := []int{3, 2, 1, 0}
array := garray.NewIntArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
gtest.Case(t, func() {
expect1 := []int{0, 1, 2, 3}
expect2 := []int{3, 2, 1, 0}
array := garray.NewIntArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
}
func TestIntArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []int{1, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
})
gtest.Case(t, func() {
expect := []int{1, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
})
}
func TestIntArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []int{1, 2})
})
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []int{1, 2})
})
}
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []int{0,1,2,3,4,5,6}
value2 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(value1)
array2 := garray.NewIntArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []int{0,1})
gtest.Assert(array1.Slice(), []int{2,3,4,5,6})
gtest.Assert(array1.PopRights(2), []int{5,6})
gtest.Assert(array1.Slice(), []int{2,3,4})
gtest.Assert(array1.PopRights(20), []int{2,3,4})
gtest.Assert(array1.Slice(), []int{})
gtest.Assert(array2.PopLefts(20), []int{0,1,2,3,4,5,6})
gtest.Assert(array2.Slice(), []int{})
})
gtest.Case(t, func() {
value1 := []int{0, 1, 2, 3, 4, 5, 6}
value2 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(value1)
array2 := garray.NewIntArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []int{0, 1})
gtest.Assert(array1.Slice(), []int{2, 3, 4, 5, 6})
gtest.Assert(array1.PopRights(2), []int{5, 6})
gtest.Assert(array1.Slice(), []int{2, 3, 4})
gtest.Assert(array1.PopRights(20), []int{2, 3, 4})
gtest.Assert(array1.Slice(), []int{})
gtest.Assert(array2.PopLefts(20), []int{0, 1, 2, 3, 4, 5, 6})
gtest.Assert(array2.Slice(), []int{})
})
}
func TestIntArray_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []int{0})
gtest.Assert(array1.Range(1, 2), []int{1})
gtest.Assert(array1.Range(0, 2), []int{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
gtest.Case(t, func() {
value1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []int{0})
gtest.Assert(array1.Range(1, 2), []int{1})
gtest.Assert(array1.Range(0, 2), []int{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestIntArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3}
a2 := []int{4, 5, 6, 7}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []int{0,1,2,3,4,5,6,7})
})
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3}
a2 := []int{4, 5, 6, 7}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
})
}
func TestIntArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0}
a2 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0,100,100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100,100})
})
gtest.Case(t, func() {
a1 := []int{0}
a2 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100})
})
}
func TestIntArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1,2,3,4,5}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []int{1,2})
gtest.Assert(chunks[1], []int{3,4})
gtest.Assert(chunks[2], []int{5})
})
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []int{1, 2})
gtest.Assert(chunks[1], []int{3, 4})
gtest.Assert(chunks[2], []int{5})
})
}
func TestIntArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0,1,1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1,0,1,1})
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1,0,1,1})
})
gtest.Case(t, func() {
a1 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0, 1, 1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1, 0, 1, 1})
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1, 0, 1, 1})
})
}
func TestIntArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []int{0,1})
gtest.Assert(array1.SubSlice(2, 2), []int{2,3})
gtest.Assert(array1.SubSlice(5, 8), []int{5,6})
})
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []int{0, 1})
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
gtest.Assert(array1.SubSlice(5, 8), []int{5, 6})
})
}
func TestIntArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
gtest.AssertIN(array1.Rand(), a1)
})
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
gtest.AssertIN(array1.Rand(), a1)
})
}
func TestIntArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
gtest.AssertIN(array.PopRands(2), a1)
})
gtest.Case(t, func() {
a1 := []int{100, 200, 300, 400, 500, 600}
array := garray.NewIntArrayFrom(a1)
ns1 := array.PopRands(2)
gtest.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
gtest.AssertIN(len(ns1), 2)
ns2 := array.PopRands(7)
gtest.AssertIN(len(ns2), 6)
gtest.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
})
}
func TestIntArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestIntArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []int{6,5,4,3,2,1,0})
})
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
})
}
func TestIntArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestNewSortedIntArrayFrom(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 3, 2, 1, 4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1, true)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestNewSortedIntArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 5, 2, 1, 4, 3, 6}
array1 := garray.NewSortedIntArrayFromCopy(a1, false)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestSortedIntArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3}
a2 := []int{4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.SetArray(a2)
gtest.Assert(array2.Len(), 3)
gtest.Assert(array2.Search(3), -1)
gtest.Assert(array2.Search(5), 1)
gtest.Assert(array2.Search(6), 2)
})
}
func TestSortedIntArray_Sort(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 3, 2, 1}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.Sort()
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []int{0, 1, 2, 3})
})
}
func TestSortedIntArray_Get(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
gtest.Assert(array1.Get(0), 0)
gtest.Assert(array1.Get(1), 1)
gtest.Assert(array1.Get(3), 5)
})
}
func TestSortedIntArray_Remove(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.Remove(2)
gtest.Assert(i1, 3)
gtest.Assert(array1.Search(5), 2)
// 再次删除剩下的数组中的第一个
i2 := array1.Remove(0)
gtest.Assert(i2, 0)
gtest.Assert(array1.Search(5), 1)
a2 := []int{1, 3, 4}
array2 := garray.NewSortedIntArrayFrom(a2)
i3 := array2.Remove(1)
gtest.Assert(array2.Search(1), 0)
gtest.Assert(i3, 3)
i3 = array2.Remove(1)
gtest.Assert(array2.Search(4), -1)
gtest.Assert(i3, 4)
})
}
func TestSortedIntArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopLeft()
gtest.Assert(i1, 1)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(1), -1)
})
}
func TestSortedIntArray_PopRight(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRight()
gtest.Assert(i1, 5)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(5), -1)
})
}
func TestSortedIntArray_PopRand(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRand()
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(i1), -1)
gtest.AssertIN(i1, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopRands(2)
gtest.Assert(array1.Len(), 2)
gtest.AssertIN(ns1, []int{1, 3, 5, 2})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopRands(5)
gtest.Assert(array2.Len(), 0)
gtest.Assert(len(ns2), 4)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopLefts(2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(ns1, []int{1, 2})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopLefts(5)
gtest.Assert(array2.Len(), 0)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopRights(2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(ns1, []int{3, 5})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopRights(5)
gtest.Assert(array2.Len(), 0)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_Range(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5, 2, 6, 7}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Range(1, 4)
gtest.Assert(len(ns1), 3)
gtest.Assert(ns1, []int{2, 3, 5})
ns2 := array1.Range(5, 4)
gtest.Assert(len(ns2), 0)
ns3 := array1.Range(-1, 4)
gtest.Assert(len(ns3), 4)
nsl := array1.Range(5, 8)
gtest.Assert(len(nsl), 1)
})
}
func TestSortedIntArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
n1 := array1.Sum()
gtest.Assert(n1, 9)
})
}
func TestSortedIntArray_Contains(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
//gtest.Assert(array1.Contains(3),true) //todo 这一行应该返回true
gtest.Assert(array1.Contains(4), false)
})
}
func TestSortedIntArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array2.Len(), 3)
gtest.Assert(array2, array1)
})
}
func TestSortedIntArray_Clear(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedIntArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
ns2 := array1.Chunk(-1)
t.Log(ns1)
gtest.Assert(len(ns1), 3)
gtest.Assert(ns1[0], []int{1, 2})
gtest.Assert(ns1[2], []int{5})
gtest.Assert(len(ns2), 0)
})
}
func TestSortedIntArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.SubSlice(1, 2)
gtest.Assert(len(ns1), 2)
gtest.Assert(ns1, []int{2, 3})
ns2 := array1.SubSlice(7, 2)
gtest.Assert(len(ns2), 0)
ns3 := array1.SubSlice(3, 5)
gtest.Assert(len(ns3), 2)
gtest.Assert(ns3, []int{4, 5})
ns4 := array1.SubSlice(3, 1)
gtest.Assert(len(ns4), 1)
gtest.Assert(ns4, []int{4})
})
}
func TestSortedIntArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rand() //按每几个元素切成一个数组
gtest.AssertIN(ns1, a1)
})
}
func TestSortedIntArray_Rands(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rands(2) //按每几个元素切成一个数组
gtest.AssertIN(ns1, a1)
gtest.Assert(len(ns1), 2)
ns2 := array1.Rands(6) //按每几个元素切成一个数组
gtest.AssertIN(ns2, a1)
gtest.Assert(len(ns2), 5)
})
}
func TestSortedIntArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5, 3}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.CountValues() //按每几个元素切成一个数组
gtest.Assert(len(ns1), 5)
gtest.Assert(ns1[2], 1)
gtest.Assert(ns1[3], 2)
})
}
func TestSortedIntArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 4, 5, 3}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.SetUnique(true)
gtest.Assert(array1.Len(), 5)
gtest.Assert(array1, []int{1, 2, 3, 4, 5})
})
}
func TestIntArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
a2 := []int{6, 7}
array1 := garray.NewIntArrayFrom(a1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1, []int{6, 7})
})
}
func TestIntArray_Replace(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
a2 := []int{6, 7}
a3 := []int{9, 10, 11, 12, 13}
array1 := garray.NewIntArrayFrom(a1)
array1.Replace(a2)
gtest.Assert(array1, []int{6, 7, 3, 5})
array1.Replace(a3)
gtest.Assert(array1, []int{9, 10, 11, 12})
})
}
func TestIntArray_Clear(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
})
}
func TestIntArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
})
}
func TestArray_Get(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Get(2), 3)
gtest.Assert(array1.Len(), 4)
})
}
func TestIntArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Sum(), 11)
})
}
func TestIntArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5, 3}
array1 := garray.NewIntArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 4)
gtest.Assert(m1[1], 1)
gtest.Assert(m1[3], 2)
})
}
func TestNewIntArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5, 3}
array1 := garray.NewIntArrayFromCopy(a1)
gtest.Assert(array1.Len(), 5)
gtest.Assert(array1, a1)
})
}
func TestIntArray_Remove(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1, 2, 3, 5, 4}
array1 := garray.NewIntArrayFrom(a1)
n1 := array1.Remove(1)
gtest.Assert(n1, 2)
gtest.Assert(array1.Len(), 4)
n1 = array1.Remove(0)
gtest.Assert(n1, 1)
gtest.Assert(array1.Len(), 3)
n1 = array1.Remove(2)
gtest.Assert(n1, 4)
gtest.Assert(array1.Len(), 2)
})
}

View File

@ -9,196 +9,670 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"strings"
"testing"
)
func Test_Array_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []interface{}{0, 1, 2, 3}
expect2 := []interface{}{3, 2, 1, 0}
array := garray.NewArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) < v2.(int)
})
gtest.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) > v2.(int)
})
gtest.Assert(array.Slice(), expect2)
})
gtest.Case(t, func() {
expect1 := []interface{}{0, 1, 2, 3}
expect2 := []interface{}{3, 2, 1, 0}
array := garray.NewArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) < v2.(int)
})
gtest.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) > v2.(int)
})
gtest.Assert(array.Slice(), expect2)
})
}
func TestArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{1, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
})
gtest.Case(t, func() {
expect := []interface{}{1, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
})
}
func TestArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []interface{}{1, 2})
})
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []interface{}{1, 2})
})
}
func TestArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
gtest.AssertIN(array.PopRands(2), a1)
})
gtest.Case(t, func() {
a1 := []interface{}{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
gtest.AssertIN(array.PopRands(2), a1)
})
}
func TestArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []interface{}{0,1,2,3,4,5,6}
value2 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{0,1})
gtest.Assert(array1.Slice(), []interface{}{2,3,4,5,6})
gtest.Assert(array1.PopRights(2), []interface{}{5,6})
gtest.Assert(array1.Slice(), []interface{}{2,3,4})
gtest.Assert(array1.PopRights(20), []interface{}{2,3,4})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{0,1,2,3,4,5,6})
gtest.Assert(array2.Slice(), []interface{}{})
})
gtest.Case(t, func() {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
value2 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{0, 1})
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
gtest.Assert(array1.PopRights(2), []interface{}{5, 6})
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4})
gtest.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
gtest.Assert(array2.Slice(), []interface{}{})
})
}
func TestArray_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{0})
gtest.Assert(array1.Range(1, 2), []interface{}{1})
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
gtest.Case(t, func() {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{0})
gtest.Assert(array1.Range(1, 2), []interface{}{1})
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{4, 5, 6, 7}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0,1,2,3,4,5,6,7})
})
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{4, 5, 6, 7}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
})
}
func TestArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0}
a2 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0,100,100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100,100})
})
gtest.Case(t, func() {
a1 := []interface{}{0}
a2 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100})
})
}
func TestArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{1,2,3,4,5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []interface{}{1,2})
gtest.Assert(chunks[1], []interface{}{3,4})
gtest.Assert(chunks[2], []interface{}{5})
})
gtest.Case(t, func() {
a1 := []interface{}{1, 2, 3, 4, 5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []interface{}{1, 2})
gtest.Assert(chunks[1], []interface{}{3, 4})
gtest.Assert(chunks[2], []interface{}{5})
})
}
func TestArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0,1,1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1,0,1,1})
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1,0,1,1})
})
gtest.Case(t, func() {
a1 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
})
}
func TestArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0,1})
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2,3})
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5,6})
})
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
})
}
func TestArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
})
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
})
}
func TestArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []interface{}{6,5,4,3,2,1,0})
})
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
})
}
func TestArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestArray_Replace(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"m", "n", "p", "z", "x", "y", "d", "u"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Replace(a2)
gtest.Assert(array2.Len(), 7)
gtest.Assert(array2.Contains("b"), true)
gtest.Assert(array2.Contains(4), true)
gtest.Assert(array2.Contains("v"), false)
array3 := array1.Replace(a3)
gtest.Assert(array3.Len(), 7)
gtest.Assert(array3.Contains(4), false)
gtest.Assert(array3.Contains("p"), true)
gtest.Assert(array3.Contains("u"), false)
})
}
func TestArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
array1 := garray.NewArrayFrom(a1)
array1 = array1.SetArray(a2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("b"), true)
gtest.Assert(array1.Contains("5"), false)
})
}
func TestArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"a", "1", "2"}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
array3 := garray.NewArrayFrom(a3)
gtest.Assert(array1.Sum(), 6)
gtest.Assert(array2.Sum(), 0)
gtest.Assert(array3.Sum(), 3)
})
}
func TestArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1.Len(), 4)
gtest.Assert(array2.Sum(), 6)
gtest.AssertEQ(array1, array2)
})
}
func TestArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "b", "c", "d", "e", "d"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.CountValues()
gtest.Assert(len(array2), 5)
gtest.Assert(array2["b"], 1)
gtest.Assert(array2["d"], 2)
})
}
func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"h", "j", "i", "k"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
func2 := func(v1, v2 interface{}) int {
return -1
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a2, func2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []interface{}{"k", "i", "j", "h"})
})
}
func TestNewSortedArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
func2 := func(v1, v2 interface{}) int {
return -1
}
array1 := garray.NewSortedArrayFromCopy(a1, func1)
array2 := garray.NewSortedArrayFromCopy(a1, func2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(array2, []interface{}{"c", "f", "a"})
})
}
func TestSortedArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"e", "h", "g", "k"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1, []interface{}{"e", "g", "h", "k"})
})
}
func TestSortedArray_Sort(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.Sort()
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
})
}
func TestSortedArray_Get(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Get(2), "f")
gtest.Assert(array1.Get(1), "c")
})
}
func TestSortedArray_Remove(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Remove(1)
gtest.Assert(gconv.String(i1), "b")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("b"), false)
i2 := array1.Remove(0)
gtest.Assert(gconv.String(i2), "a")
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1.Contains("a"), false)
i3 := array1.Remove(1)
gtest.Assert(gconv.String(i3), "d")
gtest.Assert(array1.Len(), 1)
gtest.Assert(array1.Contains("d"), false)
})
}
func TestSortedArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopLeft()
gtest.Assert(gconv.String(i1), "a")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"b", "c", "d"})
})
}
func TestSortedArray_PopRight(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRight()
gtest.Assert(gconv.String(i1), "d")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "b", "c"})
})
}
func TestSortedArray_PopRand(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRand()
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 3)
})
}
func TestSortedArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRands(2)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 2)
i2 := array1.PopRands(3)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopLefts(2)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
gtest.Assert(array1.Len(), 4)
i2 := array1.PopLefts(5)
gtest.Assert(len(i2), 4)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRights(2)
gtest.Assert(len(i1), 2)
gtest.Assert(i1, []interface{}{"e", "f"})
gtest.Assert(array1.Len(), 4)
i2 := array1.PopRights(10)
gtest.Assert(len(i2), 4)
})
}
func TestSortedArray_Range(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Range(2, 5)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
gtest.Assert(array1.Len(), 6)
i2 := array1.Range(7, 5)
gtest.Assert(len(i2), 0)
i2 = array1.Range(-1, 2)
gtest.Assert(i2, []interface{}{"a", "b"})
i2 = array1.Range(4, 10)
gtest.Assert(len(i2), 2)
gtest.Assert(i2, []interface{}{"e", "f"})
})
}
func TestSortedArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
a2 := []interface{}{"1", "2", "3", "b", "e", "f"}
a3 := []interface{}{"4", "5", "6"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a2, func1)
array3 := garray.NewSortedArrayFrom(a3, func1)
gtest.Assert(array1.Sum(), 0)
gtest.Assert(array2.Sum(), 6)
gtest.Assert(array3.Sum(), 15)
})
}
func TestSortedArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
array1.Remove(1)
gtest.AssertNE(array1, array2)
})
}
func TestSortedArray_Clear(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Len(), 6)
array1.Clear()
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Chunk(2)
gtest.Assert(len(i1), 3)
gtest.Assert(i1[0], []interface{}{"a", "b"})
gtest.Assert(i1[2], []interface{}{"e"})
i1 = array1.Chunk(0)
gtest.Assert(len(i1), 0)
})
}
func TestSortedArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.SubSlice(2, 3)
gtest.Assert(len(i1), 3)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
i1 = array1.SubSlice(2, 6)
gtest.Assert(len(i1), 3)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
i1 = array1.SubSlice(7, 2)
gtest.Assert(len(i1), 0)
})
}
func TestSortedArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rand()
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
gtest.Assert(array1.Len(), 3)
})
}
func TestSortedArray_Rands(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rands(2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
gtest.Assert(len(i1), 2)
gtest.Assert(array1.Len(), 3)
i1 = array1.Rands(4)
gtest.Assert(len(i1), 3)
})
}
func TestSortedArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Join(","), "a,c,d")
gtest.Assert(array1.Join("."), "a.c.d")
})
}
func TestSortedArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 3)
gtest.Assert(m1["c"], 2)
gtest.Assert(m1["a"], 1)
})
}
func TestSortedArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.SetUnique(true)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "d"})
})
}

View File

@ -9,193 +9,631 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"strings"
"testing"
)
func Test_StringArray_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, "100")
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search("100"), 0)
gtest.Assert(array.Contains("100"), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains("100"), false)
array.Append("4")
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, "100")
array.InsertAfter(0, "200")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
array.InsertBefore(5, "300")
array.InsertAfter(6, "400")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
gtest.Assert(array.Clear().Len(), 0)
})
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, "100")
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search("100"), 0)
gtest.Assert(array.Contains("100"), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains("100"), false)
array.Append("4")
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, "100")
array.InsertAfter(0, "200")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
array.InsertBefore(5, "300")
array.InsertAfter(6, "400")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestStringArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []string{"0", "1", "2", "3"}
expect2 := []string{"3", "2", "1", "0"}
array := garray.NewStringArray()
for i := 3; i >= 0; i-- {
array.Append(gconv.String(i))
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
gtest.Case(t, func() {
expect1 := []string{"0", "1", "2", "3"}
expect2 := []string{"3", "2", "1", "0"}
array := garray.NewStringArray()
for i := 3; i >= 0; i-- {
array.Append(gconv.String(i))
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
}
func TestStringArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"1", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})
gtest.Case(t, func() {
expect := []string{"1", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})
}
func TestStringArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), "0")
gtest.Assert(array.PopRight(), "3")
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.Assert(array.Len(), 0)
array.PushLeft("1").PushRight("2")
gtest.Assert(array.Slice(), []string{"1", "2"})
})
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), "0")
gtest.Assert(array.PopRight(), "3")
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.Assert(array.Len(), 0)
array.PushLeft("1").PushRight("2")
gtest.Assert(array.Slice(), []string{"1", "2"})
})
}
func TestStringArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []string{"0","1","2","3","4","5","6"}
value2 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(value1)
array2 := garray.NewStringArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{"0","1"})
gtest.Assert(array1.Slice(), []interface{}{"2","3","4","5","6"})
gtest.Assert(array1.PopRights(2), []interface{}{"5","6"})
gtest.Assert(array1.Slice(), []interface{}{"2","3","4"})
gtest.Assert(array1.PopRights(20), []interface{}{"2","3","4"})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{"0","1","2","3","4","5","6"})
gtest.Assert(array2.Slice(), []interface{}{})
})
gtest.Case(t, func() {
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
value2 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(value1)
array2 := garray.NewStringArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
gtest.Assert(array1.PopRights(2), []interface{}{"5", "6"})
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
gtest.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
gtest.Assert(array2.Slice(), []interface{}{})
})
}
func TestString_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
gtest.Assert(array1.Range(-1, 10), value1)
})
gtest.Case(t, func() {
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestStringArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3"}
a2 := []string{"4", "5", "6", "7"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []string{"0","1","2","3","4","5","6","7"})
})
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3"}
a2 := []string{"4", "5", "6", "7"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
})
}
func TestStringArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0"}
a2 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0","100","100"})
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100","100"})
})
gtest.Case(t, func() {
a1 := []string{"0"}
a2 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0", "100", "100"})
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100", "100"})
s1 := array2.Fill(-1, 2, "100")
gtest.Assert(s1.Len(), 2)
})
}
func TestStringArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"1","2","3","4","5"}
array1 := garray.NewStringArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []string{"1","2"})
gtest.Assert(chunks[1], []string{"3","4"})
gtest.Assert(chunks[2], []string{"5"})
})
gtest.Case(t, func() {
a1 := []string{"1", "2", "3", "4", "5"}
array1 := garray.NewStringArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []string{"1", "2"})
gtest.Assert(chunks[1], []string{"3", "4"})
gtest.Assert(chunks[2], []string{"5"})
gtest.Assert(len(array1.Chunk(0)), 0)
})
}
func TestStringArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0","1","1"})
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1","0","1","1"})
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1","0","1","1"})
})
gtest.Case(t, func() {
a1 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0", "1", "1"})
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1", "0", "1", "1"})
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1", "0", "1", "1"})
})
}
func TestStringArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []string{"0","1"})
gtest.Assert(array1.SubSlice(2, 2), []string{"2","3"})
gtest.Assert(array1.SubSlice(5, 8), []string{"5","6"})
})
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
gtest.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
gtest.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
})
}
func TestStringArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), "2")
gtest.Assert(len(array1.Rands(10)), "7")
gtest.AssertIN(array1.Rands(1)[0], a1)
})
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), "2")
gtest.Assert(len(array1.Rands(10)), "7")
gtest.AssertIN(array1.Rands(1)[0], a1)
gtest.Assert(len(array1.Rand()), 1)
gtest.AssertIN(array1.Rand(), a1)
})
}
func TestStringArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
array := garray.NewFromCopy(a1)
gtest.AssertIN(array.PopRands(2), a1)
})
gtest.Case(t, func() {
a1 := []string{"a", "b", "c", "d", "e", "f", "g"}
a2 := []string{"1", "2", "3", "4", "5", "6", "7"}
array1 := garray.NewStringArrayFrom(a1)
//todo gtest.AssertIN(array1.PopRands(1),a1)
gtest.AssertIN(array1.PopRands(1), strings.Join(a1, ","))
gtest.AssertNI(array1.PopRands(1), strings.Join(a2, ","))
})
}
func TestStringArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestStringArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []string{"6","5","4","3","2","1","0"})
})
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
})
}
func TestStringArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestNewStringArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := garray.NewStringArrayFromCopy(a1)
a3 := garray.NewStringArrayFromCopy(a1, true)
gtest.Assert(a2.Contains("1"), true)
gtest.Assert(a2.Len(), 7)
gtest.Assert(a2, a3)
})
}
func TestStringArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"a", "b", "c", "d"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Contains("2"), true)
gtest.Assert(array1.Len(), 7)
array1 = array1.SetArray(a2)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1.Len(), 4)
})
}
func TestStringArray_Replace(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"a", "b", "c", "d"}
a3 := []string{"o", "p", "q", "x", "y", "z", "w", "r", "v"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Contains("2"), true)
gtest.Assert(array1.Len(), 7)
array1 = array1.Replace(a2)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1.Contains("5"), true)
gtest.Assert(array1.Len(), 7)
array1 = array1.Replace(a3)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), false)
gtest.Assert(array1.Contains("5"), false)
gtest.Assert(array1.Contains("p"), true)
gtest.Assert(array1.Contains("r"), false)
gtest.Assert(array1.Len(), 7)
})
}
func TestStringArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"0", "a", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Sum(), 21)
gtest.Assert(array2.Sum(), 18)
})
}
//func TestStringArray_SortFunc(t *testing.T) {
// gtest.Case(t, func() {
// a1 := []string{"0","1","2","3","4","5","6"}
// //a2 := []string{"0","a","3","4","5","6"}
// array1 := garray.NewStringArrayFrom(a1)
//
// lesss:=func(v1,v2 string)bool{
// if v1>v2{
// return true
// }
// return false
// }
// gtest.Assert(array1.Len(),7)
// gtest.Assert(lesss("1","2"),false)
// gtest.Assert(array1.SortFunc(lesss("1","2")) ,false)
//
//
// })
//}
func TestStringArray_PopRand(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
str1 := array1.PopRand()
gtest.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
gtest.Assert(array1.Len(), 6)
})
}
func TestStringArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStringArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array2, array1)
gtest.Assert(array2.Len(), 7)
})
}
func TestStringArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3", "4", "4", "6"}
array1 := garray.NewStringArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 6)
gtest.Assert(m1["2"], 1)
gtest.Assert(m1["4"], 2)
})
}
func TestNewSortedStringArrayFrom(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
s1 := garray.NewSortedStringArrayFrom(a1, true)
gtest.Assert(s1, []string{"a", "b", "c", "d"})
s2 := garray.NewSortedStringArrayFrom(a1, false)
gtest.Assert(s2, []string{"a", "b", "c", "d"})
})
}
func TestNewSortedStringArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
s1 := garray.NewSortedStringArrayFromCopy(a1, true)
gtest.Assert(s1.Len(), 4)
gtest.Assert(s1, []string{"a", "b", "c", "d"})
})
}
func TestSortedStringArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
a2 := []string{"f", "g", "h"}
array1 := garray.NewSortedStringArrayFrom(a1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("d"), false)
gtest.Assert(array1.Contains("b"), false)
gtest.Assert(array1.Contains("g"), true)
})
}
func TestSortedStringArray_Sort(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.Assert(array1, []string{"a", "b", "c", "d"})
array1.Sort() //todo 这个SortedStringArray.sort这个方法没有必要
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1, []string{"a", "b", "c", "d"})
})
}
func TestSortedStringArray_Get(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.Assert(array1.Get(2), "c")
gtest.Assert(array1.Get(0), "a")
})
}
func TestSortedStringArray_Remove(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.Assert(array1.Remove(2), "c")
gtest.Assert(array1.Get(2), "d")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("c"), false)
gtest.Assert(array1.Remove(0), "a")
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1.Contains("a"), false)
// 此时array1里的元素只剩下2个
gtest.Assert(array1.Remove(1), "d")
gtest.Assert(array1.Len(), 1)
})
}
func TestSortedStringArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopLeft()
gtest.Assert(s1, "a")
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("a"), false)
})
}
func TestSortedStringArray_PopRight(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopRight()
gtest.Assert(s1, "e")
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("e"), false)
})
}
func TestSortedStringArray_PopRand(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopRand()
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains(s1), false)
})
}
func TestSortedStringArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopRands(2)
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(len(s1), 2)
s1 = array1.PopRands(4)
gtest.Assert(len(s1), 3)
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
})
}
func TestSortedStringArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopLefts(2)
gtest.Assert(s1, []string{"a", "b"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(len(s1), 2)
s1 = array1.PopLefts(4)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"c", "d", "e"})
})
}
func TestSortedStringArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.PopRights(2)
gtest.Assert(s1, []string{"f", "g"})
gtest.Assert(array1.Len(), 5)
gtest.Assert(len(s1), 2)
s1 = array1.PopRights(6)
gtest.Assert(len(s1), 5)
gtest.Assert(s1, []string{"a", "b", "c", "d", "e"})
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedStringArray_Range(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.Range(2, 4)
gtest.Assert(len(s1), 2)
gtest.Assert(s1, []string{"c", "d"})
s1 = array1.Range(-1, 2)
gtest.Assert(len(s1), 2)
gtest.Assert(s1, []string{"a", "b"})
s1 = array1.Range(4, 8)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"e", "f", "g"})
})
}
func TestSortedStringArray_Sum(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
a2 := []string{"1", "2", "3", "4", "a"}
array1 := garray.NewSortedStringArrayFrom(a1)
array2 := garray.NewSortedStringArrayFrom(a2)
gtest.Assert(array1.Sum(), 0)
gtest.Assert(array2.Sum(), 10)
})
}
func TestSortedStringArray_Clone(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
array1.Remove(1)
gtest.Assert(array2.Len(), 7)
})
}
func TestSortedStringArray_Clear(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
})
}
func TestSortedStringArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.SubSlice(1, 3)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"b", "c", "d"})
gtest.Assert(array1.Len(), 7)
s2 := array1.SubSlice(1, 10)
gtest.Assert(len(s2), 6)
s3 := array1.SubSlice(10, 2)
gtest.Assert(len(s3), 0)
})
}
func TestSortedStringArray_Len(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.Assert(array1.Len(), 7)
})
}
func TestSortedStringArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.AssertIN(array1.Rand(), []string{"e", "a", "d"})
})
}
func TestSortedStringArray_Rands(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStringArrayFrom(a1)
s1 := array1.Rands(2)
gtest.AssertIN(s1, []string{"e", "a", "d"})
gtest.Assert(len(s1), 2)
s1 = array1.Rands(4)
gtest.AssertIN(s1, []string{"e", "a", "d"})
gtest.Assert(len(s1), 3)
})
}
func TestSortedStringArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStringArrayFrom(a1)
gtest.Assert(array1.Join(","), "a,d,e")
gtest.Assert(array1.Join("."), "a.d.e")
})
}
func TestSortedStringArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStringArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(m1["a"], 2)
gtest.Assert(m1["d"], 1)
})
}
func TestSortedStringArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStringArrayFrom(a1)
array2 := array1.Chunk(2)
gtest.Assert(len(array2), 3)
gtest.Assert(len(array2[0]), 2)
gtest.Assert(array2[1], []string{"c", "d"})
})
}
func TestSortedStringArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStringArrayFrom(a1)
array2 := array1.SetUnique(true)
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []string{"a", "c", "d", "e"})
})
}
func TestStringArray_Remove(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewStringArrayFrom(a1)
s1 := array1.Remove(1)
gtest.Assert(s1, "a")
gtest.Assert(array1.Len(), 4)
s1 = array1.Remove(3)
gtest.Assert(s1, "c")
gtest.Assert(array1.Len(), 3)
})
}

View File

@ -4,59 +4,67 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gchan provides graceful channel for safe operations.
// Package gchan provides graceful channel for no panic operations.
//
// It's safe to call Chan.Push/Close functions repeatedly.
package gchan
import (
"errors"
"github.com/gogf/gf/g/container/gtype"
"errors"
"github.com/gogf/gf/g/container/gtype"
)
// Graceful channel.
type Chan struct {
channel chan interface{}
closed *gtype.Bool
channel chan interface{}
closed *gtype.Bool
}
// New creates a graceful channel with given limit.
// New creates a graceful channel with given <limit>.
func New(limit int) *Chan {
return &Chan {
channel : make(chan interface{}, limit),
closed : gtype.NewBool(),
}
return &Chan{
channel: make(chan interface{}, limit),
closed: gtype.NewBool(),
}
}
// Push pushes <value> to channel.
// It is safe to be called repeatedly.
func (c *Chan) Push(value interface{}) error {
if c.closed.Val() {
return errors.New("closed")
}
c.channel <- value
return nil
if c.closed.Val() {
return errors.New("channel is closed")
}
c.channel <- value
return nil
}
// Pop pops value from channel.
// If there's no value in channel, it would block to wait.
// If the channel is closed, it will return a nil value immediately.
func (c *Chan) Pop() interface{} {
return <- c.channel
return <-c.channel
}
// Close closes the channel.
// It is safe to be called repeatedly.
func (c *Chan) Close() {
if !c.closed.Set(true) {
close(c.channel)
}
if !c.closed.Set(true) {
close(c.channel)
}
}
// See Len.
func (c *Chan) Size() int {
return c.Len()
return c.Len()
}
// Len returns the length of the channel.
func (c *Chan) Len() int {
return len(c.channel)
}
}
// Cap returns the capacity of the channel.
func (c *Chan) Cap() int {
return cap(c.channel)
}

View File

@ -1,32 +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 this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*"
package gchan_test
import (
"testing"
"github.com/gogf/gf/g/container/gchan"
)
var length = 10000000
var q1 = gchan.New(length)
var q2 = make(chan int, length)
func BenchmarkGchanPushAndPop(b *testing.B) {
for i := 0; i < b.N; i++ {
q1.Push(i)
q1.Pop()
}
}
func BenchmarkChannelPushAndPop(b *testing.B) {
for i := 0; i < b.N; i++ {
q2 <- i
<- q2
}
}

View File

@ -0,0 +1,47 @@
package gchan_test
import (
"errors"
"testing"
"github.com/gogf/gf/g/container/gchan"
"github.com/gogf/gf/g/test/gtest"
)
func Test_Gchan(t *testing.T) {
gtest.Case(t, func() {
ch := gchan.New(10)
gtest.Assert(ch.Cap(), 10)
gtest.Assert(ch.Push(1), nil)
gtest.Assert(ch.Len(), 1)
gtest.Assert(ch.Size(), 1)
ch.Pop()
gtest.Assert(ch.Len(), 0)
gtest.Assert(ch.Size(), 0)
ch.Close()
gtest.Assert(ch.Push(1), errors.New("channel is closed"))
ch = gchan.New(0)
ch1 := gchan.New(0)
go func() {
var i = 0
for {
v := ch.Pop()
if v == nil {
ch1.Push(i)
break
}
gtest.Assert(v, i)
i++
}
}()
for index := 0; index < 10; index++ {
ch.Push(index)
}
ch.Close()
gtest.Assert(ch1.Pop(), 10)
ch1.Close()
})
}

View File

@ -209,7 +209,7 @@ func (l *List) Len() (length int) {
return
}
// Alias of Len.
// Size is alias of Len.
func (l *List) Size() int {
return l.Len()
}

View File

@ -7,361 +7,577 @@
package glist
import (
"container/list"
"testing"
"container/list"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
)
// 检查链表长度
func checkListLen(t *testing.T, l *List, len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
return false
}
return true
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
return false
}
return true
}
// 检查指针地址
func checkListPointers(t *testing.T, l *List, es []*Element) {
if !checkListLen(t, l, len(es)) {
return
}
l.RLockFunc(func(list *list.List) {
for i, e := 0, l.list.Front(); i < list.Len(); i, e = i + 1, e.Next() {
if e.Prev() != es[i].Prev() {
t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev())
}
if e.Next() != es[i].Next() {
t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next())
}
}
})
if !checkListLen(t, l, len(es)) {
return
}
l.RLockFunc(func(list *list.List) {
for i, e := 0, l.list.Front(); i < list.Len(); i, e = i+1, e.Next() {
if e.Prev() != es[i].Prev() {
t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev())
}
if e.Next() != es[i].Next() {
t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next())
}
}
})
}
func TestBasic(t *testing.T) {
l := New()
l.PushFront(1)
l.PushFront(2)
if v := l.PopBack(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l.PushBack(1)
l.PushBack(2)
if v := l.PopFront(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l := New()
l.PushFront(1)
l.PushFront(2)
if v := l.PopBack(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l.PushBack(1)
l.PushBack(2)
if v := l.PopFront(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
}
func TestList(t *testing.T) {
l := New()
checkListPointers(t, l, []*Element{})
l := New()
checkListPointers(t, l, []*Element{})
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
l.Remove(e)
checkListPointers(t, l, []*Element{})
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
l.Remove(e)
checkListPointers(t, l, []*Element{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Element{e1, e4, e3})
e2 = l.InsertBefore(2, e1) // insert before front
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e4) // insert before middle
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e3) // insert before back
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e1) // insert before front
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e4) // insert before middle
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e3) // insert before back
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e1) // insert after front
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e4) // insert after middle
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e3) // insert after back
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
l.Remove(e2)
e2 = l.InsertAfter(2, e1) // insert after front
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e4) // insert after middle
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e3) // insert after back
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
l.Remove(e2)
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
if i, ok := e.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
if i, ok := e.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
var next *Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
// Clear all elements by iterating
var next *Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
}
func checkList(t *testing.T, l *List, es []interface{}) {
if !checkListLen(t, l, len(es)) {
return
}
if !checkListLen(t, l, len(es)) {
return
}
i := 0
for e := l.Front(); e != nil; e = e.Next() {
le := e.Value.(int)
if le != es[i] {
t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
}
i++
}
i := 0
for e := l.Front(); e != nil; e = e.Next() {
switch e.Value.(type) {
case int:
if le := e.Value.(int); le != es[i] {
t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
}
// default string
default:
if le := e.Value.(string); le != es[i] {
t.Errorf("elt[%v].Value() = %v, want %v", i, le, es[i])
}
}
i++
}
//for e := l.Front(); e != nil; e = e.Next() {
// le := e.Value.(int)
// if le != es[i] {
// t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
// }
// i++
//}
}
func TestExtending(t *testing.T) {
l1 := New()
l2 := New()
l1 := New()
l2 := New()
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l2.PushBack(4)
l2.PushBack(5)
l2.PushBack(4)
l2.PushBack(5)
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
}
func TestRemove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Element{e1, e2})
//e := l.Front()
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Element{e1, e2})
//e := l.Front()
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
}
func TestIssue4103(t *testing.T) {
l1 := New()
l1.PushBack(1)
l1.PushBack(2)
l1 := New()
l1.PushBack(1)
l1.PushBack(2)
l2 := New()
l2.PushBack(3)
l2.PushBack(4)
l2 := New()
l2.PushBack(3)
l2.PushBack(4)
e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
}
func TestIssue6349(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l := New()
l.PushBack(1)
l.PushBack(2)
e := l.Front()
l.Remove(e)
if e.Value != 1 {
t.Errorf("e.value = %d, want 1", e.Value)
}
//if e.Next() != nil {
// t.Errorf("e.Next() != nil")
//}
//if e.Prev() != nil {
// t.Errorf("e.Prev() != nil")
//}
e := l.Front()
l.Remove(e)
if e.Value != 1 {
t.Errorf("e.value = %d, want 1", e.Value)
}
//if e.Next() != nil {
// t.Errorf("e.Next() != nil")
//}
//if e.Prev() != nil {
// t.Errorf("e.Prev() != nil")
//}
}
func TestMove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
}
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
func TestZeroList(t *testing.T) {
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
}
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
}
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
}
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
l1 := New()
e1 := l1.PushBack(1)
l1 := New()
e1 := l1.PushBack(1)
l2 := New()
e2 := l2.PushBack(2)
l2 := New()
e2 := l2.PushBack(2)
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
}
func TestList_RemoveAll(t *testing.T) {
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
}
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
}
func TestList_PushFronts(t *testing.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushFronts(a1)
checkList(t, l, []interface{}{2, 1})
a1 = []interface{}{3, 4, 5}
l.PushFronts(a1)
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
}
func TestList_PushBacks(t *testing.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2})
a1 = []interface{}{3, 4, 5}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
}
func TestList_PopBacks(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a2 := []interface{}{"a", "c", "b", "e"}
l.PushFronts(a1)
i1 := l.PopBacks(2)
gtest.Assert(i1, []interface{}{"1", "2"})
l.PushBacks(a2) //4.3,a,c,b,e
i1 = l.PopBacks(3)
gtest.Assert(i1, []interface{}{"e", "b", "c"})
}
func TestList_PopFronts(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFronts(2)
gtest.Assert(i1, []interface{}{"4", "3"})
gtest.Assert(l.Len(), 2)
}
func TestList_PopBackAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopBackAll()
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
gtest.Assert(l.Len(), 0)
}
func TestList_PopFrontAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFrontAll()
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
gtest.Assert(l.Len(), 0)
}
func TestList_FrontAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontAll()
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
gtest.Assert(l.Len(), 4)
}
func TestList_BackAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackAll()
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
gtest.Assert(l.Len(), 4)
}
func TestList_FrontValue(t *testing.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontValue()
gtest.Assert(gconv.Int(i1), 4)
gtest.Assert(l.Len(), 4)
i1 = l2.FrontValue()
gtest.Assert(i1, nil)
}
func TestList_BackValue(t *testing.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackValue()
gtest.Assert(gconv.Int(i1), 1)
gtest.Assert(l.Len(), 4)
i1 = l2.FrontValue()
gtest.Assert(i1, nil)
}
func TestList_Back(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
gtest.Assert(e1.Value, 1)
gtest.Assert(l.Len(), 4)
}
func TestList_Size(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
gtest.Assert(l.Size(), 4)
l.PopFront()
gtest.Assert(l.Size(), 3)
}
func TestList_Removes(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
l.Removes([]*Element{e1})
gtest.Assert(l.Len(), 3)
e2 := l.Back()
l.Removes([]*Element{e2})
gtest.Assert(l.Len(), 2)
checkList(t, l, []interface{}{4, 3})
}
func TestList_Clear(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
l.Clear()
gtest.Assert(l.Len(), 0)
}
func TestList_IteratorAsc(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 5, 6, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 2 {
return true
}
return false
}
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
l.IteratorAsc(fun1)
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
}
func TestList_IteratorDesc(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 6 {
return true
}
return false
}
l.IteratorDesc(fun1)
gtest.Assert(l.Len(), 4)
checkList(t, l, []interface{}{4, 3, 2, 1})
}
func TestList_Iterator(t *testing.T) {
l := New()
a1 := []interface{}{"a", "b", "c", "d", "e"}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.String(e1.Value) > "c" {
return true
}
return false
}
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
l.Iterator(fun1)
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
}

View File

@ -12,7 +12,7 @@ type Map = AnyAnyMap
type HashMap = AnyAnyMap
// New returns an empty hash map.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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 NewAnyAnyMap(unsafe...)
@ -21,14 +21,14 @@ func New(unsafe ...bool) *Map {
// 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.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// The parameter <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...)
}
// NewHashMap returns an empty hash map.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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...)
@ -37,7 +37,7 @@ func NewHashMap(unsafe ...bool) *Map {
// 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,
// The parameter <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

@ -17,7 +17,7 @@ type AnyAnyMap struct {
}
// NewAnyAnyMap returns an empty hash map.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -19,7 +19,7 @@ type IntAnyMap struct {
}
// NewIntAnyMap returns an empty IntAnyMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -16,7 +16,7 @@ type IntIntMap struct {
}
// NewIntIntMap returns an empty IntIntMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -17,7 +17,7 @@ type IntStrMap struct {
}
// NewIntStrMap returns an empty IntStrMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -19,7 +19,7 @@ type StrAnyMap struct {
}
// NewStrAnyMap returns an empty StrAnyMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -18,7 +18,7 @@ type StrIntMap struct {
}
// NewStrIntMap returns an empty StrIntMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -17,7 +17,7 @@ type StrStrMap struct {
}
// NewStrStrMap returns an empty StrStrMap object.
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
// The parameter <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{

View File

@ -25,7 +25,7 @@ type gListMapNode struct {
// 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,
// The parameter <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{

View File

@ -14,7 +14,7 @@ import (
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,
// The parameter <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...)
@ -23,7 +23,7 @@ func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe...bool) *TreeMap
// 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,
// The parameter <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

@ -0,0 +1,92 @@
package gpool_test
import (
"errors"
"testing"
"time"
"github.com/gogf/gf/g/container/gpool"
"github.com/gogf/gf/g/test/gtest"
)
var nf gpool.NewFunc = func() (i interface{}, e error) {
return "hello", nil
}
var assertIndex int = 0
var ef gpool.ExpireFunc = func(i interface{}) {
assertIndex++
gtest.Assert(i, assertIndex)
}
func Test_Gpool(t *testing.T) {
gtest.Case(t, func() {
//
//expire = 0
p1 := gpool.New(0, nf)
p1.Put(1)
p1.Put(2)
time.Sleep(1 * time.Second)
//test won't be timeout
v1, err1 := p1.Get()
gtest.Assert(err1, nil)
gtest.Assert(v1, 1)
//test clear
p1.Clear()
gtest.Assert(p1.Size(), 0)
//test newFunc
v1, err1 = p1.Get()
gtest.Assert(err1, nil)
gtest.Assert(v1, "hello")
//put data again
p1.Put(3)
p1.Put(4)
v1, err1 = p1.Get()
gtest.Assert(err1, nil)
gtest.Assert(v1, 3)
//test close
p1.Close()
v1, err1 = p1.Get()
gtest.Assert(err1, nil)
gtest.Assert(v1, "hello")
})
gtest.Case(t, func() {
//
//expire > 0
p2 := gpool.New(2000, nil, ef)
for index := 0; index < 10; index++ {
p2.Put(index)
}
gtest.Assert(p2.Size(), 10)
v2, err2 := p2.Get()
gtest.Assert(err2, nil)
gtest.Assert(v2, 0)
//test timeout expireFunc
time.Sleep(3 * time.Second)
v2, err2 = p2.Get()
gtest.Assert(err2, errors.New("pool is empty"))
gtest.Assert(v2, nil)
//test close expireFunc
for index := 0; index < 10; index++ {
p2.Put(index)
}
gtest.Assert(p2.Size(), 10)
v2, err2 = p2.Get()
gtest.Assert(err2, nil)
gtest.Assert(v2, 0)
assertIndex = 0
p2.Close()
time.Sleep(3 * time.Second)
})
gtest.Case(t, func() {
//
//expire < 0
p3 := gpool.New(-1, nil)
v3, err3 := p3.Get()
gtest.Assert(err3, errors.New("pool is empty"))
gtest.Assert(v3, nil)
})
}

View File

@ -37,8 +37,8 @@ const (
)
// 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.
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default.
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel.
func New(limit...int) *Queue {
q := &Queue {
closed : make(chan struct{}, 0),
@ -103,16 +103,23 @@ func (q *Queue) Pop() interface{} {
// Close closes the queue.
// Notice: It would notify all goroutines return immediately,
// which are being blocked reading by Pop method.
// which are being blocked reading using Pop method.
func (q *Queue) Close() {
close(q.C)
close(q.events)
close(q.closed)
}
// Size returns the length of the queue.
func (q *Queue) Size() int {
return len(q.C) + q.list.Len()
// Len returns the length of the queue.
func (q *Queue) Len() (length int) {
if q.list != nil {
length += q.list.Len()
}
length += len(q.C)
return
}
// Size is alias of Len.
func (q *Queue) Size() int {
return q.Len()
}

View File

@ -19,7 +19,7 @@ type Set struct {
}
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Set {
return NewSet(unsafe...)

View File

@ -19,7 +19,7 @@ type IntSet struct {
}
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewIntSet(unsafe...bool) *IntSet {
return &IntSet{

View File

@ -19,7 +19,7 @@ type StringSet struct {
}
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewStringSet(unsafe...bool) *StringSet {
return &StringSet {

View File

@ -9,152 +9,208 @@
package gset_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"strings"
"testing"
)
func TestIntSet_Basic(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(1).Add(2)
s.Add([]int{3,4}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN(1, s.Slice())
gtest.AssertIN(2, s.Slice())
gtest.AssertIN(3, s.Slice())
gtest.AssertIN(4, s.Slice())
gtest.AssertNI(0, s.Slice())
gtest.Assert(s.Contains(4), true)
gtest.Assert(s.Contains(5), false)
s.Remove(1)
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(1).Add(2)
s.Add([]int{3, 4}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN(1, s.Slice())
gtest.AssertIN(2, s.Slice())
gtest.AssertIN(3, s.Slice())
gtest.AssertIN(4, s.Slice())
gtest.AssertNI(0, s.Slice())
gtest.Assert(s.Contains(4), true)
gtest.Assert(s.Contains(5), false)
s.Remove(1)
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
}
func TestIntSet_Iterator(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v int) bool {
a1.Append(1)
return false
})
s.Iterator(func(v int) bool {
a2.Append(1)
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v int) bool {
a1.Append(1)
return false
})
s.Iterator(func(v int) bool {
a2.Append(1)
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
}
func TestIntSet_LockFunc(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[int]struct{}) {
delete(m, 1)
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[int]struct{}) {
gtest.Assert(m, map[int]struct{}{
3 : struct{}{},
2 : struct{}{},
})
})
})
gtest.Case(t, func() {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[int]struct{}) {
delete(m, 1)
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[int]struct{}) {
gtest.Assert(m, map[int]struct{}{
3: struct{}{},
2: struct{}{},
})
})
})
}
func TestIntSet_Equal(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
}
func TestIntSet_IsSubsetOf(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
}
func TestIntSet_Union(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s3 := s1.Union(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), true)
})
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s3 := s1.Union(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), true)
})
}
func TestIntSet_Diff(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), false)
gtest.Assert(s3.Contains(4), false)
})
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), false)
gtest.Assert(s3.Contains(4), false)
})
}
func TestIntSet_Intersect(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), false)
})
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), false)
})
}
func TestIntSet_Complement(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(4), true)
gtest.Assert(s3.Contains(5), true)
})
}
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(4), true)
gtest.Assert(s3.Contains(5), true)
})
}
func TestIntSet_Size(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet(true)
s1.Add(1).Add(2).Add(3)
gtest.Assert(s1.Size(), 3)
})
}
func TestIntSet_Merge(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Merge(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(5), true)
gtest.Assert(s3.Contains(6), false)
})
}
func TestIntSet_Join(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s3 := s1.Join(",")
gtest.Assert(strings.Contains(s3, "3"), true)
})
}
func TestIntSet_Sum(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2 := gset.NewIntSet()
s2.Add(5).Add(6).Add(7)
gtest.Assert(s2.Sum(), 18)
})
}
func TestIntSet_Pop(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSet()
s1.Add(4).Add(2).Add(3)
gtest.AssertIN(s1.Pop(1), []int{4, 2, 3})
gtest.AssertIN(s1.Pop(5), []int{4, 2, 3})
gtest.Assert(s1.Size(), 3)
})
}

View File

@ -9,152 +9,248 @@
package gset_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"strings"
"testing"
)
func TestStringSet_Basic(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("1").Add("2")
s.Add([]string{"3","4"}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN("1", s.Slice())
gtest.AssertIN("2", s.Slice())
gtest.AssertIN("3", s.Slice())
gtest.AssertIN("4", s.Slice())
gtest.AssertNI("0", s.Slice())
gtest.Assert(s.Contains("4"), true)
gtest.Assert(s.Contains("5"), false)
s.Remove("1")
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("1").Add("2")
s.Add([]string{"3", "4"}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN("1", s.Slice())
gtest.AssertIN("2", s.Slice())
gtest.AssertIN("3", s.Slice())
gtest.AssertIN("4", s.Slice())
gtest.AssertNI("0", s.Slice())
gtest.Assert(s.Contains("4"), true)
gtest.Assert(s.Contains("5"), false)
s.Remove("1")
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
}
func TestStringSet_Iterator(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("2").Add("3")
gtest.Assert(s.Size(), 3)
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("2").Add("3")
gtest.Assert(s.Size(), 3)
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v string) bool {
a1.Append("1")
return false
})
s.Iterator(func(v string) bool {
a2.Append("1")
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v string) bool {
a1.Append("1")
return false
})
s.Iterator(func(v string) bool {
a2.Append("1")
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
}
func TestStringSet_LockFunc(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("2").Add("3")
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[string]struct{}) {
delete(m, "1")
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[string]struct{}) {
gtest.Assert(m, map[string]struct{}{
"3" : struct{}{},
"2" : struct{}{},
})
})
})
gtest.Case(t, func() {
s := gset.NewStringSet()
s.Add("1").Add("2").Add("3")
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[string]struct{}) {
delete(m, "1")
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[string]struct{}) {
gtest.Assert(m, map[string]struct{}{
"3": struct{}{},
"2": struct{}{},
})
})
})
}
func TestStringSet_Equal(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s3 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s3 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
}
func TestStringSet_IsSubsetOf(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s3 := gset.NewStringSet()
s1.Add("1").Add("2")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s3 := gset.NewStringSet()
s1.Add("1").Add("2")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
}
func TestStringSet_Union(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2")
s2.Add("3").Add("4")
s3 := s1.Union(s2)
gtest.Assert(s3.Contains("1"), true)
gtest.Assert(s3.Contains("2"), true)
gtest.Assert(s3.Contains("3"), true)
gtest.Assert(s3.Contains("4"), true)
})
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2")
s2.Add("3").Add("4")
s3 := s1.Union(s2)
gtest.Assert(s3.Contains("1"), true)
gtest.Assert(s3.Contains("2"), true)
gtest.Assert(s3.Contains("3"), true)
gtest.Assert(s3.Contains("4"), true)
})
}
func TestStringSet_Diff(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains("1"), true)
gtest.Assert(s3.Contains("2"), true)
gtest.Assert(s3.Contains("3"), false)
gtest.Assert(s3.Contains("4"), false)
})
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains("1"), true)
gtest.Assert(s3.Contains("2"), true)
gtest.Assert(s3.Contains("3"), false)
gtest.Assert(s3.Contains("4"), false)
})
}
func TestStringSet_Intersect(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains("1"), false)
gtest.Assert(s3.Contains("2"), false)
gtest.Assert(s3.Contains("3"), true)
gtest.Assert(s3.Contains("4"), false)
})
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains("1"), false)
gtest.Assert(s3.Contains("2"), false)
gtest.Assert(s3.Contains("3"), true)
gtest.Assert(s3.Contains("4"), false)
})
}
func TestStringSet_Complement(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains("1"), false)
gtest.Assert(s3.Contains("2"), false)
gtest.Assert(s3.Contains("4"), true)
gtest.Assert(s3.Contains("5"), true)
})
}
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains("1"), false)
gtest.Assert(s3.Contains("2"), false)
gtest.Assert(s3.Contains("4"), true)
gtest.Assert(s3.Contains("5"), true)
})
}
func TestNewIntSetFrom(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewIntSetFrom([]int{1, 2, 3, 4})
s2 := gset.NewIntSetFrom([]int{5, 6, 7, 8})
gtest.Assert(s1.Contains(3), true)
gtest.Assert(s1.Contains(5), false)
gtest.Assert(s2.Contains(3), false)
gtest.Assert(s2.Contains(5), true)
})
}
func TestStringSet_Merge(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSet()
s2 := gset.NewStringSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s3 := s1.Merge(s2)
gtest.Assert(s3.Contains("1"), true)
gtest.Assert(s3.Contains("6"), false)
gtest.Assert(s3.Contains("4"), true)
gtest.Assert(s3.Contains("5"), true)
})
}
func TestNewStringSetFrom(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
gtest.Assert(s1.Contains("b"), true)
gtest.Assert(s1.Contains("d"), false)
})
}
func TestStringSet_Join(t *testing.T) {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
str1 := s1.Join(",")
gtest.Assert(strings.Contains(str1, "b"), true)
gtest.Assert(strings.Contains(str1, "d"), false)
}
func TestStringSet_String(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
str1 := s1.String()
gtest.Assert(strings.Contains(str1, "b"), true)
gtest.Assert(strings.Contains(str1, "d"), false)
})
}
func TestStringSet_Sum(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
s2 := gset.NewIntSetFrom([]int{2, 3, 4}, true)
gtest.Assert(s1.Sum(), 0)
gtest.Assert(s2.Sum(), 9)
})
}
func TestStringSet_Size(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
gtest.Assert(s1.Size(), 3)
})
}
func TestStringSet_Remove(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
s1 = s1.Remove("b")
gtest.Assert(s1.Contains("b"), false)
gtest.Assert(s1.Contains("c"), true)
})
}
func TestStringSet_Pop(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
str1 := s1.Pop(1)
gtest.Assert(strings.Contains("a,b,c", str1), true)
})
}
func TestStringSet_Pops(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewStringSetFrom([]string{"a", "b", "c"}, true)
strs1 := s1.Pops(2)
gtest.AssertIN(strs1, []string{"a", "b", "c"})
gtest.Assert(len(strs1), 2)
str2 := s1.Pops(7)
gtest.AssertIN(str2, []string{"a", "b", "c"})
})
}

View File

@ -9,152 +9,260 @@
package gset_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/test/gtest"
"strings"
"testing"
)
func TestSet_New(t *testing.T) {
gtest.Case(t, func() {
s := gset.New()
s.Add(1).Add(1).Add(2)
s.Add([]interface{}{3, 4}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN(1, s.Slice())
gtest.AssertIN(2, s.Slice())
gtest.AssertIN(3, s.Slice())
gtest.AssertIN(4, s.Slice())
gtest.AssertNI(0, s.Slice())
gtest.Assert(s.Contains(4), true)
gtest.Assert(s.Contains(5), false)
s.Remove(1)
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
}
func TestSet_Basic(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(1).Add(2)
s.Add([]interface{}{3,4}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN(1, s.Slice())
gtest.AssertIN(2, s.Slice())
gtest.AssertIN(3, s.Slice())
gtest.AssertIN(4, s.Slice())
gtest.AssertNI(0, s.Slice())
gtest.Assert(s.Contains(4), true)
gtest.Assert(s.Contains(5), false)
s.Remove(1)
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(1).Add(2)
s.Add([]interface{}{3, 4}...)
gtest.Assert(s.Size(), 4)
gtest.AssertIN(1, s.Slice())
gtest.AssertIN(2, s.Slice())
gtest.AssertIN(3, s.Slice())
gtest.AssertIN(4, s.Slice())
gtest.AssertNI(0, s.Slice())
gtest.Assert(s.Contains(4), true)
gtest.Assert(s.Contains(5), false)
s.Remove(1)
gtest.Assert(s.Size(), 3)
s.Clear()
gtest.Assert(s.Size(), 0)
})
}
func TestSet_Iterator(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v interface{}) bool {
a1.Append(1)
return false
})
s.Iterator(func(v interface{}) bool {
a2.Append(1)
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
a1 := garray.New()
a2 := garray.New()
s.Iterator(func(v interface{}) bool {
a1.Append(1)
return false
})
s.Iterator(func(v interface{}) bool {
a2.Append(1)
return true
})
gtest.Assert(a1.Len(), 1)
gtest.Assert(a2.Len(), 3)
})
}
func TestSet_LockFunc(t *testing.T) {
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[interface{}]struct{}) {
delete(m, 1)
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[interface{}]struct{}) {
gtest.Assert(m, map[interface{}]struct{}{
3 : struct{}{},
2 : struct{}{},
})
})
})
gtest.Case(t, func() {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
gtest.Assert(s.Size(), 3)
s.LockFunc(func(m map[interface{}]struct{}) {
delete(m, 1)
})
gtest.Assert(s.Size(), 2)
s.RLockFunc(func(m map[interface{}]struct{}) {
gtest.Assert(m, map[interface{}]struct{}{
3: struct{}{},
2: struct{}{},
})
})
})
}
func TestSet_Equal(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.Equal(s2), true)
gtest.Assert(s1.Equal(s3), false)
})
}
func TestSet_IsSubsetOf(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.IsSubsetOf(s2), true)
gtest.Assert(s2.IsSubsetOf(s3), true)
gtest.Assert(s1.IsSubsetOf(s3), true)
gtest.Assert(s2.IsSubsetOf(s1), false)
gtest.Assert(s3.IsSubsetOf(s2), false)
})
}
func TestSet_Union(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s3 := s1.Union(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), true)
})
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s3 := s1.Union(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), true)
})
}
func TestSet_Diff(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), false)
gtest.Assert(s3.Contains(4), false)
})
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Diff(s2)
gtest.Assert(s3.Contains(1), true)
gtest.Assert(s3.Contains(2), true)
gtest.Assert(s3.Contains(3), false)
gtest.Assert(s3.Contains(4), false)
})
}
func TestSet_Intersect(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), false)
})
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Intersect(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s3.Contains(4), false)
})
}
func TestSet_Complement(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(4), true)
gtest.Assert(s3.Contains(5), true)
})
}
gtest.Case(t, func() {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s3 := s1.Complement(s2)
gtest.Assert(s3.Contains(1), false)
gtest.Assert(s3.Contains(2), false)
gtest.Assert(s3.Contains(4), true)
gtest.Assert(s3.Contains(5), true)
})
}
func TestNewFrom(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.NewFrom("a")
s2 := gset.NewFrom("b", false)
s3 := gset.NewFrom(3, true)
s4 := gset.NewFrom([]string{"s1", "s2"}, true)
gtest.Assert(s1.Contains("a"), true)
gtest.Assert(s2.Contains("b"), true)
gtest.Assert(s3.Contains(3), true)
gtest.Assert(s4.Contains("s1"), true)
gtest.Assert(s4.Contains("s3"), false)
})
}
func TestNew(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New()
s1.Add("a").Add(2)
s2 := gset.New(true)
s2.Add("b").Add(3)
gtest.Assert(s1.Contains("a"), true)
})
}
func TestSet_Join(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s1.Add("a").Add("a1").Add("b").Add("c")
str1 := s1.Join(",")
gtest.Assert(strings.Contains(str1, "a1"), true)
})
}
func TestSet_String(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
str1 := s1.String()
gtest.Assert(strings.Contains(str1, "a2"), true)
})
}
func TestSet_Merge(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s2 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s2.Add("b").Add("b1").Add("e").Add("f")
ss := s1.Merge(s2)
gtest.Assert(ss.Contains("a2"), true)
gtest.Assert(ss.Contains("b1"), true)
})
}
func TestSet_Sum(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s1.Add(1).Add(2).Add(3).Add(4)
gtest.Assert(s1.Sum(), int(10))
})
}
func TestSet_Pop(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s1.Add(1).Add(2).Add(3).Add(4)
gtest.AssertIN(s1.Pop(1), []int{1, 2, 3, 4})
})
}
func TestSet_Pops(t *testing.T) {
gtest.Case(t, func() {
s1 := gset.New(true)
s1.Add(1).Add(2).Add(3).Add(4)
gtest.AssertIN(s1.Pops(1), []int{1, 2, 3, 4})
gtest.AssertIN(s1.Pops(6), []int{1, 2, 3, 4})
gtest.Assert(len(s1.Pops(2)), 2)
})
}

View File

@ -29,8 +29,8 @@ type AVLTreeNode struct {
b int8
}
// NewAVLTree instantiates an AVL tree with the custom comparator.
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
// NewAVLTree instantiates an AVL tree with the custom key comparator.
// The parameter <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{
@ -39,8 +39,8 @@ func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *AVLTree
}
}
// 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,
// NewAVLTreeFrom instantiates an AVL tree with the custom key comparator and data map.
// The parameter <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...)
@ -222,7 +222,7 @@ func (tree *AVLTree) Contains(key interface{}) bool {
return ok
}
// Remove remove the node from the tree by key.
// Remove removes 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()
@ -306,7 +306,7 @@ func (tree *AVLTree) Right() *AVLTreeNode {
return node
}
// Floor Finds floor node of the input key, return the floor node or nil if no ceiling is found.
// Floor Finds floor node of the input key, return the floor node or nil if no floor node 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.
@ -317,16 +317,15 @@ func (tree *AVLTree) Right() *AVLTreeNode {
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]
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 {
@ -335,7 +334,7 @@ func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
return nil, false
}
// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling is found.
// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling node 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.
@ -343,10 +342,9 @@ func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
// 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) {
func (tree *AVLTree) Ceiling(key interface{}) (ceiling *AVLTreeNode, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found = false
n := tree.root
for n != nil {
c := tree.comparator(key, n.Key)
@ -354,7 +352,7 @@ func (tree *AVLTree) Ceiling(key interface{}) (floor *AVLTreeNode, found bool) {
case c == 0: return n, true
case c > 0: n = n.children[1]
case c < 0:
floor, found = n, true
ceiling, found = n, true
n = n.children[0]
}
}
@ -514,7 +512,7 @@ func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{
if fix {
return value, removeFix(int8(-c), qp)
}
return nil, false
return value, false
}
func removeMin(qp **AVLTreeNode, minKey *interface{}, minVal *interface{}) bool {

View File

@ -37,7 +37,7 @@ type BTreeEntry struct {
}
// 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,
// The parameter <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 {
@ -52,7 +52,7 @@ func NewBTree(m int, comparator func(v1, v2 interface{}) int, unsafe...bool) *BT
}
// 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,
// The parameter <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...)
@ -503,9 +503,9 @@ 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) isFull(node *BTreeNode) bool {
// return len(node.Entries) == tree.maxEntries()
//}
func (tree *BTree) shouldSplit(node *BTreeNode) bool {
return len(node.Entries) > tree.maxEntries()

View File

@ -36,8 +36,8 @@ type RedBlackTreeNode struct {
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,
// NewRedBlackTree instantiates a red-black tree with the custom key comparator.
// The parameter <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 {
@ -46,8 +46,8 @@ func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *Re
}
}
// 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,
// NewRedBlackTreeFrom instantiates a red-black tree with the custom key comparator and <data> map.
// The parameter <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...)
@ -393,60 +393,56 @@ func (tree *RedBlackTree) rightNode() *RedBlackTreeNode {
return p
}
// Floor Finds floor node of the input <key>, return the floor node or nil if no floor is found.
// Floor Finds floor node of the input key, return the floor node or nil if no floor node is found.
// Second return parameter is true if floor was found, otherwise false.
//
// 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) {
func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found := false
node := tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
n := tree.root
for n != nil {
compare := tree.comparator(key, n.Key)
switch {
case compare == 0:
return node
case compare < 0:
node = node.left
case compare > 0:
floor, found = node, true
node = node.right
case compare == 0: return n, true
case compare < 0: n = n.left
case compare > 0:
floor, found = n, true
n = n.right
}
}
if found {
return floor
return
}
return nil
return nil, false
}
// Ceiling finds ceiling node of the input <key>, return the ceiling node or nil if no ceiling is found.
// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling node is found.
// Second return parameter is true if ceiling was found, otherwise false.
//
// 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) {
func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode, found bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
found := false
node := tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
n := tree.root
for n != nil {
compare := tree.comparator(key, n.Key)
switch {
case compare == 0:
return node
case compare < 0:
ceiling, found = node, true
node = node.left
case compare > 0:
node = node.right
case compare == 0: return n, true
case compare > 0: n = n.right
case compare < 0:
ceiling, found = n, true
n = n.left
}
}
if found {
return ceiling
return
}
return nil
return nil, false
}
// Iterator is alias of IteratorAsc.

View File

@ -0,0 +1,291 @@
package gtype_test
import (
"sync"
"testing"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/test/gtest"
)
type Temp struct {
Name string
Age int
}
func Test_Bool(t *testing.T) {
gtest.Case(t, func() {
i := gtype.NewBool(true)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(false), true)
gtest.AssertEQ(iClone.Val(), false)
i1 := gtype.NewBool(false)
iClone1 := i1.Clone()
gtest.AssertEQ(iClone1.Set(true), false)
gtest.AssertEQ(iClone1.Val(), true)
//空参测试
i2 := gtype.NewBool()
gtest.AssertEQ(i2.Val(), false)
})
}
func Test_Byte(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 127
i := gtype.NewByte(byte(0))
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(byte(1)), byte(0))
gtest.AssertEQ(iClone.Val(), byte(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(byte(addTimes), i.Val())
//空参测试
i1 := gtype.NewByte()
gtest.AssertEQ(i1.Val(), byte(0))
})
}
func Test_Bytes(t *testing.T) {
gtest.Case(t, func() {
i := gtype.NewBytes([]byte("abc"))
iClone := i.Clone()
gtest.AssertEQ(iClone.Set([]byte("123")), []byte("abc"))
gtest.AssertEQ(iClone.Val(), []byte("123"))
//空参测试
i1 := gtype.NewBytes()
gtest.AssertEQ(i1.Val(), nil)
})
}
func Test_String(t *testing.T) {
gtest.Case(t, func() {
i := gtype.NewString("abc")
iClone := i.Clone()
gtest.AssertEQ(iClone.Set("123"), "abc")
gtest.AssertEQ(iClone.Val(), "123")
//空参测试
i1 := gtype.NewString()
gtest.AssertEQ(i1.Val(), "")
})
}
func Test_Interface(t *testing.T) {
gtest.Case(t, func() {
t := Temp{Name: "gf", Age: 18}
t1 := Temp{Name: "gf", Age: 19}
i := gtype.New(t)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(t1), t)
gtest.AssertEQ(iClone.Val().(Temp), t1)
//空参测试
i1 := gtype.New()
gtest.AssertEQ(i1.Val(), nil)
})
}
func Test_Float32(t *testing.T) {
gtest.Case(t, func() {
//var wg sync.WaitGroup
//addTimes := 100
i := gtype.NewFloat32(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(0.1), float32(0))
gtest.AssertEQ(iClone.Val(), float32(0.1))
// for index := 0; index < addTimes; index++ {
// wg.Add(1)
// go func() {
// defer wg.Done()
// i.Add(0.2)
// fmt.Println(i.Val())
// }()
// }
// wg.Wait()
// gtest.AssertEQ(100.0, i.Val())
//空参测试
i1 := gtype.NewFloat32()
gtest.AssertEQ(i1.Val(), float32(0))
})
}
func Test_Float64(t *testing.T) {
gtest.Case(t, func() {
//var wg sync.WaitGroup
//addTimes := 100
i := gtype.NewFloat64(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(0.1), float64(0))
gtest.AssertEQ(iClone.Val(), float64(0.1))
// for index := 0; index < addTimes; index++ {
// wg.Add(1)
// go func() {
// defer wg.Done()
// i.Add(0.1)
// fmt.Println(i.Val())
// }()
// }
// wg.Wait()
// gtest.AssertEQ(100.0, i.Val())
//空参测试
i1 := gtype.NewFloat64()
gtest.AssertEQ(i1.Val(), float64(0))
})
}
func Test_Int(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewInt(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), 0)
gtest.AssertEQ(iClone.Val(), 1)
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(addTimes, i.Val())
//空参测试
i1 := gtype.NewInt()
gtest.AssertEQ(i1.Val(), 0)
})
}
func Test_Int32(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewInt32(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), int32(0))
gtest.AssertEQ(iClone.Val(), int32(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(int32(addTimes), i.Val())
//空参测试
i1 := gtype.NewInt32()
gtest.AssertEQ(i1.Val(), int32(0))
})
}
func Test_Int64(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewInt64(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), int64(0))
gtest.AssertEQ(iClone.Val(), int64(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(int64(addTimes), i.Val())
//空参测试
i1 := gtype.NewInt64()
gtest.AssertEQ(i1.Val(), int64(0))
})
}
func Test_Uint(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewUint(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), uint(0))
gtest.AssertEQ(iClone.Val(), uint(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(uint(addTimes), i.Val())
//空参测试
i1 := gtype.NewUint()
gtest.AssertEQ(i1.Val(), uint(0))
})
}
func Test_Uint32(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewUint32(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), uint32(0))
gtest.AssertEQ(iClone.Val(), uint32(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(uint32(addTimes), i.Val())
//空参测试
i1 := gtype.NewUint32()
gtest.AssertEQ(i1.Val(), uint32(0))
})
}
func Test_Uint64(t *testing.T) {
gtest.Case(t, func() {
var wg sync.WaitGroup
addTimes := 1000
i := gtype.NewUint64(0)
iClone := i.Clone()
gtest.AssertEQ(iClone.Set(1), uint64(0))
gtest.AssertEQ(iClone.Val(), uint64(1))
for index := 0; index < addTimes; index++ {
wg.Add(1)
go func() {
defer wg.Done()
i.Add(1)
}()
}
wg.Wait()
gtest.AssertEQ(uint64(addTimes), i.Val())
//空参测试
i1 := gtype.NewUint64()
gtest.AssertEQ(i1.Val(), uint64(0))
})
}

View File

@ -20,7 +20,7 @@ type Var struct {
}
// New returns a new Var with given <value>.
// The param <unsafe> used to specify whether using Var in un-concurrent-safety,
// The parameter <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{}
@ -59,7 +59,7 @@ func (v *Var) Interface() interface{} {
}
// Time converts and returns <v> as time.Time.
// The param <format> specifies the format of the time string using gtime,
// The parameter <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...)
@ -72,15 +72,15 @@ func (v *Var) Duration() time.Duration {
}
// GTime converts and returns <v> as *gtime.Time.
// The param <format> specifies the format of the time string using gtime,
// The parameter <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 <mapping> is used to specify the key-to-attribute mapping rules.
// The parameter <objPointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) Struct(pointer interface{}, mapping...map[string]string) error {
return gconv.Struct(v.Val(), pointer, mapping...)
}

View File

@ -25,18 +25,18 @@ func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) {
return nil, err
}
blockSize := block.BlockSize()
plainText = PKCS5Padding(plainText, blockSize)
ivValue := ([]byte)(nil)
plainText = PKCS5Padding(plainText, blockSize)
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(ivDefValue)
}
blockMode := cipher.NewCBCEncrypter(block, ivValue)
ciphertext := make([]byte, len(plainText))
blockMode.CryptBlocks(ciphertext, plainText)
cipherText := make([]byte, len(plainText))
blockMode.CryptBlocks(cipherText, plainText)
return ciphertext, nil
return cipherText, nil
}
// AES解密, 使用CBC模式注意key必须为16/24/32位长度iv初始化向量为非必需参数

View File

@ -9,6 +9,7 @@
package gaes_test
import (
"github.com/gogf/gf/g/encoding/gbase64"
"testing"
"github.com/gogf/gf/g/crypto/gaes"
@ -16,52 +17,111 @@ import (
)
var (
content = []byte("pibigstar")
content = []byte("pibigstar")
content_16, _ = gbase64.Decode("v1jqsGHId/H8onlVHR8Vaw==")
content_24, _ = gbase64.Decode("0TXOaj5KMoLhNWmJ3lxY1A==")
content_32, _ = gbase64.Decode("qM/Waw1kkWhrwzek24rCSA==")
content_16_iv, _ = gbase64.Decode("DqQUXiHgW/XFb6Qs98+hrA==")
content_32_iv, _ = gbase64.Decode("ZuLgAOii+lrD5KJoQ7yQ8Q==")
// iv 长度必须等于blockSize只能为16
iv = []byte("Hello My GoFrame")
key_16 = []byte("1234567891234567")
key_24 = []byte("123456789123456789123456")
key_32 = []byte("12345678912345678912345678912345")
keys = []byte("12345678912345678912345678912346")
iv = []byte("Hello My GoFrame")
key_16 = []byte("1234567891234567")
key_17 = []byte("12345678912345670")
key_24 = []byte("123456789123456789123456")
key_32 = []byte("12345678912345678912345678912345")
keys = []byte("12345678912345678912345678912346")
key_err = []byte("1234")
key_32_err = []byte("1234567891234567891234567891234 ")
)
func TestEncrypt(t *testing.T) {
gtest.Case(t, func() {
_, err := gaes.Encrypt(content, key_16)
data, err := gaes.Encrypt(content, key_16)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_24)
gtest.Assert(data, []byte(content_16))
data, err = gaes.Encrypt(content, key_24)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_32)
gtest.Assert(data, []byte(content_24))
data, err = gaes.Encrypt(content, key_32)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_16, iv)
gtest.Assert(data, []byte(content_32))
data, err = gaes.Encrypt(content, key_16, iv)
gtest.Assert(err, nil)
gtest.Assert(data, []byte(content_16_iv))
data, err = gaes.Encrypt(content, key_32, iv)
gtest.Assert(err, nil)
gtest.Assert(data, []byte(content_32_iv))
})
}
func TestDecrypt(t *testing.T) {
gtest.Case(t, func() {
encrypt, err := gaes.Encrypt(content, key_16)
decrypt, err := gaes.Decrypt(encrypt, key_16)
decrypt, err := gaes.Decrypt([]byte(content_16), key_16)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
gtest.Assert(decrypt, content)
encrypt, err = gaes.Encrypt(content, key_24)
decrypt, err = gaes.Decrypt(encrypt, key_24)
decrypt, err = gaes.Decrypt([]byte(content_24), key_24)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
gtest.Assert(decrypt, content)
encrypt, err = gaes.Encrypt(content, key_32)
decrypt, err = gaes.Decrypt(encrypt, key_32)
decrypt, err = gaes.Decrypt([]byte(content_32), key_32)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
gtest.Assert(decrypt, content)
encrypt, err = gaes.Encrypt(content, key_32, iv)
decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
decrypt, err = gaes.Decrypt([]byte(content_16_iv), key_16, iv)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
gtest.Assert(decrypt, content)
encrypt, err = gaes.Encrypt(content, key_32, iv)
decrypt, err = gaes.Decrypt(encrypt, keys, iv)
decrypt, err = gaes.Decrypt([]byte(content_32_iv), key_32, iv)
gtest.Assert(err, nil)
gtest.Assert(decrypt, content)
decrypt, err = gaes.Decrypt([]byte(content_32_iv), keys, iv)
gtest.Assert(err, "invalid padding")
})
}
func TestEncryptErr(t *testing.T) {
gtest.Case(t, func() {
// encrypt key error
_, err := gaes.Encrypt(content, key_err)
gtest.AssertNE(err, nil)
})
}
func TestDecryptErr(t *testing.T) {
gtest.Case(t, func() {
// decrypt key error
encrypt, err := gaes.Encrypt(content, key_16)
_, err = gaes.Decrypt(encrypt, key_err)
gtest.AssertNE(err, nil)
// decrypt content too short error
_, err = gaes.Decrypt([]byte("test"), key_16)
gtest.AssertNE(err, nil)
// decrypt content size error
_, err = gaes.Decrypt(key_17, key_16)
gtest.AssertNE(err, nil)
})
}
func TestPKCS5UnPaddingErr(t *testing.T) {
gtest.Case(t, func() {
// PKCS5UnPadding blockSize zero
_, err := gaes.PKCS5UnPadding(content, 0)
gtest.AssertNE(err, nil)
// PKCS5UnPadding src len zero
_, err = gaes.PKCS5UnPadding([]byte(""), 16)
gtest.AssertNE(err, nil)
// PKCS5UnPadding src len > blockSize
_, err = gaes.PKCS5UnPadding(key_17, 16)
gtest.AssertNE(err, nil)
// PKCS5UnPadding src len > blockSize
_, err = gaes.PKCS5UnPadding(key_32_err, 32)
gtest.AssertNE(err, nil)
})
}

View File

@ -9,13 +9,14 @@
package gcrc32_test
import (
"github.com/gogf/gf/g/crypto/gmd5"
"testing"
"github.com/gogf/gf/g/crypto/gcrc32"
"github.com/gogf/gf/g/test/gtest"
)
func TestEncrypt(t *testing.T) {
func TestEncryptString(t *testing.T) {
gtest.Case(t, func() {
s := "pibigstar"
result := 693191136
@ -25,3 +26,19 @@ func TestEncrypt(t *testing.T) {
gtest.AssertEQ(int(encrypt2), result)
})
}
func TestEncrypt(t *testing.T) {
gtest.Case(t, func() {
s := "pibigstar"
result := 693191136
encrypt1 := gcrc32.Encrypt(s)
encrypt2 := gcrc32.Encrypt([]byte(s))
gtest.AssertEQ(int(encrypt1), result)
gtest.AssertEQ(int(encrypt2), result)
strmd5 := gmd5.Encrypt(s)
test1 := gcrc32.Encrypt(strmd5)
test2 := gcrc32.Encrypt([]byte(strmd5))
gtest.AssertEQ(test2, test1)
})
}

View File

@ -162,7 +162,7 @@ var (
)
// New creates ORM DB object with global configurations.
// The param <name> specifies the configuration group name,
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func New(name ...string) (db DB, err error) {
group := configs.defaultGroup
@ -210,7 +210,7 @@ func New(name ...string) (db DB, err error) {
}
// Instance returns an instance for DB operations.
// The param <name> specifies the configuration group name,
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func Instance(name ...string) (db DB, err error) {
group := configs.defaultGroup

View File

@ -7,133 +7,203 @@
package gredis_test
import (
"github.com/gogf/gf/g/database/gredis"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
"github.com/gogf/gf/g/database/gredis"
"github.com/gogf/gf/g/test/gtest"
redis2 "github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
"testing"
"time"
)
var (
config = gredis.Config{
Host : "127.0.0.1",
Port : 6379,
Db : 1,
}
config = gredis.Config{
Host: "127.0.0.1",
Port: 6379,
Db: 1,
}
)
func Test_NewClose(t *testing.T) {
gtest.Case(t, func() {
redis := gredis.New(config)
gtest.AssertNE(redis, nil)
err := redis.Close()
gtest.Assert(err, nil)
})
gtest.Case(t, func() {
redis := gredis.New(config)
gtest.AssertNE(redis, nil)
err := redis.Close()
gtest.Assert(err, nil)
})
}
func Test_Do(t *testing.T) {
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
_, err := redis.Do("SET", "k", "v")
gtest.Assert(err, nil)
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
_, err := redis.Do("SET", "k", "v")
gtest.Assert(err, nil)
r, err := redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
r, err := redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = redis.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
_, err = redis.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Send(t *testing.T) {
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
err := redis.Send("SET", "k", "v")
gtest.Assert(err, nil)
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
err := redis.Send("SET", "k", "v")
gtest.Assert(err, nil)
r, err := redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
})
r, err := redis.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
})
}
func Test_Stats(t *testing.T) {
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
redis.SetMaxIdle(2)
redis.SetMaxActive(100)
redis.SetIdleTimeout(500*time.Millisecond)
redis.SetMaxConnLifetime(500*time.Millisecond)
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
redis.SetMaxIdle(2)
redis.SetMaxActive(100)
redis.SetIdleTimeout(500 * time.Millisecond)
redis.SetMaxConnLifetime(500 * time.Millisecond)
array := make([]*gredis.Conn, 0)
for i := 0; i < 10; i++ {
array = append(array, redis.Conn())
}
stats := redis.Stats()
gtest.Assert(stats.ActiveCount, 10)
gtest.Assert(stats.IdleCount, 0)
for i := 0; i < 10; i++ {
array[i].Close()
}
stats = redis.Stats()
gtest.Assert(stats.ActiveCount, 2)
gtest.Assert(stats.IdleCount, 2)
//time.Sleep(3000*time.Millisecond)
//stats = redis.Stats()
//fmt.Println(stats)
//gtest.Assert(stats.ActiveCount, 0)
//gtest.Assert(stats.IdleCount, 0)
})
array := make([]*gredis.Conn, 0)
for i := 0; i < 10; i++ {
array = append(array, redis.Conn())
}
stats := redis.Stats()
gtest.Assert(stats.ActiveCount, 10)
gtest.Assert(stats.IdleCount, 0)
for i := 0; i < 10; i++ {
array[i].Close()
}
stats = redis.Stats()
gtest.Assert(stats.ActiveCount, 2)
gtest.Assert(stats.IdleCount, 2)
//time.Sleep(3000*time.Millisecond)
//stats = redis.Stats()
//fmt.Println(stats)
//gtest.Assert(stats.ActiveCount, 0)
//gtest.Assert(stats.IdleCount, 0)
})
}
func Test_Conn(t *testing.T) {
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
gtest.Case(t, func() {
redis := gredis.New(config)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
group := "my-test"
gredis.SetConfig(config, group)
defer gredis.RemoveConfig(group)
redis := gredis.Instance(group)
defer redis.Close()
gtest.Case(t, func() {
group := "my-test"
gredis.SetConfig(config, group)
defer gredis.RemoveConfig(group)
redis := gredis.Instance(group)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
conn := redis.Conn()
defer conn.Close()
_, err := conn.Do("SET", "k", "v")
gtest.Assert(err, nil)
_, err := conn.Do("SET", "k", "v")
gtest.Assert(err, nil)
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Basic(t *testing.T) {
gtest.Case(t, func() {
config1 := gredis.Config{
Host: "127.0.0.2",
Port: 6379,
Db: 1,
}
redis := gredis.New(config1)
_, err := redis.Do("info")
gtest.AssertNE(err, nil)
config1 = gredis.Config{
Host: "127.0.0.1",
Port: 6379,
Db: 1,
Pass: "666666",
}
redis = gredis.New(config1)
_, err = redis.Do("info")
gtest.AssertNE(err, nil)
config1 = gredis.Config{
Host: "127.0.0.1",
Port: 6379,
Db: 100,
}
redis = gredis.New(config1)
_, err = redis.Do("info")
gtest.AssertNE(err, nil)
redis = gredis.Instance("gf")
gtest.Assert(redis == nil, true)
gredis.ClearConfig()
redis = gredis.New(config)
defer redis.Close()
_, err = redis.DoVar("SET", "k", "v")
gtest.Assert(err, nil)
v, err := redis.DoVar("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(v.String(), "v")
conn := redis.GetConn()
_, err = conn.DoVar("SET", "k", "v")
gtest.Assert(err, nil)
//v, err = conn.ReceiveVar()
//gtest.Assert(err, nil)
//gtest.Assert(v.String(), "v")
psc := redis2.PubSubConn{Conn: conn}
psc.Subscribe("gf")
redis.DoVar("PUBLISH", "gf", "gf test")
go func() {
for {
v, _ := conn.ReceiveVar()
switch obj := v.Val().(type) {
case redis2.Message:
gtest.Assert(string(obj.Data), "gf test")
case redis2.Subscription:
}
}
}()
time.Sleep(time.Second)
})
}

View File

@ -1,55 +1,110 @@
// 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,
// You can obtain one at https://github.com/gogf/gf.
// @author wenzi1
// @date 20180604
// Package gcharset provides converting string to requested character encoding.
// Package charset implements character-set conversion functionality.
//
// 字符集转换方法,
// 使用mahonia实现的字符集转换方法支持的字符集包括常见的utf8/UTF-16/UTF-16LE/macintosh/big5/gbk/gb18030,支持的全量字符集可以参考mahonia包
// Supported Character Set:
//
// Chinese : GBK/GB18030/GB2312/Big5
//
// Japanese: EUCJP/ISO2022JP/ShiftJIS
//
// Korean : EUCKR
//
// Unicode : UTF-8/UTF-16/UTF-16BE/UTF-16LE
//
// Other : macintosh/IBM*/Windows*/ISO-*
package gcharset
import (
"github.com/gogf/gf/third/github.com/axgle/mahonia"
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/third/golang.org/x/text/encoding"
"github.com/gogf/gf/third/golang.org/x/text/encoding/ianaindex"
"github.com/gogf/gf/third/golang.org/x/text/transform"
"io/ioutil"
)
var (
// Alias for charsets.
charsetAlias = map[string]string {
"HZGB2312" : "HZ-GB-2312",
"hzgb2312" : "HZ-GB-2312",
"GB2312" : "HZ-GB-2312",
"gb2312" : "HZ-GB-2312",
}
)
// 2个字符集之间的转换
// Supported returns whether charset <charset> is supported.
func Supported(charset string) bool {
return getEncoding(charset) != nil
}
// Convert converts <src> charset encoding from <srcCharset> to <dstCharset>,
// and returns the converted string.
// It returns <src> as <dst> if it fails converting.
func Convert(dstCharset string, srcCharset string, src string) (dst string, err error) {
s := mahonia.GetCharset(srcCharset)
if s == nil {
return "", errors.New(fmt.Sprintf("not support charset:%s", srcCharset))
if dstCharset == srcCharset {
return src, nil
}
d := mahonia.GetCharset(dstCharset)
if d == nil {
return "", errors.New(fmt.Sprintf("not support charset:%s", dstCharset))
dst = src
// Converting <src> to UTF-8.
if srcCharset != "UTF-8" {
if e := getEncoding(srcCharset); e != nil {
tmp, err := ioutil.ReadAll(
transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()),
)
if err != nil {
return "", fmt.Errorf("%s to utf8 failed. %v", srcCharset, err)
}
src = string(tmp)
} else {
return dst, errors.New(fmt.Sprintf("unsupport srcCharset: %s", srcCharset))
}
}
srctmp := src
if s.Name != "UTF-8" {
srctmp = s.NewDecoder().ConvertString(srctmp)
// Do the converting from UTF-8 to <dstCharset>.
if dstCharset != "UTF-8" {
if e := getEncoding(dstCharset); e != nil {
tmp, err := ioutil.ReadAll(
transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()),
)
if err != nil {
return "", fmt.Errorf("utf to %s failed. %v", dstCharset, err)
}
dst = string(tmp)
} else {
return dst, errors.New(fmt.Sprintf("unsupport dstCharset: %s", dstCharset))
}
} else {
dst = src
}
dst = srctmp
if d.Name != "UTF-8" {
dst = d.NewEncoder().ConvertString(dst)
}
return dst, nil
}
// 指定字符集转UTF8
func ToUTF8(charset string, src string) (dst string, err error) {
return Convert("UTF-8", charset, src)
// ToUTF8 converts <src> charset encoding from <srcCharset> to UTF-8 ,
// and returns the converted string.
func ToUTF8(srcCharset string, src string) (dst string, err error) {
return Convert("UTF-8", srcCharset, src)
}
// UTF8转指定字符集
func UTF8To(charset string, src string) (dst string, err error) {
return Convert(charset, "UTF-8", src)
// UTF8To converts <src> charset encoding from UTF-8 to <dstCharset>,
// and returns the converted string.
func UTF8To(dstCharset string, src string) (dst string, err error) {
return Convert(dstCharset, "UTF-8", src)
}
// getEncoding returns the encoding.Encoding interface object for <charset>.
// It returns nil if <charset> is not supported.
func getEncoding(charset string) encoding.Encoding {
if c, ok := charsetAlias[charset]; ok {
charset = c
}
if e, err := ianaindex.MIB.Encoding(charset); err == nil && e != nil {
return e
}
return nil
}

View File

@ -8,22 +8,23 @@ package gcharset_test
import (
"github.com/gogf/gf/g/encoding/gcharset"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
{"Résumé", "Résumé", "utf8"},
{"Résumé", "R\xe9sum\xe9", "latin-1"},
{"Résumé", "Résumé", "utf-8"},
//{"Résumé", "R\xe9sum\xe9", "latin-1"},
{"これは漢字です。", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"},
{"これは漢字です。", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"},
{"これは漢字です。", "\xfe\xff0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16"},
{"𝄢𝄞𝄪𝄫", "\xfe\xff\xd8\x34\xdd\x22\xd8\x34\xdd\x1e\xd8\x34\xdd\x2a\xd8\x34\xdd\x2b", "UTF-16"},
{"Hello, world", "Hello, world", "ASCII"},
//{"Hello, world", "Hello, world", "ASCII"},
{"Gdańsk", "Gda\xf1sk", "ISO-8859-2"},
{"Ââ Čč Đđ Ŋŋ Õõ Šš Žž Åå Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"},
{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"},
//{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"},
{"latviešu", "latvie\xf0u", "ISO-8859-13"},
{"Seònaid", "Se\xf2naid", "ISO-8859-14"},
{"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"},
@ -49,9 +50,12 @@ var testData = []struct {
{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
{"花间一壶酒,独酌无相亲。", "~{;(<dR;:x>F#,6@WCN^O`GW!#", "GB2312"},
{"花间一壶酒,独酌无相亲。", "~{;(<dR;:x>F#,6@WCN^O`GW!#", "HZGB2312"},
{"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"},
{"㧯", "\x82\x31\x89\x38", "gb18030"},
{"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"},
{"㧯", "㧯", "UTF-8"},
//{"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"},
{"これは漢字です。", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"},
}
@ -63,14 +67,43 @@ func TestDecode(t *testing.T) {
t.Errorf("Could not create decoder for %v", err)
continue
}
if str != data.utf8 {
t.Errorf("Unexpected value: %#v (expected %#v) %v", str, data.utf8, data.otherEncoding)
}
}
}
func TestUTF8To(t *testing.T) {
for _, data := range testData {
str := ""
str, err := gcharset.UTF8To(data.otherEncoding, data.utf8)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
}
if str != data.other {
t.Errorf("Unexpected value: %#v (expected %#v) %v", str, data.other, data.otherEncoding)
}
}
}
func TestToUTF8(t *testing.T) {
for _, data := range testData {
str := ""
str, err := gcharset.ToUTF8(data.otherEncoding, data.other)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
}
if str != data.utf8 {
t.Errorf("Unexpected value: %#v (expected %#v)", str, data.utf8)
}
}
}
func TestEncode(t *testing.T) {
for _, data := range testData {
str := ""
@ -101,4 +134,32 @@ func TestConvert(t *testing.T) {
if str != dst {
t.Errorf("unexpected value:%#v (expected %#v)", str, dst)
}
}
}
func TestConvertErr(t *testing.T) {
gtest.Case(t, func() {
srcCharset := "big5"
dstCharset := "gbk"
src := "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed"
s1, e1 := gcharset.Convert(srcCharset, srcCharset, src)
gtest.Assert(e1, nil)
gtest.Assert(s1, src)
s2, e2 := gcharset.Convert(dstCharset, "no this charset", src)
gtest.AssertNE(e2, nil)
gtest.Assert(s2, src)
s3, e3 := gcharset.Convert("no this charset", srcCharset, src)
gtest.AssertNE(e3, nil)
gtest.Assert(s3, src)
})
}
func TestSupported(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gcharset.Supported("UTF-8"), true)
gtest.Assert(gcharset.Supported("UTF-80"), false)
})
}

View File

@ -5,8 +5,6 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gcompress provides kinds of compression algorithms for binary/bytes data.
//
// 数据压缩/解压.
package gcompress
import (
@ -16,19 +14,19 @@ import (
"io"
)
// 进行zlib压缩
// Zlib compresses <data> with zlib algorithm.
func Zlib(data []byte) []byte {
if data == nil || len(data) < 13 {
return data
}
var in bytes.Buffer
w := zlib.NewWriter(&in)
w.Write(data)
w.Close()
w := zlib.NewWriter(&in)
_, _ = w.Write(data)
_ = w.Close()
return in.Bytes()
}
// 进行zlib解压缩
// UnZlib decompresses <data> with zlib algorithm.
func UnZlib(data []byte) []byte {
if data == nil || len(data) < 13 {
return data
@ -39,32 +37,32 @@ func UnZlib(data []byte) []byte {
if err != nil {
return nil
}
io.Copy(&out, r)
_, _ = io.Copy(&out, r)
return out.Bytes()
}
//做gzip解压缩
func UnGzip(data []byte) []byte {
var buf bytes.Buffer
content := bytes.NewReader(data)
zipdata, err := gzip.NewReader(content)
if err != nil {
return nil
}
io.Copy(&buf, zipdata)
zipdata.Close()
return buf.Bytes()
}
//做gzip压缩
// Gzip compresses <data> with gzip algorithm.
func Gzip(data []byte) []byte {
var buf bytes.Buffer
zip := gzip.NewWriter(&buf)
var buf bytes.Buffer
zip := gzip.NewWriter(&buf)
_, err := zip.Write(data)
if err != nil {
return nil
}
zip.Close()
_ = zip.Close()
return buf.Bytes()
}
}
// UnGzip decompresses <data> with gzip algorithm.
func UnGzip(data []byte) []byte {
var buf bytes.Buffer
content := bytes.NewReader(data)
zipData, err := gzip.NewReader(content)
if err != nil {
return nil
}
_, _ = io.Copy(&buf, zipData)
_ = zipData.Close()
return buf.Bytes()
}

View File

@ -3,6 +3,7 @@
// 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 gcompress_test
import (
@ -18,6 +19,11 @@ func TestZlib(t *testing.T) {
gtest.Assert(gcompress.Zlib([]byte(src)), dst)
gtest.Assert(gcompress.UnZlib(dst), []byte(src))
gtest.Assert(gcompress.Zlib(nil), nil)
gtest.Assert(gcompress.UnZlib(nil), nil)
gtest.Assert(gcompress.UnZlib(dst[1:]), nil)
})
}
@ -36,7 +42,10 @@ func TestGzip(t *testing.T) {
0x00, 0x00,
}
gtest.Assert(gcompress.Gzip([]byte(src)), gzip)
arr := []byte(src)
gtest.Assert(gcompress.Gzip(arr), gzip)
gtest.Assert(gcompress.UnGzip(gzip), []byte(src))
gtest.Assert(gcompress.UnGzip(gzip), arr)
gtest.Assert(gcompress.UnGzip(gzip[1:]), nil)
}

View File

@ -5,15 +5,13 @@
// You can obtain one at https://github.com/gogf/gf.
// Package ghash provides some popular hash functions(uint32/uint64) in go.
//
// 常用的hash函数.
package ghash
// BKDR Hash Function
func BKDRHash(str []byte) uint32 {
var seed uint32 = 131; // 31 131 1313 13131 131313 etc..
var hash uint32 = 0;
var seed uint32 = 131 // 31 131 1313 13131 131313 etc..
var hash uint32 = 0
for i := 0; i < len(str); i++ {
hash = hash * seed + uint32(str[i])
}
@ -22,8 +20,8 @@ func BKDRHash(str []byte) uint32 {
// BKDR Hash Function 64
func BKDRHash64(str []byte) uint64 {
var seed uint64 = 131; // 31 131 1313 13131 131313 etc..
var hash uint64 = 0;
var seed uint64 = 131 // 31 131 1313 13131 131313 etc..
var hash uint64 = 0
for i := 0; i < len(str); i++ {
hash = hash * seed + uint64(str[i])
}
@ -32,78 +30,78 @@ func BKDRHash64(str []byte) uint64 {
// SDBM Hash
func SDBMHash(str []byte) uint32 {
var hash uint32 = 0;
var hash uint32 = 0
for i := 0; i < len(str); i++ {
// equivalent to: hash = 65599*hash + uint32(str[i]);
hash = uint32(str[i]) + (hash << 6) + (hash << 16) - hash;
hash = uint32(str[i]) + (hash << 6) + (hash << 16) - hash
}
return hash
}
// SDBM Hash 64
func SDBMHash64(str []byte) uint64 {
var hash uint64 = 0;
var hash uint64 = 0
for i := 0; i < len(str); i++ {
// equivalent to: hash = 65599*hash + uint32(str[i]);
hash = uint64(str[i]) + (hash << 6) + (hash << 16) - hash;
// equivalent to: hash = 65599*hash + uint32(str[i])
hash = uint64(str[i]) + (hash << 6) + (hash << 16) - hash
}
return hash
}
// RS Hash Function
func RSHash(str []byte) uint32 {
var b uint32 = 378551;
var a uint32 = 63689;
var hash uint32 = 0;
var b uint32 = 378551
var a uint32 = 63689
var hash uint32 = 0
for i := 0; i < len(str); i++ {
hash = hash * a + uint32(str[i]);
a *= b;
hash = hash * a + uint32(str[i])
a *= b
}
return hash
}
// RS Hash Function 64
func RSHash64(str []byte) uint64 {
var b uint64 = 378551;
var a uint64 = 63689;
var hash uint64 = 0;
var b uint64 = 378551
var a uint64 = 63689
var hash uint64 = 0
for i := 0; i < len(str); i++ {
hash = hash * a + uint64(str[i]);
a *= b;
hash = hash * a + uint64(str[i])
a *= b;
}
return hash
}
// JS Hash Function
func JSHash(str []byte) uint32 {
var hash uint32 = 1315423911;
var hash uint32 = 1315423911
for i := 0; i < len(str); i++ {
hash ^= ((hash << 5) + uint32(str[i]) + (hash >> 2));
hash ^= (hash << 5) + uint32(str[i]) + (hash >> 2)
}
return hash
}
// JS Hash Function 64
func JSHash64(str []byte) uint64 {
var hash uint64 = 1315423911;
var hash uint64 = 1315423911
for i := 0; i < len(str); i++ {
hash ^= ((hash << 5) + uint64(str[i]) + (hash >> 2));
hash ^= (hash << 5) + uint64(str[i]) + (hash >> 2)
}
return hash
}
// P. J. Weinberger Hash Function
func PJWHash(str []byte) uint32 {
var BitsInUnignedInt uint32 = (4 * 8);
var ThreeQuarters uint32 = ((BitsInUnignedInt * 3) / 4);
var OneEighth uint32 = (BitsInUnignedInt / 8);
var HighBits uint32 = (0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
var hash uint32 = 0;
var test uint32 = 0;
var BitsInUnignedInt uint32 = 4 * 8
var ThreeQuarters uint32 = (BitsInUnignedInt * 3) / 4
var OneEighth uint32 = BitsInUnignedInt / 8
var HighBits uint32 = (0xFFFFFFFF) << (BitsInUnignedInt - OneEighth)
var hash uint32 = 0
var test uint32 = 0
for i := 0; i < len(str); i++ {
hash = (hash << OneEighth) + uint32(str[i]);
hash = (hash << OneEighth) + uint32(str[i])
if test = hash & HighBits; test != 0 {
hash = ((hash ^ (test >> ThreeQuarters)) & (^HighBits + 1));
hash = (hash ^ (test >> ThreeQuarters)) & (^HighBits + 1)
}
}
return hash
@ -111,16 +109,16 @@ func PJWHash(str []byte) uint32 {
// P. J. Weinberger Hash Function 64
func PJWHash64(str []byte) uint64 {
var BitsInUnignedInt uint64 = (4 * 8);
var ThreeQuarters uint64 = ((BitsInUnignedInt * 3) / 4);
var OneEighth uint64 = (BitsInUnignedInt / 8);
var HighBits uint64 = (0xFFFFFFFFFFFFFFFF) << (BitsInUnignedInt - OneEighth);
var hash uint64 = 0;
var test uint64 = 0;
var BitsInUnignedInt uint64 = 4 * 8
var ThreeQuarters uint64 = (BitsInUnignedInt * 3) / 4
var OneEighth uint64 = BitsInUnignedInt / 8
var HighBits uint64 = (0xFFFFFFFFFFFFFFFF) << (BitsInUnignedInt - OneEighth)
var hash uint64 = 0
var test uint64 = 0
for i := 0; i < len(str); i++ {
hash = (hash << OneEighth) + uint64(str[i]);
hash = (hash << OneEighth) + uint64(str[i])
if test = hash & HighBits; test != 0 {
hash = ((hash ^ (test >> ThreeQuarters)) & (^HighBits + 1));
hash = (hash ^ (test >> ThreeQuarters)) & (^HighBits + 1)
}
}
return hash
@ -128,13 +126,13 @@ func PJWHash64(str []byte) uint64 {
// ELF Hash Function
func ELFHash(str []byte) uint32 {
var hash uint32 = 0;
var x uint32 = 0;
var hash uint32 = 0
var x uint32 = 0
for i := 0; i < len(str); i++ {
hash = (hash << 4) + uint32(str[i]);
hash = (hash << 4) + uint32(str[i])
if x = hash & 0xF0000000; x != 0 {
hash ^= (x >> 24);
hash &= ^x + 1;
hash ^= x >> 24
hash &= ^x + 1
}
}
return hash
@ -142,13 +140,13 @@ func ELFHash(str []byte) uint32 {
// ELF Hash Function 64
func ELFHash64(str []byte) uint64 {
var hash uint64 = 0;
var x uint64 = 0;
var hash uint64 = 0
var x uint64 = 0
for i := 0; i < len(str); i++ {
hash = (hash << 4) + uint64(str[i]);
hash = (hash << 4) + uint64(str[i])
if x = hash & 0xF000000000000000; x != 0 {
hash ^= (x >> 24);
hash &= ^x + 1;
hash ^= x >> 24
hash &= ^x + 1
}
}
return hash
@ -156,30 +154,30 @@ func ELFHash64(str []byte) uint64 {
// DJB Hash Function
func DJBHash(str []byte) uint32 {
var hash uint32 = 5381;
var hash uint32 = 5381
for i := 0; i < len(str); i++ {
hash += (hash << 5) + uint32(str[i]);
hash += (hash << 5) + uint32(str[i])
}
return hash
}
// DJB Hash Function 64
func DJBHash64(str []byte) uint64 {
var hash uint64 = 5381;
var hash uint64 = 5381
for i := 0; i < len(str); i++ {
hash += (hash << 5) + uint64(str[i]);
hash += (hash << 5) + uint64(str[i])
}
return hash
}
// AP Hash Function
func APHash(str []byte) uint32 {
var hash uint32 = 0;
var hash uint32 = 0
for i := 0; i < len(str); i++ {
if ((i & 1) == 0) {
hash ^= ((hash << 7) ^ uint32(str[i]) ^ (hash >> 3));
if (i & 1) == 0 {
hash ^= (hash << 7) ^ uint32(str[i]) ^ (hash >> 3)
} else {
hash ^= (^((hash << 11) ^ uint32(str[i]) ^ (hash >> 5)) + 1);
hash ^= ^((hash << 11) ^ uint32(str[i]) ^ (hash >> 5)) + 1
}
}
return hash
@ -187,12 +185,12 @@ func APHash(str []byte) uint32 {
// AP Hash Function 64
func APHash64(str []byte) uint64 {
var hash uint64 = 0;
var hash uint64 = 0
for i := 0; i < len(str); i++ {
if ((i & 1) == 0) {
hash ^= ((hash << 7) ^ uint64(str[i]) ^ (hash >> 3));
if (i & 1) == 0 {
hash ^= (hash << 7) ^ uint64(str[i]) ^ (hash >> 3)
} else {
hash ^= (^((hash << 11) ^ uint64(str[i]) ^ (hash >> 5)) + 1);
hash ^= ^((hash << 11) ^ uint64(str[i]) ^ (hash >> 5)) + 1
}
}
return hash

View File

@ -0,0 +1,140 @@
package ghash_test
import (
"testing"
"github.com/gogf/gf/g/encoding/ghash"
"github.com/gogf/gf/g/test/gtest"
)
var (
strBasic = []byte("This is the test string for hash.")
)
func Test_BKDRHash(t *testing.T) {
var x uint32 = 200645773
gtest.Case(t, func() {
j := ghash.BKDRHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_BKDRHash64(t *testing.T) {
var x uint64 = 4214762819217104013
gtest.Case(t, func() {
j := ghash.BKDRHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_SDBMHash(t *testing.T) {
var x uint32 = 1069170245
gtest.Case(t, func() {
j := ghash.SDBMHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_SDBMHash64(t *testing.T) {
var x uint64 = 9881052176572890693
gtest.Case(t, func() {
j := ghash.SDBMHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_RSHash(t *testing.T) {
var x uint32 = 1944033799
gtest.Case(t, func() {
j := ghash.RSHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_RSHash64(t *testing.T) {
var x uint64 = 13439708950444349959
gtest.Case(t, func() {
j := ghash.RSHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_JSHash(t *testing.T) {
var x uint32 = 498688898
gtest.Case(t, func() {
j := ghash.JSHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_JSHash64(t *testing.T) {
var x uint64 = 13410163655098759877
gtest.Case(t, func() {
j := ghash.JSHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_PJWHash(t *testing.T) {
var x uint32 = 7244206
gtest.Case(t, func() {
j := ghash.PJWHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_PJWHash64(t *testing.T) {
var x uint64 = 31150
gtest.Case(t, func() {
j := ghash.PJWHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_ELFHash(t *testing.T) {
var x uint32 = 7244206
gtest.Case(t, func() {
j := ghash.ELFHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_ELFHash64(t *testing.T) {
var x uint64 = 31150
gtest.Case(t, func() {
j := ghash.ELFHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_DJBHash(t *testing.T) {
var x uint32 = 959862602
gtest.Case(t, func() {
j := ghash.DJBHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_DJBHash64(t *testing.T) {
var x uint64 = 2519720351310960458
gtest.Case(t, func() {
j := ghash.DJBHash64(strBasic)
gtest.Assert(j, x)
})
}
func Test_APHash(t *testing.T) {
var x uint32 = 3998202516
gtest.Case(t, func() {
j := ghash.APHash(strBasic)
gtest.Assert(j, x)
})
}
func Test_APHash64(t *testing.T) {
var x uint64 = 2531023058543352243
gtest.Case(t, func() {
j := ghash.APHash64(strBasic)
gtest.Assert(j, x)
})
}

View File

@ -108,7 +108,11 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
if len((*pointer).([]interface{})) > valn {
if removed && value == nil {
// 删除数据元素
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
if pparent == nil {
*pointer = append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...)
} else {
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
}
} else {
(*pointer).([]interface{})[valn] = value
}

View File

@ -136,7 +136,10 @@ func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
var err error
var result interface{}
b := gconv.Bytes(data)
t := "json"
t := ""
if len(b) == 0 {
return New(nil, unsafe...), nil
}
// auto check data type
if json.Valid(b) {
t = "json"

View File

@ -7,221 +7,221 @@
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_New(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
}
func Test_NewUnsafe(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Valid(t *testing.T) {
data1 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
data2 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
gtest.Case(t, func() {
gtest.Assert(gjson.Valid(data1), true)
gtest.Assert(gjson.Valid(data2), false)
})
data1 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
data2 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
gtest.Case(t, func() {
gtest.Assert(gjson.Valid(data1), true)
gtest.Assert(gjson.Valid(data2), false)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gjson.Encode(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gjson.Encode(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
}
func Test_Decode(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
v, err := gjson.Decode(data)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
var v interface{}
err := gjson.DecodeTo(data, &v)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
v, err := gjson.Decode(data)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n": 123456789,
"a": g.Slice{1, 2, 3},
"m": g.Map{
"k": "v",
},
})
})
gtest.Case(t, func() {
var v interface{}
err := gjson.DecodeTo(data, &v)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n": 123456789,
"a": g.Slice{1, 2, 3},
"m": g.Map{
"k": "v",
},
})
})
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
j.SetSplitChar(byte('#'))
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
j.SetSplitChar(byte('#'))
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
}
func Test_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
}
func Test_GetToVar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k" : "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k": "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
}
func Test_GetMap(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), nil)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k": "v"})
gtest.Assert(j.GetMap("a"), nil)
})
}
func Test_GetJson(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j2 := j.GetJson("m")
gtest.AssertNE(j2, nil)
gtest.Assert(j2.Get("k"), "v")
gtest.Assert(j2.Get("a"), nil)
gtest.Assert(j2.Get("n"), nil)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j2 := j.GetJson("m")
gtest.AssertNE(j2, nil)
gtest.Assert(j2.Get("k"), "v")
gtest.Assert(j2.Get("a"), nil)
gtest.Assert(j2.Get("n"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k": "v"}})
gtest.Assert(j.GetArray("a"), g.Array{1, 2, 3})
})
}
func Test_GetString(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
}
func Test_GetStrings(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}
func Test_GetInterfaces(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k": "v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1, 2, 3})
})
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
@ -236,8 +236,8 @@ func Test_Append(t *testing.T) {
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},
"b": g.Slice{1},
"c": g.Slice{2},
})
})
gtest.Case(t, func() {
@ -282,21 +282,180 @@ func TestJson_Default(t *testing.T) {
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.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.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"},
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"},
"m1": g.Map{"k1": "v1"},
"m2": g.Map{"k2": "v2"},
})["m2"].Get("k2"), "v2")
})
}
func Test_Convert(t *testing.T) {
gtest.Case(t, func() {
j := gjson.New(`{"name":"gf"}`)
arr, err := j.ToXml()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "<name>gf</name>")
arr, err = j.ToXmlIndent()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "<name>gf</name>")
str, err := j.ToXmlString()
gtest.Assert(err, nil)
gtest.Assert(str, "<name>gf</name>")
str, err = j.ToXmlIndentString()
gtest.Assert(err, nil)
gtest.Assert(str, "<name>gf</name>")
arr, err = j.ToJsonIndent()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "{\n\t\"name\": \"gf\"\n}")
str, err = j.ToJsonIndentString()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "{\n\t\"name\": \"gf\"\n}")
arr, err = j.ToYaml()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "name: gf\n")
str, err = j.ToYamlString()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "name: gf\n")
arr, err = j.ToToml()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "name = \"gf\"\n")
str, err = j.ToTomlString()
gtest.Assert(err, nil)
gtest.Assert(string(arr), "name = \"gf\"\n")
})
}
func Test_Convert2(t *testing.T) {
gtest.Case(t, func() {
name := struct {
Name string
}{}
j := gjson.New(`{"name":"gf","time":"2019-06-12"}`)
gtest.Assert(j.Value().(g.Map)["name"], "gf")
gtest.Assert(j.GetMap("name1"), nil)
gtest.Assert(j.GetJson("name1"), nil)
gtest.Assert(j.GetJsons("name1"), nil)
gtest.Assert(j.GetJsonMap("name1"), nil)
gtest.Assert(j.Contains("name1"), false)
gtest.Assert(j.GetToVar("name1", &name) == nil, true)
gtest.Assert(j.GetToVar("name", &name) == nil, false)
gtest.Assert(j.Len("name1"), -1)
gtest.Assert(j.GetTime("time").Format("2006-01-02"), "2019-06-12")
gtest.Assert(j.GetGTime("time").Format("Y-m-d"), "2019-06-12")
gtest.Assert(j.GetDuration("time").String(), "0s")
err := j.ToStruct(&name)
gtest.Assert(err, nil)
gtest.Assert(name.Name, "gf")
err = j.Dump()
gtest.Assert(err, nil)
j = gjson.New(`{"person":{"name":"gf"}}`)
err = j.GetToStruct("person", &name)
gtest.Assert(err, nil)
gtest.Assert(name.Name, "gf")
j = gjson.New(`{"name":"gf""}`)
err = j.Dump()
gtest.Assert(err, nil)
j = gjson.New(`[1,2,3]`)
gtest.Assert(len(j.ToArray()), 3)
})
}
func Test_Basic(t *testing.T) {
gtest.Case(t, func() {
j := gjson.New(`{"name":"gf","time":"2019-06-12"}`)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("").(g.Map)["name"], "gf")
gtest.Assert(j.Get("").(g.Map)["name1"], nil)
j.SetViolenceCheck(false)
gtest.Assert(j.Get("").(g.Map)["name"], "gf")
err := j.Set("name", "gf1")
gtest.Assert(err, nil)
gtest.Assert(j.Get("name"), "gf1")
j = gjson.New(`[1,2,3]`)
err = j.Set("\"0\".1", 11)
gtest.Assert(err, nil)
gtest.Assert(j.Get("1"), 11)
j = gjson.New(`[1,2,3]`)
err = j.Set("11111111111111111111111", 11)
gtest.AssertNE(err, nil)
j = gjson.New(`[1,2,3]`)
err = j.Remove("1")
gtest.Assert(err, nil)
gtest.Assert(j.Get("0"), 1)
j = gjson.New(`[1,2,3]`)
err = j.Remove("3")
gtest.Assert(err, nil)
gtest.Assert(j.Get("0"), 1)
j = gjson.New(`[1,2,3]`)
err = j.Remove("0.3")
gtest.Assert(err, nil)
gtest.Assert(len(j.Get("0").([]interface{})), 3)
j = gjson.New(`[1,2,3]`)
err = j.Remove("0.a")
gtest.Assert(err, nil)
gtest.Assert(len(j.Get("0").(g.Map)), 0)
name := struct {
Name string
}{Name: "gf"}
j = gjson.New(name)
gtest.Assert(j.Get("Name"), "gf")
err = j.Remove("Name")
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name"), nil)
err = j.Set("Name", "gf1")
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name"), "gf1")
j = gjson.New(nil)
err = j.Remove("Name")
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name"), nil)
j = gjson.New(name)
gtest.Assert(j.Get("Name"), "gf")
err = j.Set("Name1", g.Map{"Name": "gf1"})
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name1").(g.Map)["Name"], "gf1")
err = j.Set("Name2", g.Slice{1, 2, 3})
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name2").(g.Slice)[0], 1)
err = j.Set("Name3", name)
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name3").(g.Map)["Name"], "gf")
err = j.Set("Name4", &name)
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name4").(g.Map)["Name"], "gf")
arr := [3]int{1, 2, 3}
err = j.Set("Name5", arr)
gtest.Assert(err, nil)
gtest.Assert(j.Get("Name5").(g.Array)[0], 1)
})
}

View File

@ -7,66 +7,65 @@
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Load_JSON(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
@ -82,13 +81,13 @@ func Test_Load_XML(t *testing.T) {
</Output>`
j, err := gjson.LoadContent(xml)
gtest.Assert(err, nil)
gtest.Assert(j.Get("Output.ipageIndex"), "2")
gtest.Assert(j.Get("Output.ipageIndex"), "2")
gtest.Assert(j.Get("Output.itotalRecords"), "GF框架")
})
}
func Test_Load_YAML1(t *testing.T) {
data := []byte(`
data := []byte(`
a:
- 1
- 2
@ -97,78 +96,102 @@ m:
k: v
"n": 123456789
`)
// YAML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_YAML2(t *testing.T) {
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_TOML1(t *testing.T) {
data := []byte(`
data := []byte(`
a = ["1", "2", "3"]
n = "123456789"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_TOML2(t *testing.T) {
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_Basic(t *testing.T) {
gtest.Case(t, func() {
j := gjson.NewUnsafe()
gtest.Assert(j.Value(), nil)
_, err := gjson.Decode(nil)
gtest.AssertNE(err, nil)
_, err = gjson.DecodeToJson(nil)
gtest.AssertNE(err, nil)
j, err = gjson.LoadContent(nil)
gtest.Assert(err, nil)
gtest.Assert(j.Value(), nil)
j, err = gjson.LoadContent(`{"name": "gf"}`)
gtest.Assert(err, nil)
j, err = gjson.LoadContent(`{"name": "gf"""}`)
gtest.AssertNE(err, nil)
j = gjson.New(&g.Map{"name": "gf"})
gtest.Assert(j.GetString("name"), "gf")
})
}

View File

@ -7,202 +7,314 @@
package gparser_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_New(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
v := j.Value().(g.Map)
gtest.Assert(v["n"], 123456789)
})
}
func Test_NewUnsafe(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gparser.VarToJson(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gparser.VarToJson(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
}
func Test_Decode(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
j.SetSplitChar(byte('#'))
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
j.SetSplitChar(byte('#'))
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
}
func Test_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
}
func Test_GetToVar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j := gparser.New(data)
gtest.AssertNE(j, nil)
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j := gparser.New(data)
gtest.AssertNE(j, nil)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k" : "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k": "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
}
func Test_GetMap(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), nil)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k": "v"})
gtest.Assert(j.GetMap("a"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k": "v"}})
gtest.Assert(j.GetArray("a"), g.Array{1, 2, 3})
})
}
func Test_GetString(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
}
func Test_GetStrings(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}
func Test_GetInterfaces(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k": "v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1, 2, 3})
})
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gparser.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 := gparser.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 := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gparser.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 := gparser.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}
func Test_Convert(t *testing.T) {
gtest.Case(t, func() {
p := gparser.New(`{"name":"gf","bool":true,"int":1,"float":1,"ints":[1,2],"floats":[1,2],"time":"2019-06-12","person": {"name": "gf"}}`)
gtest.Assert(p.GetVar("name").String(), "gf")
gtest.Assert(p.GetString("name"), "gf")
gtest.Assert(p.GetBool("bool"), true)
gtest.Assert(p.GetInt("int"), 1)
gtest.Assert(p.GetInt8("int"), 1)
gtest.Assert(p.GetInt16("int"), 1)
gtest.Assert(p.GetInt32("int"), 1)
gtest.Assert(p.GetInt64("int"), 1)
gtest.Assert(p.GetUint("int"), 1)
gtest.Assert(p.GetUint8("int"), 1)
gtest.Assert(p.GetUint16("int"), 1)
gtest.Assert(p.GetUint32("int"), 1)
gtest.Assert(p.GetUint64("int"), 1)
gtest.Assert(p.GetInts("ints")[0], 1)
gtest.Assert(p.GetFloat32("float"), 1)
gtest.Assert(p.GetFloat64("float"), 1)
gtest.Assert(p.GetFloats("floats")[0], 1)
gtest.Assert(p.GetTime("time").Format("2006-01-02"), "2019-06-12")
gtest.Assert(p.GetGTime("time").Format("Y-m-d"), "2019-06-12")
gtest.Assert(p.GetDuration("time").String(), "0s")
name := struct {
Name string
}{}
err := p.GetToStruct("person", &name)
gtest.Assert(err, nil)
gtest.Assert(name.Name, "gf")
gtest.Assert(p.ToMap()["name"], "gf")
err = p.ToStruct(&name)
gtest.Assert(err, nil)
gtest.Assert(name.Name, "gf")
p.Dump()
p = gparser.New(`[0,1,2]`)
gtest.Assert(p.ToArray()[0], 0)
err = gparser.VarToStruct(`{"name":"gf"}`, &name)
gtest.Assert(err, nil)
gtest.Assert(name.Name, "gf")
})
}
func Test_Convert2(t *testing.T) {
gtest.Case(t, func() {
xmlArr := []byte{60, 114, 111, 111, 116, 47, 62}
p := gparser.New(`<root></root>`)
arr, err := p.ToXml("root")
gtest.Assert(err, nil)
gtest.Assert(arr, xmlArr)
arr, err = gparser.VarToXml(`<root></root>`, "root")
gtest.Assert(err, nil)
gtest.Assert(arr, xmlArr)
arr, err = p.ToXmlIndent("root")
gtest.Assert(err, nil)
gtest.Assert(arr, xmlArr)
arr, err = gparser.VarToXmlIndent(`<root></root>`, "root")
gtest.Assert(err, nil)
gtest.Assert(arr, xmlArr)
p = gparser.New(`{"name":"gf"}`)
str, err := p.ToJsonString()
gtest.Assert(err, nil)
gtest.Assert(str, `{"name":"gf"}`)
str, err = gparser.VarToJsonString(`{"name":"gf"}`)
gtest.Assert(err, nil)
gtest.Assert(str, `{"name":"gf"}`)
jsonIndentArr := []byte{123, 10, 9, 34, 110, 97, 109, 101, 34, 58, 32, 34, 103, 102, 34, 10, 125}
arr, err = p.ToJsonIndent()
gtest.Assert(err, nil)
gtest.Assert(arr, jsonIndentArr)
arr, err = gparser.VarToJsonIndent(`{"name":"gf"}`)
gtest.Assert(err, nil)
gtest.Assert(arr, jsonIndentArr)
str, err = p.ToJsonIndentString()
gtest.Assert(err, nil)
gtest.Assert(str, "{\n\t\"name\": \"gf\"\n}")
str, err = gparser.VarToJsonIndentString(`{"name":"gf"}`)
gtest.Assert(err, nil)
gtest.Assert(str, "{\n\t\"name\": \"gf\"\n}")
yamlArr := []byte{124, 50, 10, 10, 32, 32, 110, 97, 109, 101, 58, 103, 102, 10}
p = gparser.New(`
name:gf
`)
arr, err = p.ToYaml()
gtest.Assert(err, nil)
gtest.Assert(arr, yamlArr)
arr, err = gparser.VarToYaml(`
name:gf
`)
gtest.Assert(err, nil)
gtest.Assert(arr, yamlArr)
tomlArr := []byte{110, 97, 109, 101, 32, 61, 32, 34, 103, 102, 34, 10}
p = gparser.New(`
name= "gf"
`)
arr, err = p.ToToml()
gtest.Assert(err, nil)
gtest.Assert(arr, tomlArr)
arr, err = gparser.VarToToml(`
name= "gf"
`)
gtest.Assert(err, nil)
gtest.Assert(arr, tomlArr)
})
}

View File

@ -11,62 +11,62 @@ import (
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"io/ioutil"
"testing"
)
func Test_Load_JSON(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
@ -82,13 +82,13 @@ func Test_Load_XML(t *testing.T) {
</Output>`
j, err := gparser.LoadContent(xml)
gtest.Assert(err, nil)
gtest.Assert(j.Get("Output.ipageIndex"), "2")
gtest.Assert(j.Get("Output.ipageIndex"), "2")
gtest.Assert(j.Get("Output.itotalRecords"), "GF框架")
})
}
func Test_Load_YAML1(t *testing.T) {
data := []byte(`
data := []byte(`
a:
- 1
- 2
@ -97,78 +97,93 @@ m:
k: v
"n": 123456789
`)
// YAML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_YAML2(t *testing.T) {
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_TOML1(t *testing.T) {
data := []byte(`
data := []byte(`
a = ["1", "2", "3"]
n = "123456789"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_TOML2(t *testing.T) {
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_Nil(t *testing.T) {
gtest.Case(t, func() {
p := gparser.NewUnsafe()
gtest.Assert(p.Value(), nil)
file := "test22222.json"
filePath := gfile.Pwd() + gfile.Separator + file
ioutil.WriteFile(filePath, []byte("{"), 0644)
defer gfile.Remove(filePath)
_, err := gparser.Load(file)
gtest.AssertNE(err, nil)
_, err = gparser.LoadContent("{")
gtest.AssertNE(err, nil)
})
}

View File

@ -9,8 +9,8 @@ package gxml
import (
"fmt"
"github.com/gogf/gf/g/encoding/gcharset"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/third/github.com/axgle/mahonia"
"github.com/gogf/gf/third/github.com/clbanning/mxj"
"strings"
)
@ -60,16 +60,17 @@ func convert(xml []byte) (res []byte, err error) {
if len(matchStr) == 2 {
xmlEncode = matchStr[1]
}
s := mahonia.GetCharset(xmlEncode)
if s == nil {
return nil, fmt.Errorf("not support charset:%s\n", xmlEncode)
}
res, err = gregex.Replace(patten, []byte(""), xml)
xmlEncode = strings.ToUpper(xmlEncode)
res, err = gregex.Replace(patten, []byte(""), xml)
if err != nil {
return nil, err
}
if !strings.EqualFold(s.Name, "UTF-8") {
res = []byte(s.NewDecoder().ConvertString(string(res)))
if xmlEncode != "UTF-8" && xmlEncode != "UTF8" {
dst, err := gcharset.Convert("UTF-8", xmlEncode, string(res))
if err != nil {
return nil, err
}
res = []byte(dst)
}
return res, nil
}

View File

@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/g/encoding/gcharset"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gxml"
"github.com/gogf/gf/g/test/gtest"
"strings"
"testing"
)
@ -23,6 +24,12 @@ var testData = []struct {
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
}
var testErrData = []struct {
utf8, other, otherEncoding string
}{
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
}
func buildXml(charset string, str string) (string, string) {
head := `<?xml version="1.0" encoding="UTF-8"?>`
srcXml := strings.Replace(head, "UTF-8", charset, -1)
@ -112,7 +119,7 @@ func Test_Encode(t *testing.T) {
if err != nil {
t.Errorf("encode error.")
}
t.Logf("%s\n", string(xmlStr))
//t.Logf("%s\n", string(xmlStr))
res := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string></root>`
if string(xmlStr) != res {
@ -130,11 +137,45 @@ func Test_EncodeIndent(t *testing.T) {
}
m["root"] = interface{}(v)
xmlStr, err := gxml.EncodeWithIndent(m, "xml")
_, err := gxml.EncodeWithIndent(m, "xml")
if err != nil {
t.Errorf("encodeWithIndent error.")
}
t.Logf("%s\n", string(xmlStr))
//t.Logf("%s\n", string(xmlStr))
}
func TestErrXml(t *testing.T) {
for _, v := range testErrData {
srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
if len(srcXml) == 0 && len(dstXml) == 0 {
t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
}
srcXml = strings.Replace(srcXml, "gbk", "XXX", -1)
_, err := gxml.ToJson([]byte(srcXml))
if err == nil {
t.Errorf("srcXml to json should be failed. %s", srcXml)
}
}
}
func TestErrCase(t *testing.T) {
gtest.Case(t, func() {
errXml := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string>`
_, err := gxml.ToJson([]byte(errXml))
if err == nil {
t.Errorf("unexpected value: nil")
}
})
gtest.Case(t, func() {
errXml := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string>`
_, err := gxml.Decode([]byte(errXml))
if err == nil {
t.Errorf("unexpected value: nil")
}
})
}

View File

@ -61,13 +61,13 @@ func SetIfNotExist(key string, value interface{}) bool {
}
// View returns an instance of View with default settings.
// The param <name> is the name for the instance.
// The parameter <name> is the name for the instance.
func View(name ...string) *gview.View {
return gview.Instance(name ...)
}
// Config returns an instance of View with default settings.
// The param <name> is the name for the instance.
// The parameter <name> is the name for the instance.
func Config(name ...string) *gcfg.Config {
return gcfg.Instance(name ...)
}
@ -219,13 +219,13 @@ func Redis(name...string) *gredis.Redis {
Pass : array[4],
})
} else {
glog.Errorfln(`invalid redis node configuration: "%s"`, line)
glog.Errorf(`invalid redis node configuration: "%s"`, line)
}
} else {
glog.Errorfln(`configuration for redis not found for group "%s"`, group)
glog.Errorf(`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.FilePath())
glog.Errorf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())
}
return nil
})

View File

@ -47,7 +47,7 @@ func Database(name...string) gdb.DB {
return gins.Database(name...)
}
// Alias of Database. See Database.
// DB is alias of Database. See Database.
func DB(name...string) gdb.DB {
return gins.Database(name...)
}

View File

@ -4,6 +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 cmdenv provides access to certain variable for both command options and environment.
package cmdenv
import (
@ -18,7 +19,12 @@ var (
cmdOptions = make(map[string]string)
)
func init() {
func init() {
doInit()
}
// doInit does the initialization for this package.
func doInit() {
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
@ -28,10 +34,13 @@ func init() {
}
}
// 获取指定名称的命令行参数,当不存在时获取环境变量参数,皆不存在时,返回给定的默认值。
// 规则:
// 1、命令行参数以小写字母格式使用: gf.包名.变量名 传递;
// 2、环境变量参数以大写字母格式使用: GF_包名_变量名 传递;
// Get returns the command line argument of the specified <key>.
// If the argument does not exist, then it returns the environment variable with specified <key>.
// It returns the default value <def> if none of them exists.
//
// Fetching Rules:
// 1. Command line arguments are in lowercase format, eg: gf.<package name>.<variable name>;
// 2. Environment arguments are in uppercase format, eg: GF_<package name>_<variable name>
func Get(key string, def...interface{}) *gvar.Var {
value := interface{}(nil)
if len(def) > 0 {

View File

@ -0,0 +1,29 @@
// 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.
// go test *.go -bench=".*" -benchmem
package cmdenv
import (
"github.com/gogf/gf/g/test/gtest"
"os"
"testing"
)
func Test_Get(t *testing.T) {
os.Args = []string{"--gf.test.value1=111"}
os.Setenv("GF_TEST_VALUE1", "222")
os.Setenv("GF_TEST_VALUE2", "333")
doInit()
gtest.Case(t, func() {
gtest.Assert(Get("gf.test.value1").String(), "111")
gtest.Assert(Get("gf.test.value2").String(), "333")
gtest.Assert(Get("gf.test.value3").String(), "")
gtest.Assert(Get("gf.test.value3", 1).String(), "1")
})
}

View File

@ -4,15 +4,16 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package empty provides checks for empty variables.
package empty
import (
"reflect"
"reflect"
)
// 判断给定的变量是否为空。
// 整型为0, 布尔为false, slice/map长度为0, 其他为nil的情况都为空。
// 为空时返回true否则返回false。
// IsEmpty checks whether given <value> empty.
// It returns true if <value> is in: 0, nil, false, "", len(slice/map/chan) == 0.
// Or else it returns true.
func IsEmpty(value interface{}) bool {
if value == nil {
return true
@ -35,17 +36,22 @@ func IsEmpty(value interface{}) bool {
case string: return value == ""
case []byte: return len(value) == 0
default:
// 最后通过反射来判断
// Finally using reflect.
rv := reflect.ValueOf(value)
if rv.IsNil() {
return true
}
kind := rv.Kind()
switch kind {
case reflect.Map: fallthrough
case reflect.Slice: fallthrough
case reflect.Array:
switch rv.Kind() {
case reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Array:
return rv.Len() == 0
case reflect.Func,
reflect.Ptr,
reflect.Interface,
reflect.UnsafePointer:
if rv.IsNil() {
return true
}
}
}
return false

View File

@ -4,11 +4,11 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package mutex provides switch for sync.Mutex for concurrent safe feature.
package mutex
import "sync"
// Mutex的封装支持对并发安全开启/关闭的控制。
type Mutex struct {
sync.Mutex
safe bool

View File

@ -4,6 +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 rwmutex provides switch for sync.RWMutex for concurrent safe feature.
package rwmutex
import "sync"

View File

@ -52,7 +52,10 @@ func (r *Response) buildInVars(params...map[string]interface{}) map[string]inter
} else {
vars = make(map[string]interface{})
}
vars["Config"] = gins.Config().GetMap("")
// 当配置文件不存在时就不赋值该模板变量,不然会报错
if c := gins.Config(); c.FilePath() != "" {
vars["Config"] = c.GetMap("")
}
vars["Cookie"] = r.request.Cookie.Map()
vars["Session"] = r.request.Session.Map()
vars["Get"] = r.request.GetQueryMap()

View File

@ -389,7 +389,7 @@ func (s *Server) Run() error {
// 阻塞等待服务执行完成
<- s.closeChan
glog.Printfln("%d: all servers shutdown", gproc.Pid())
glog.Printf("%d: all servers shutdown", gproc.Pid())
return nil
}
@ -400,7 +400,7 @@ func Wait() {
// 阻塞等待服务执行完成
<- allDoneChan
glog.Printfln("%d: all servers shutdown", gproc.Pid())
glog.Printf("%d: all servers shutdown", gproc.Pid())
}

View File

@ -129,7 +129,7 @@ func forkReloadProcess(newExeFilePath...string) error {
buffer, _ := gjson.Encode(sfm)
p.Env = append(p.Env, gADMIN_ACTION_RELOAD_ENVKEY + "=" + string(buffer))
if _, err := p.Start(); err != nil {
glog.Errorfln("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
glog.Errorf("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
return err
}
return nil
@ -147,7 +147,7 @@ func forkRestartProcess(newExeFilePath...string) error {
env = append(env, gADMIN_ACTION_RESTART_ENVKEY + "=1")
p := gproc.NewProcess(path, os.Args, env)
if _, err := p.Start(); err != nil {
glog.Errorfln("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
glog.Errorf("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
return err
}
return nil
@ -197,14 +197,14 @@ func restartWebServers(signal string, newExeFilePath...string) error {
}
} else {
if err := forkReloadProcess(newExeFilePath...); err != nil {
glog.Printfln("%d: server restarts failed", gproc.Pid())
glog.Printf("%d: server restarts failed", gproc.Pid())
serverProcessStatus.Set(gADMIN_ACTION_NONE)
return err
} else {
if len(signal) > 0 {
glog.Printfln("%d: server restarting by signal: %s", gproc.Pid(), signal)
glog.Printf("%d: server restarting by signal: %s", gproc.Pid(), signal)
} else {
glog.Printfln("%d: server restarting by web admin", gproc.Pid())
glog.Printf("%d: server restarting by web admin", gproc.Pid())
}
}
@ -216,12 +216,12 @@ func restartWebServers(signal string, newExeFilePath...string) error {
func shutdownWebServers(signal...string) {
serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN)
if len(signal) > 0 {
glog.Printfln("%d: server shutting down by signal: %s", gproc.Pid(), signal[0])
glog.Printf("%d: server shutting down by signal: %s", gproc.Pid(), signal[0])
// 在终端信号下,立即执行关闭操作
forceCloseWebServers()
allDoneChan <- struct{}{}
} else {
glog.Printfln("%d: server shutting down by api", gproc.Pid())
glog.Printf("%d: server shutting down by api", gproc.Pid())
// 非终端信号下异步1秒后再执行关闭
// 目的是让接口能够正确返回结果,否则接口会报错(因为web server关闭了)
gtimer.SetTimeout(time.Second, func() {

View File

@ -130,7 +130,7 @@ func (s *gracefulServer) doServe() error {
if s.fd != 0 {
action = "reloaded"
}
glog.Printfln("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
glog.Printf("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
s.status = SERVER_STATUS_RUNNING
err := s.httpServer.Serve(s.listener)
s.status = SERVER_STATUS_STOPPED
@ -173,7 +173,7 @@ func (s *gracefulServer) shutdown() {
return
}
if err := s.httpServer.Shutdown(context.Background()); err != nil {
glog.Errorfln("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
glog.Errorf("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
}
}
@ -183,7 +183,7 @@ func (s *gracefulServer) close() {
return
}
if err := s.httpServer.Close(); err != nil {
glog.Errorfln("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
glog.Errorf("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
}
}

View File

@ -84,7 +84,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
caller := s.getHandlerRegisterCallerLine(handler)
if len(hook) == 0 {
if item, ok := s.routesMap[regkey]; ok {
glog.Errorfln(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file)
glog.Errorf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file)
return
}
}

View File

@ -50,7 +50,7 @@ func (d *Domain) Group(prefix...string) *RouterGroup {
func (g *RouterGroup) Bind(items []GroupItem) {
for _, item := range items {
if len(item) < 3 {
glog.Fatalfln("invalid router item: %s", item)
glog.Fatalf("invalid router item: %s", item)
}
if strings.EqualFold(gconv.String(item[0]), "REST") {
g.bind("REST", gconv.String(item[0]) + ":" + gconv.String(item[1]), item[2])
@ -124,7 +124,7 @@ func (g *RouterGroup) bind(bindType string, pattern string, object interface{},
if len(g.prefix) > 0 {
domain, method, path, err := g.server.parsePattern(pattern)
if err != nil {
glog.Fatalfln("invalid pattern: %s", pattern)
glog.Fatalf("invalid pattern: %s", pattern)
}
if bindType == "HANDLER" {
pattern = g.server.serveHandlerKey(method, g.prefix + "/" + strings.TrimLeft(path, "/"), domain)
@ -196,7 +196,7 @@ func (g *RouterGroup) bind(bindType string, pattern string, object interface{},
g.domain.BindHookHandler(pattern, methods[0], h)
}
} else {
glog.Fatalfln("invalid hook handler for pattern:%s", pattern)
glog.Fatalf("invalid hook handler for pattern:%s", pattern)
}
}
}

View File

@ -50,11 +50,11 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) {
if _, ok := v.Method(i).Interface().(func()); !ok {
if len(methodMap) > 0 {
// 指定的方法名称注册,那么需要使用错误提示
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
} else {
// 否则只是Debug提示
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
}
continue
@ -108,7 +108,7 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
ctlName = fmt.Sprintf(`(%s)`, ctlName)
}
if _, ok := fval.Interface().(func()); !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
pkgPath, ctlName, mname, fval.Type().String())
return
}
@ -147,7 +147,7 @@ func (s *Server)BindControllerRest(pattern string, c Controller) {
ctlName = fmt.Sprintf(`(%s)`, ctlName)
}
if _, ok := v.Method(i).Interface().(func()); !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
return
}

View File

@ -57,11 +57,11 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) {
if !ok {
if len(methodMap) > 0 {
// 指定的方法名称注册,那么需要使用错误提示
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, mname, v.Method(i).Type().String())
} else {
// 否则只是Debug提示
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
pkgPath, objName, mname, v.Method(i).Type().String())
}
continue
@ -127,7 +127,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
}
faddr, ok := fval.Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, mname, fval.Type().String())
return
}
@ -174,7 +174,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) {
}
faddr, ok := v.Method(i).Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, mname, v.Method(i).Type().String())
continue
}

View File

@ -20,10 +20,10 @@ import (
// SESSION对象
type Session struct {
id string // SessionId
id string // SessionId
data *gmap.StrAnyMap // Session数据
server *Server // 所属Server
request *Request // 关联的请求
server *Server // 所属Server
request *Request // 关联的请求
}
// 生成一个唯一的SessionId字符串长度18位。
@ -58,7 +58,7 @@ func (s *Session) init() {
// 否则执行初始化创建
s.id = s.request.Cookie.MakeSessionId()
s.data = gmap.NewStrAnyMap()
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge())
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
}
}

View File

@ -46,6 +46,48 @@ func Test_Params_Json(t *testing.T) {
Pass2 : "456",
})
})
s.BindHandler("/json3", func(r *ghttp.Request){
type Message struct {
Code int `json:"code"`
Body string `json:"body,omitempty"`
Error string `json:"error,omitempty"`
}
type ResponseJson struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
ExtData interface{} `json:"ext_data,omitempty"`
Paginate interface{} `json:"paginate,omitempty"`
Message Message `json:"message,omitempty"`
}
responseJson := &ResponseJson{
Success: true,
Data: nil,
ExtData: nil,
Message: Message{3, "测试", "error"},
}
r.Response.WriteJson(responseJson)
})
s.BindHandler("/json4", func(r *ghttp.Request){
type Message struct {
Code int `json:"code"`
Body string `json:"body,omitempty"`
Error string `json:"error,omitempty"`
}
type ResponseJson struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
ExtData interface{} `json:"ext_data,omitempty"`
Paginate interface{} `json:"paginate,omitempty"`
Message *Message `json:"message,omitempty"`
}
responseJson := ResponseJson{
Success: true,
Data: nil,
ExtData: nil,
Message: &Message{3, "测试", "error"},
}
r.Response.WriteJson(responseJson)
})
s.SetPort(p)
s.SetDumpRouteMap(false)
s.Start()
@ -67,7 +109,7 @@ func Test_Params_Json(t *testing.T) {
gtest.Assert(map1["password2"], "456")
map2 := make(map[string]interface{})
err2 := json.Unmarshal([]byte(client.GetContent("/json1")), &map2)
err2 := json.Unmarshal([]byte(client.GetContent("/json2")), &map2)
gtest.Assert(err2, nil)
gtest.Assert(len(map2), 4)
gtest.Assert(map2["Name"], "john")
@ -75,5 +117,18 @@ func Test_Params_Json(t *testing.T) {
gtest.Assert(map2["password1"], "123")
gtest.Assert(map2["password2"], "456")
map3 := make(map[string]interface{})
err3 := json.Unmarshal([]byte(client.GetContent("/json3")), &map3)
gtest.Assert(err3, nil)
gtest.Assert(len(map3), 2)
gtest.Assert(map3["success"], "true")
gtest.Assert(map3["message"], g.Map{"body":"测试", "code":3, "error":"error"})
map4 := make(map[string]interface{})
err4 := json.Unmarshal([]byte(client.GetContent("/json4")), &map4)
gtest.Assert(err4, nil)
gtest.Assert(len(map4), 2)
gtest.Assert(map4["success"], "true")
gtest.Assert(map4["message"], g.Map{"body":"测试", "code":3, "error":"error"})
})
}

View File

@ -85,3 +85,36 @@ func Test_Router_Hook_Priority(t *testing.T) {
})
}
func Test_Router_Hook_Multi(t *testing.T) {
p := ports.PopRand()
s := g.Server(p)
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
r.Response.Write("show")
})
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
"BeforeServe" : func(r *ghttp.Request) {
r.Response.Write("1")
},
})
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
"BeforeServe" : func(r *ghttp.Request) {
r.Response.Write("2")
},
})
s.SetPort(p)
s.SetDumpRouteMap(false)
s.Start()
defer s.Shutdown()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
gtest.Assert(client.GetContent("/"), "Not Found")
gtest.Assert(client.GetContent("/multi-hook"), "12show")
})
}

View File

@ -7,11 +7,12 @@
package gtcp
import (
"net"
"time"
"io"
"bufio"
"bytes"
"bufio"
"bytes"
"crypto/tls"
"io"
"net"
"time"
)
// 封装的链接对象
@ -21,7 +22,7 @@ type Conn struct {
buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理)
recvDeadline time.Time // 读取超时时间
sendDeadline time.Time // 写入超时时间
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取缓冲区完毕后的等待间隔
}
const (
@ -38,6 +39,24 @@ func NewConn(addr string, timeout...int) (*Conn, error) {
}
}
// 创建支持TLS加密通信的TCP链接
func NewConnTLS(addr string, tlsConfig *tls.Config) (*Conn, error) {
if conn, err := NewNetConnTLS(addr, tlsConfig); err == nil {
return NewConnByNetConn(conn), nil
} else {
return nil, err
}
}
// 根据证书和密钥文件创建支持TLS加密通信的TCP链接
func NewConnKeyCrt(addr, crtFile, keyFile string) (*Conn, error) {
if conn, err := NewNetConnKeyCrt(addr, crtFile, keyFile); err == nil {
return NewConnByNetConn(conn), nil
} else {
return nil, err
}
}
// 将net.Conn接口对象转换为*gtcp.Conn对象
func NewConnByNetConn(conn net.Conn) *Conn {
return &Conn {
@ -50,16 +69,14 @@ func NewConnByNetConn(conn net.Conn) *Conn {
}
// 关闭连接
func (c *Conn) Close() {
c.conn.Close()
func (c *Conn) Close() error {
return c.conn.Close()
}
// 发送数据
func (c *Conn) Send(data []byte, retry...Retry) error {
length := 0
for {
n, err := c.conn.Write(data)
if err != nil {
if _, err := c.conn.Write(data); err != nil {
// 链接已关闭
if err == io.EOF {
return err
@ -76,18 +93,17 @@ func (c *Conn) Send(data []byte, retry...Retry) error {
time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
}
} else {
length += n
if length == len(data) {
return nil
}
return nil
}
}
}
// 获取数据,指定读取的数据长度(length < 1表示获取所有可读数据),以及重试策略(retry)
// 阻塞等待获取指定读取的数据长度,并给定重试策略。
//
// 需要注意:
// 1、往往在socket通信中需要指定固定的数据结构并在设定对应的长度字段并在读取数据时便于区分包大小
// 2、当length < 1时表示获取缓冲区所有的数据,但是可能会引起包解析问题(可能出现非完整的包情况),因此需要解析端注意解析策略;
// 2、当length < 0时表示获取缓冲区所有的数据,但是可能会引起包解析问题(可能出现粘包/断包情况),因此需要解析端注意解析策略;
// 3、当length = 0时表示获取一次缓冲区的数据后立即返回
func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
var err error // 读取错误
var size int // 读取长度
@ -106,9 +122,11 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
// 如果已经读取到数据(这点很关键,表明缓冲区已经有数据,剩下的操作就是将所有数据读取完毕)
// 那么可以设置读取全部缓冲数据的超时时间;如果没有接收到任何数据,那么将会进入读取阻塞(或者自定义的超时阻塞);
// 仅对读取全部缓冲区数据操作有效
if length <= 0 && index > 0 {
if length < 0 && index > 0 {
bufferWait = true
c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait))
if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
return nil, err
}
}
size, err = c.reader.Read(buffer[index:])
if size > 0 {
@ -137,7 +155,9 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
}
// 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠)
if bufferWait && isTimeout(err) {
c.conn.SetReadDeadline(c.recvDeadline)
if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil {
return nil, err
}
err = nil
break
}
@ -155,6 +175,10 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
}
break
}
// 只获取一次数据
if length == 0 {
break
}
}
return buffer[:index], err
}
@ -184,14 +208,18 @@ func (c *Conn) RecvLine(retry...Retry) ([]byte, error) {
// 带超时时间的数据获取
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
defer c.SetRecvDeadline(time.Time{})
return c.Recv(length, retry...)
}
// 带超时时间的数据发送
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer c.SetSendDeadline(time.Time{})
return c.Send(data, retry...)
}

View File

@ -14,88 +14,118 @@ import (
)
const (
// 允许最大的简单协议包大小(byte), 15MB
PKG_MAX_SIZE = 0xFFFFFF
// 消息包头大小: "总长度"3字节+"校验码"4字节
PKG_HEADER_SIZE = 7
// 默认允许最大的简单协议包大小(byte), 65535 byte
gPKG_MAX_DATA_SIZE = 65535
// 简单协议包头大小
gPKG_HEADER_SIZE = 3
)
// 根据简单协议发送数据包。
// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。
// 注意:
// 1. "总长度"包含自身3字节及"校验码"4字节。
// 2. 由于"总长度"为3字节并且使用的BigEndian字节序因此最后返回的buffer使用了buffer[1:]。
func (c *Conn) SendPkg(data []byte, retry...Retry) error {
length := uint32(len(data))
if length > PKG_MAX_SIZE - PKG_HEADER_SIZE {
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE))
// 数据读取选项
type PkgOption struct {
MaxSize int // (byte)数据读取的最大包大小最大不能超过3字节(0xFFFFFF,15MB)默认为65535byte
Retry Retry // 失败重试
}
// getPkgOption wraps and returns the PkgOption.
// If no option given, it returns a new option with default value.
func getPkgOption(option...PkgOption) (*PkgOption, error) {
pkgOption := PkgOption{}
if len(option) > 0 {
pkgOption = option[0]
}
if pkgOption.MaxSize == 0 {
pkgOption.MaxSize = gPKG_MAX_DATA_SIZE
} else if pkgOption.MaxSize > 0xFFFFFF {
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxSize, 0xFFFFFF)
}
return &pkgOption, nil
}
// 根据简单协议发送数据包。
//
// 简单协议数据格式:数据长度(24bit)|数据字段(变长)。
//
// 注意:
// 1. "数据长度"仅为"数据字段"的长度不包含头信息的长度字段3字节。
// 2. 由于"数据长度"为3字节并且使用的BigEndian字节序因此这里最后返回的buffer使用了buffer[1:]。
func (c *Conn) SendPkg(data []byte, option...PkgOption) error {
pkgOption, err := getPkgOption(option...)
if err != nil {
return err
}
length := len(data)
if length > pkgOption.MaxSize {
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE))
}
buffer := make([]byte, gPKG_HEADER_SIZE + 1 + len(data))
binary.BigEndian.PutUint32(buffer[0 : ], uint32(length))
copy(buffer[gPKG_HEADER_SIZE + 1 : ], data)
if pkgOption.Retry.Count > 0 {
return c.Send(buffer[1:], pkgOption.Retry)
}
buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data))
copy(buffer[PKG_HEADER_SIZE + 1 : ], data)
binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length)
binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data))
//fmt.Println("SendPkg:", buffer[1:])
return c.Send(buffer[1:], retry...)
return c.Send(buffer[1:])
}
// 简单协议: 带超时时间的数据发送
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) error {
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
return c.SendPkg(data, option...)
}
// 简单协议: 发送数据并等待接收返回数据
func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
func (c *Conn) SendRecvPkg(data []byte, option...PkgOption) ([]byte, error) {
if err := c.SendPkg(data, option...); err == nil {
return c.RecvPkg(option...)
} else {
return nil, err
}
}
// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
if err := c.SendPkg(data, option...); err == nil {
return c.RecvPkgWithTimeout(timeout, option...)
} else {
return nil, err
}
}
// 简单协议: 获取一个数据包。
func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
func (c *Conn) RecvPkg(option...PkgOption) (result []byte, err error) {
var temp []byte
var length uint32
var length int
pkgOption, err := getPkgOption(option...)
if err != nil {
return nil, err
}
for {
// 先根据对象的缓冲区数据进行计算
for {
if len(c.buffer) >= PKG_HEADER_SIZE {
// 注意"长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]})
// 解析的大小是否符合规范
if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE {
c.buffer = c.buffer[1:]
continue
if len(c.buffer) >= gPKG_HEADER_SIZE {
// 注意"数据长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
// 解析的大小是否符合规范,清空从该连接接收到的所有数据包
if length < 0 || length > pkgOption.MaxSize {
c.buffer = c.buffer[:0]
return nil, fmt.Errorf(`invalid package size %d`, length)
}
// 不满足包大小,需要继续读取
if uint32(len(c.buffer)) < length {
if len(c.buffer) < length + gPKG_HEADER_SIZE {
break
}
// 数据校验
if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) {
c.buffer = c.buffer[1:]
continue
}
result = c.buffer[PKG_HEADER_SIZE : length]
c.buffer = c.buffer[length: ]
result = c.buffer[gPKG_HEADER_SIZE : gPKG_HEADER_SIZE + length]
c.buffer = c.buffer[gPKG_HEADER_SIZE + length: ]
return
} else {
break
}
}
// 读取系统socket缓冲区的完整数据
temp, err = c.Recv(-1, retry...)
temp, err = c.Recv(0, pkgOption.Retry)
if err != nil {
break
}
@ -108,8 +138,10 @@ func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
}
// 简单协议: 带超时时间的消息包获取
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option...PkgOption) ([]byte, error) {
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
return c.RecvPkg(option...)
}

View File

@ -7,13 +7,16 @@
package gtcp
import (
"net"
"time"
"crypto/rand"
"crypto/tls"
"github.com/gogf/gf/g/os/gfile"
"net"
"time"
)
const (
gDEFAULT_RETRY_INTERVAL = 100 // (毫秒)默认重试时间间隔
gDEFAULT_READ_BUFFER_SIZE = 1024 // 默认数据读取缓冲区大小
gDEFAULT_RETRY_INTERVAL = 100 // (毫秒)默认重试时间间隔
gDEFAULT_READ_BUFFER_SIZE = 128 // (byte)默认数据读取缓冲区大小
)
type Retry struct {
@ -21,6 +24,7 @@ type Retry struct {
Interval int // 重试间隔(毫秒)
}
// Deprecated.
// 常见的二进制数据校验方式,生成校验结果
func Checksum(buffer []byte) uint32 {
var checksum uint32
@ -39,6 +43,20 @@ func NewNetConn(addr string, timeout...int) (net.Conn, error) {
}
}
// 创建支持TLS的原生TCP链接, addr地址格式形如127.0.0.1:80
func NewNetConnTLS(addr string, tlsConfig *tls.Config) (net.Conn, error) {
return tls.Dial("tcp", addr, tlsConfig)
}
// 根据给定的证书和密钥文件创建支持TLS的原生TCP链接, addr地址格式形如127.0.0.1:80
func NewNetConnKeyCrt(addr, crtFile, keyFile string) (net.Conn, error) {
tlsConfig, err := LoadKeyCrt(crtFile, keyFile)
if err != nil {
return nil, err
}
return NewNetConnTLS(addr, tlsConfig)
}
// (面向短链接)发送数据
func Send(addr string, data []byte, retry...Retry) error {
conn, err := NewConn(addr)
@ -88,4 +106,25 @@ func isTimeout(err error) bool {
return true
}
return false
}
// 根据证书和密钥生成TLS对象
func LoadKeyCrt(crtFile, keyFile string) (*tls.Config, error) {
crtPath, err := gfile.Search(crtFile)
if err != nil {
return nil, err
}
keyPath, err := gfile.Search(keyFile)
if err != nil {
return nil, err
}
crt, err := tls.LoadX509KeyPair(crtPath, keyPath)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{}
tlsConfig.Certificates = []tls.Certificate{crt}
tlsConfig.Time = time.Now
tlsConfig.Rand = rand.Reader
return tlsConfig, nil
}

View File

@ -9,41 +9,41 @@ package gtcp
import "time"
// 简单协议: (面向短链接)发送消息包
func SendPkg(addr string, data []byte, retry...Retry) error {
func SendPkg(addr string, data []byte, option...PkgOption) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkg(data, retry...)
return conn.SendPkg(data, option...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据
func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) {
func SendRecvPkg(addr string, data []byte, option...PkgOption) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkg(data, retry...)
return conn.SendRecvPkg(data, option...)
}
// 简单协议: (面向短链接)带超时时间的数据发送
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error {
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, option...PkgOption) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkgWithTimeout(data, timeout, retry...)
return conn.SendPkgWithTimeout(data, timeout, option...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间)
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkgWithTimeout(data, timeout, retry...)
return conn.SendRecvPkgWithTimeout(data, timeout, option...)
}

View File

@ -67,7 +67,7 @@ func (c *PoolConn) Close() error {
c.status = gCONN_STATUS_UNKNOWN
c.pool.Put(c)
} else {
c.Conn.Close()
return c.Conn.Close()
}
return nil
}
@ -114,15 +114,19 @@ func (c *PoolConn) RecvLine(retry...Retry) ([]byte, error) {
}
// (方法覆盖)带超时时间的数据获取
func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) (data []byte, err error) {
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
defer c.SetRecvDeadline(time.Time{})
return c.Recv(length, retry...)
}
// (方法覆盖)带超时时间的数据发送
func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer c.SetSendDeadline(time.Time{})
return c.Send(data, retry...)
}

View File

@ -11,11 +11,11 @@ import (
)
// 简单协议: (方法覆盖)发送数据
func (c *PoolConn) SendPkg(data []byte, retry...Retry) (err error) {
if err = c.Conn.SendPkg(data, retry...); err != nil && c.status == gCONN_STATUS_UNKNOWN {
func (c *PoolConn) SendPkg(data []byte, option...PkgOption) (err error) {
if err = c.Conn.SendPkg(data, option...); err != nil && c.status == gCONN_STATUS_UNKNOWN {
if v, e := c.pool.NewFunc(); e == nil {
c.Conn = v.(*PoolConn).Conn
err = c.Conn.SendPkg(data, retry...)
err = c.Conn.SendPkg(data, option...)
} else {
err = e
}
@ -29,8 +29,8 @@ func (c *PoolConn) SendPkg(data []byte, retry...Retry) (err error) {
}
// 简单协议: (方法覆盖)接收数据
func (c *PoolConn) RecvPkg(retry...Retry) ([]byte, error) {
data, err := c.Conn.RecvPkg(retry...)
func (c *PoolConn) RecvPkg(option...PkgOption) ([]byte, error) {
data, err := c.Conn.RecvPkg(option...)
if err != nil {
c.status = gCONN_STATUS_ERROR
} else {
@ -40,32 +40,36 @@ func (c *PoolConn) RecvPkg(retry...Retry) ([]byte, error) {
}
// 简单协议: (方法覆盖)带超时时间的数据获取
func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option...PkgOption) ([]byte, error) {
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
return c.RecvPkg(option...)
}
// 简单协议: (方法覆盖)带超时时间的数据发送
func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) error {
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
return c.SendPkg(data, option...)
}
// 简单协议: (方法覆盖)发送数据并等待接收返回数据
func (c *PoolConn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
func (c *PoolConn) SendRecvPkg(data []byte, option...PkgOption) ([]byte, error) {
if err := c.SendPkg(data, option...); err == nil {
return c.RecvPkg(option...)
} else {
return nil, err
}
}
// 简单协议: (方法覆盖)发送数据并等待接收返回数据(带返回超时等待时间)
func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
if err := c.SendPkg(data, option...); err == nil {
return c.RecvPkgWithTimeout(timeout, option...)
} else {
return nil, err
}

View File

@ -8,79 +8,132 @@
package gtcp
import (
"errors"
"github.com/gogf/gf/g/os/glog"
"net"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/util/gconv"
"crypto/tls"
"errors"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/util/gconv"
"net"
)
const (
gDEFAULT_SERVER = "default"
)
// tcp server结构体
// TCP Server.
type Server struct {
listen net.Listener
address string
handler func (*Conn)
tlsConfig *tls.Config
}
// Server表用以存储和检索名称与Server对象之间的关联关系
// Map for name to server, for singleton purpose.
var serverMapping = gmap.NewStrAnyMap()
// 获取/创建一个空配置的TCP Server
// 单例模式请保证name的唯一性
// GetServer returns the TCP server with specified <name>,
// or it returns a new normal TCP server named <name> if it does not exist.
// The parameter <name> is used to specify the TCP server
func GetServer(name...interface{}) *Server {
serverName := gDEFAULT_SERVER
if len(name) > 0 {
serverName = gconv.String(name[0])
}
if s := serverMapping.Get(serverName); s != nil {
return s.(*Server)
}
s := NewServer("", nil)
serverMapping.Set(serverName, s)
return s
return serverMapping.GetOrSetFuncLock(serverName, func() interface{} {
return NewServer("", nil)
}).(*Server)
}
// 创建一个tcp server对象并且可以选择指定一个单例名字
func NewServer(address string, handler func (*Conn), names...string) *Server {
s := &Server{address, handler}
if len(names) > 0 {
serverMapping.Set(names[0], s)
// NewServer creates and returns a new normal TCP server.
// The parameter <name> is optional, which is used to specify the instance name of the server.
func NewServer(address string, handler func (*Conn), name...string) *Server {
s := &Server{
address : address,
handler : handler,
}
if len(name) > 0 {
serverMapping.Set(name[0], s)
}
return s
}
// 设置参数 - address
func (s *Server) SetAddress (address string) {
// NewServerTLS creates and returns a new TCP server with TLS support.
// The parameter <name> is optional, which is used to specify the instance name of the server.
func NewServerTLS(address string, tlsConfig *tls.Config, handler func (*Conn), name...string) *Server {
s := NewServer(address, handler, name...)
s.SetTLSConfig(tlsConfig)
return s
}
// NewServerKeyCrt creates and returns a new TCP server with TLS support.
// The parameter <name> is optional, which is used to specify the instance name of the server.
func NewServerKeyCrt(address, crtFile, keyFile string, handler func (*Conn), name...string) *Server {
s := NewServer(address, handler, name...)
if err := s.SetTLSKeyCrt(crtFile, keyFile); err != nil {
glog.Error(err)
}
return s
}
// SetAddress sets the listening address for server.
func (s *Server) SetAddress(address string) {
s.address = address
}
// 设置参数 - handler
func (s *Server) SetHandler (handler func (*Conn)) {
// SetHandler sets the connection handler for server.
func (s *Server) SetHandler(handler func (*Conn)) {
s.handler = handler
}
// 执行监听
func (s *Server) Run() error {
// SetTlsKeyCrt sets the certificate and key file for TLS configuration of server.
func (s *Server) SetTLSKeyCrt(crtFile, keyFile string) error {
tlsConfig, err := LoadKeyCrt(crtFile, keyFile)
if err != nil {
return err
}
s.tlsConfig = tlsConfig
return nil
}
// SetTlsConfig sets the TLS configuration of server.
func (s *Server) SetTLSConfig(tlsConfig *tls.Config) {
s.tlsConfig = tlsConfig
}
// Close closes the listener and shutdowns the server.
func (s *Server) Close() error {
return s.listen.Close()
}
// Run starts running the TCP Server.
func (s *Server) Run() (err error) {
if s.handler == nil {
err := errors.New("start running failed: socket handler not defined")
err = errors.New("start running failed: socket handler not defined")
glog.Error(err)
return err
return
}
addr, err := net.ResolveTCPAddr("tcp", s.address)
if err != nil {
glog.Error(err)
return err
if s.tlsConfig != nil {
// TLS Server
s.listen, err = tls.Listen("tcp", s.address, s.tlsConfig)
if err != nil {
glog.Error(err)
return
}
} else {
// Normal Server
addr, err := net.ResolveTCPAddr("tcp", s.address)
if err != nil {
glog.Error(err)
return err
}
s.listen, err = net.ListenTCP("tcp", addr)
if err != nil {
glog.Error(err)
return err
}
}
listen, err := net.ListenTCP("tcp", addr)
if err != nil {
glog.Error(err)
return err
}
for {
if conn, err := listen.Accept(); err != nil {
for {
if conn, err := s.listen.Accept(); err != nil {
glog.Error(err)
return err
} else if conn != nil {

View File

@ -12,7 +12,7 @@ import (
"io"
)
// 封装的链接对象
// 封装的UDP链接对象
type Conn struct {
conn *net.UDPConn // 底层链接对象
raddr *net.UDPAddr // 远程地址
@ -53,15 +53,12 @@ func NewConnByNetConn(udp *net.UDPConn) *Conn {
}
// 发送数据
func (c *Conn) Send(data []byte, retry...Retry) error {
var err error
var size int
var length int
func (c *Conn) Send(data []byte, retry...Retry) (err error) {
for {
if c.raddr != nil {
size, err = c.conn.WriteToUDP(data, c.raddr)
_, err = c.conn.WriteToUDP(data, c.raddr)
} else {
size, err = c.conn.Write(data)
_, err = c.conn.Write(data)
}
if err != nil {
// 链接已关闭
@ -80,16 +77,16 @@ func (c *Conn) Send(data []byte, retry...Retry) error {
time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
}
} else {
length += size
if length == len(data) {
return nil
}
return nil
}
}
}
// 接收数据.
// 注意UDP协议存在消息边界因此使用 length<=0 可以获取缓冲区所有消息包数据,即一个完整包。
// 接收UDP协议数据.
//
// 注意事项:
// 1、UDP协议存在消息边界因此使用 length < 0 可以获取缓冲区所有消息包数据,即一个完整包;
// 2、当length = 0时表示获取当前的缓冲区数据获取一次后立即返回
func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
var err error // 读取错误
var size int // 读取长度
@ -105,9 +102,11 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
}
for {
if length <= 0 && index > 0 {
if length < 0 && index > 0 {
bufferWait = true
c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait))
if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
return nil, err
}
}
size, raddr, err = c.conn.ReadFromUDP(buffer[index:])
if err == nil {
@ -139,7 +138,9 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
}
// 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠)
if bufferWait && isTimeout(err) {
c.conn.SetReadDeadline(c.recvDeadline)
if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil {
return nil, err
}
err = nil
break
}
@ -157,6 +158,10 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
}
break
}
// 只获取一次数据
if length == 0 {
break
}
}
return buffer[:index], err
}
@ -172,14 +177,18 @@ func (c *Conn) SendRecv(data []byte, receive int, retry...Retry) ([]byte, error)
// 带超时时间的数据获取
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
defer c.SetRecvDeadline(time.Time{})
return c.Recv(length, retry...)
}
// 带超时时间的数据发送
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer c.SetSendDeadline(time.Time{})
return c.Send(data, retry...)
}

View File

@ -10,6 +10,7 @@ import (
"net"
)
// Deprecated.
// 常见的二进制数据校验方式,生成校验结果
func Checksum(buffer []byte) uint32 {
var checksum uint32
@ -22,18 +23,18 @@ func Checksum(buffer []byte) uint32 {
// 创建标准库UDP链接操作对象
func NewNetConn(raddr string, laddr...string) (*net.UDPConn, error) {
var err error
var rudpaddr, ludpaddr *net.UDPAddr
rudpaddr, err = net.ResolveUDPAddr("udp", raddr)
var remoteAddr, localAddr *net.UDPAddr
remoteAddr, err = net.ResolveUDPAddr("udp", raddr)
if err != nil {
return nil, err
}
if len(laddr) > 0 {
ludpaddr, err = net.ResolveUDPAddr("udp", laddr[0])
localAddr, err = net.ResolveUDPAddr("udp", laddr[0])
if err != nil {
return nil, err
}
}
conn, err := net.DialUDP("udp", ludpaddr, rudpaddr)
conn, err := net.DialUDP("udp", localAddr, remoteAddr)
if err != nil {
return nil, err
}

View File

@ -21,8 +21,9 @@ const (
// tcp server结构体
type Server struct {
address string
handler func (*Conn)
conn *Conn // UDP server connection object.
address string // Listening address.
handler func (*Conn)
}
// Server表用以存储和检索名称与Server对象之间的关联关系
@ -30,7 +31,7 @@ var serverMapping = gmap.NewStrAnyMap()
// 获取/创建一个空配置的UDP Server
// 单例模式请保证name的唯一性
func GetServer(name...interface{}) (*Server) {
func GetServer(name...interface{}) *Server {
serverName := gDEFAULT_SERVER
if len(name) > 0 {
serverName = gconv.String(name[0])
@ -44,8 +45,11 @@ func GetServer(name...interface{}) (*Server) {
}
// 创建一个tcp server对象并且可以选择指定一个单例名字
func NewServer (address string, handler func (*Conn), names...string) *Server {
s := &Server{address, handler}
func NewServer(address string, handler func (*Conn), names...string) *Server {
s := &Server{
address : address,
handler : handler,
}
if len(names) > 0 {
serverMapping.Set(names[0], s)
}
@ -58,10 +62,16 @@ func (s *Server) SetAddress (address string) {
}
// 设置参数 - handler
func (s *Server) SetHandler (handler func (*Conn)) {
func (s *Server) SetHandler(handler func (*Conn)) {
s.handler = handler
}
// Close closes the connection.
// It will make server shutdowns immediately.
func (s *Server) Close() error {
return s.conn.Close()
}
// 执行监听
func (s *Server) Run() error {
if s.handler == nil {
@ -79,7 +89,7 @@ func (s *Server) Run() error {
glog.Error(err)
return err
}
for {
s.handler(NewConnByNetConn(conn))
}
s.conn = NewConnByNetConn(conn)
s.handler(s.conn)
return nil
}

View File

@ -7,81 +7,100 @@
// Package gcache provides high performance and concurrent-safe in-memory cache for process.
package gcache
// 全局缓存管理对象
// Default cache object.
var cache = New()
// (使用全局KV缓存对象)设置kv缓存键值对过期时间单位为**毫秒**
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func Set(key interface{}, value interface{}, expire int) {
cache.Set(key, value, expire)
}
// 当键名不存在时写入并返回true否则返回false。
// 常用来做对并发性要求不高的内存锁。
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
return cache.SetIfNotExist(key, value, expire)
}
// (使用全局KV缓存对象)批量设置kv缓存键值对过期时间单位为**毫秒**
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func Sets(data map[interface{}]interface{}, expire int) {
cache.Sets(data, expire)
}
// (使用全局KV缓存对象)获取指定键名的值
// Get returns the value of <key>.
// It returns nil if it does not exist or its value is nil.
func Get(key interface{}) interface{} {
return cache.Get(key)
}
// 当键名存在时返回其键值,否则写入指定的键值
// GetOrSet returns the value of <key>,
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
return cache.GetOrSet(key, value, expire)
}
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
// GetOrSetFunc returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
return cache.GetOrSetFunc(key, f, expire)
}
// GetOrSetFunc不同的是f是在写锁机制内执行
// GetOrSetFuncLock returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
return cache.GetOrSetFuncLock(key, f, expire)
}
// 是否存在指定的键名true表示存在false表示不存在。
// Contains returns true if <key> exists in the cache, or else returns false.
func Contains(key interface{}) bool {
return cache.Contains(key)
}
// (使用全局KV缓存对象)删除指定键值对
// Remove deletes the <key> in the cache, and returns its value.
func Remove(key interface{}) interface{} {
return cache.Remove(key)
}
// (使用全局KV缓存对象)批量删除指定键值对
// Removes deletes <keys> in the cache.
func Removes(keys []interface{}) {
cache.Removes(keys)
}
// 返回缓存的所有数据键值对(不包含已过期数据)
// Data returns a copy of all key-value pairs in the cache as map type.
func Data() map[interface{}]interface{} {
return cache.Data()
}
// 获得所有的键名,组成数组返回
// Keys returns all keys in the cache as slice.
func Keys() []interface{} {
return cache.Keys()
}
// 获得所有的键名,组成字符串数组返回
// KeyStrings returns all keys in the cache as string slice.
func KeyStrings() []string {
return cache.KeyStrings()
}
// 获得所有的值,组成数组返回
// Values returns all values in the cache as slice.
func Values() []interface{} {
return cache.Values()
}
// 获得缓存对象的键值对数量
// Size returns the size of the cache.
func Size() int {
return cache.Size()
}

View File

@ -13,13 +13,12 @@ import (
"unsafe"
)
// 缓存对象。
// 底层只有一个缓存对象,如果需要提高并发性能,可新增缓存对象无锁哈希表,用键名做固定分区。
// Cache struct.
type Cache struct {
*memCache
}
// Cache对象按照缓存键名首字母做了分组
// New creates and returns a new cache object.
func New(lruCap...int) *Cache {
c := &Cache {
memCache : newMemCache(lruCap...),
@ -28,10 +27,10 @@ func New(lruCap...int) *Cache {
return c
}
// 清空缓存中的所有数据
// Clear clears all data of the cache.
func (c *Cache) Clear() {
// 使用原子操作替换缓存对象
// atomic swap to ensure atomicity.
old := atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.memCache)), unsafe.Pointer(newMemCache()))
// 关闭旧的缓存对象
// close the old cache object.
(*memCache)(old).Close()
}

View File

@ -18,41 +18,46 @@ import (
)
// 缓存对象
// Internal cache object.
type memCache struct {
dataMu sync.RWMutex
expireTimeMu sync.RWMutex
expireSetMu sync.RWMutex
cap int // 控制缓存池大小超过大小则按照LRU算法进行缓存过期处理(默认为0表示不进行限制)
data map[interface{}]memCacheItem // 缓存数据(所有的缓存数据存放哈希表)
expireTimes map[interface{}]int64 // 键名对应的分组过期时间(用于相同键名过期时间快速更新)键值为1秒级时间戳
expireSets map[int64]*gset.Set // 分组过期时间对应的键名列表(用于自动过期快速删除)键值为1秒级时间戳
// <cap> limits the size of the cache pool.
// If the size of the cache exceeds the <cap>,
// the cache expiration process is performed according to the LRU algorithm.
// It is 0 in default which means no limits.
cap int
data map[interface{}]memCacheItem // Underlying cache data which is stored in a hash table.
expireTimes map[interface{}]int64 // Expiring key mapping to its timestamp, which is used for quick indexing and deleting.
expireSets map[int64]*gset.Set // Expiring timestamp mapping to its key set, which is used for quick indexing and deleting.
lru *memCacheLru // LRU缓存限制(只有限定cap池大小时才启用)
lruGetList *glist.List // Get操作的LRU记录
eventList *glist.List // 异步处理队列
closed *gtype.Bool // 关闭事件通知
lru *memCacheLru // LRU object, which is enabled when <cap> > 0.
lruGetList *glist.List // LRU history according with Get function.
eventList *glist.List // Asynchronous event list for internal data synchronization.
closed *gtype.Bool // Is this cache closed or not.
}
// 缓存数据项
// Internal cache item.
type memCacheItem struct {
v interface{} // 键值
e int64 // 过期时间
v interface{} // Value.
e int64 // Expire time in milliseconds.
}
// 异步队列数据项
// Internal event item.
type memCacheEvent struct {
k interface{} // 键名
e int64 // 过期时间
k interface{} // Key.
e int64 // Expire time in milliseconds.
}
const (
// 当数据不过期时默认设置的过期属性值相当于math.MaxInt64/1000000
// Default expire time for no expiring items.
// It equals to math.MaxInt64/1000000.
gDEFAULT_MAX_EXPIRE = 9223372036854
)
// 创建底层的缓存对象
// newMemCache creates and returns a new memory cache object.
func newMemCache(lruCap...int) *memCache {
c := &memCache {
lruGetList : glist.New(),
@ -69,12 +74,12 @@ func newMemCache(lruCap...int) *memCache {
return c
}
// 计算过期缓存的键名(将毫秒换算成秒的整数毫秒按照1秒进行分组)
// makeExpireKey groups the <expire> in milliseconds to its according seconds.
func (c *memCache) makeExpireKey(expire int64) int64 {
return int64(math.Ceil(float64(expire/1000) + 1)*1000)
}
// 获取一个过期键名存放Set, 如果没有则返回nil
// getExpireSet returns the expire set for given <expire> in seconds.
func (c *memCache) getExpireSet(expire int64) (expireSet *gset.Set) {
c.expireSetMu.RLock()
expireSet, _ = c.expireSets[expire]
@ -82,23 +87,24 @@ func (c *memCache) getExpireSet(expire int64) (expireSet *gset.Set) {
return
}
// 获取或者创建一个过期键名存放Set(由于是异步单线程执行因此不会出现创建set时的覆盖问题)
// getOrNewExpireSet returns the expire set for given <expire> in seconds.
// It creates and returns a new set for <expire> if it does not exist.
func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) {
if expireSet = c.getExpireSet(expire); expireSet == nil {
expireSet = gset.New()
c.expireSetMu.Lock()
// 写锁二次检索确认
if es, ok := c.expireSets[expire]; ok {
expireSet = es
} else {
c.expireSets[expire] = expireSet
}
expireSet = gset.New()
c.expireSetMu.Lock()
if es, ok := c.expireSets[expire]; ok {
expireSet = es
} else {
c.expireSets[expire] = expireSet
}
c.expireSetMu.Unlock()
}
return
}
// 设置kv缓存键值对过期时间单位为毫秒expire<=0表示不过期
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) Set(key interface{}, value interface{}, expire int) {
expireTime := c.getInternalExpire(expire)
c.dataMu.Lock()
@ -107,8 +113,12 @@ func (c *memCache) Set(key interface{}, value interface{}, expire int) {
c.eventList.PushBack(&memCacheEvent{k : key, e : expireTime})
}
// 设置kv缓存键值对内部会对键名的存在性使用写锁进行二次检索确认如果存在则不再写入返回键名对应的键值。
// 在高并发下有用,防止数据写入的并发逻辑错误。
// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
//
// It doubly checks the <key> whether exists in the cache using mutex writing lock
// before setting it to the cache.
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire int) interface{} {
expireTimestamp := c.getInternalExpire(expire)
c.dataMu.Lock()
@ -128,7 +138,7 @@ func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire
return value
}
// 根据给定expire参数计算内部使用的expire过期时间
// getInternalExpire returns the expire time with given expire duration in milliseconds.
func (c *memCache) getInternalExpire(expire int) int64 {
if expire != 0 {
return gtime.Millisecond() + int64(expire)
@ -137,7 +147,9 @@ func (c *memCache) getInternalExpire(expire int) int64 {
}
}
// 当键名不存在时写入并返回true否则返回false。
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int) bool {
if !c.Contains(key) {
c.doSetWithLockCheck(key, value, expire)
@ -146,7 +158,8 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int)
return false
}
// 批量设置
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) Sets(data map[interface{}]interface{}, expire int) {
expireTime := c.getInternalExpire(expire)
for k, v := range data {
@ -157,13 +170,14 @@ func (c *memCache) Sets(data map[interface{}]interface{}, expire int) {
}
}
// 获取指定键名的值
// Get returns the value of <key>.
// It returns nil if it does not exist or its value is nil.
func (c *memCache) Get(key interface{}) interface{} {
c.dataMu.RLock()
item, ok := c.data[key]
c.dataMu.RUnlock()
if ok && !item.IsExpired() {
// 增加LRU(Least Recently Used)操作记录
// Adding to LRU history if LRU feature is enbaled.
if c.cap > 0 {
c.lruGetList.PushBack(key)
}
@ -172,7 +186,10 @@ func (c *memCache) Get(key interface{}) interface{} {
return nil
}
// 当键名存在时返回其键值,否则写入指定的键值
// GetOrSet returns the value of <key>,
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, value, expire)
@ -181,17 +198,26 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) inte
}
}
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
// GetOrSetFunc returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
if v := c.Get(key); v == nil {
// 可能存在多个goroutine被阻塞在这里f可能是并发运行
return c.doSetWithLockCheck(key, f(), expire)
} else {
return v
}
}
// GetOrSetFunc不同的是f是在写锁机制内执行
// GetOrSetFuncLock returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, f, expire)
@ -200,12 +226,12 @@ func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, expir
}
}
// 是否存在指定的键名true表示存在false表示不存在。
// Contains returns true if <key> exists in the cache, or else returns false.
func (c *memCache) Contains(key interface{}) bool {
return c.Get(key) != nil
}
// 删除指定键值对,并返回被删除的键值
// Remove deletes the <key> in the cache, and returns its value.
func (c *memCache) Remove(key interface{}) (value interface{}) {
c.dataMu.RLock()
item, ok := c.data[key]
@ -220,14 +246,14 @@ func (c *memCache) Remove(key interface{}) (value interface{}) {
return
}
// 批量删除键值对,并返回被删除的键值对数据
// Removes deletes <keys> in the cache.
func (c *memCache) Removes(keys []interface{}) {
for _, key := range keys {
c.Remove(key)
}
}
// 返回缓存的所有数据键值对(不包含已过期数据)
// Data returns a copy of all key-value pairs in the cache as map type.
func (c *memCache) Data() map[interface{}]interface{} {
m := make(map[interface{}]interface{})
c.dataMu.RLock()
@ -240,7 +266,7 @@ func (c *memCache) Data() map[interface{}]interface{} {
return m
}
// 获得所有的键名,组成数组返回
// Keys returns all keys in the cache as slice.
func (c *memCache) Keys() []interface{} {
keys := make([]interface{}, 0)
c.dataMu.RLock()
@ -253,12 +279,12 @@ func (c *memCache) Keys() []interface{} {
return keys
}
// 获得所有的键名,组成字符串数组返回
// KeyStrings returns all keys in the cache as string slice.
func (c *memCache) KeyStrings() []string {
return gconv.Strings(c.Keys())
}
// 获得所有的值,组成数组返回
// Values returns all values in the cache as slice.
func (c *memCache) Values() []interface{} {
values := make([]interface{}, 0)
c.dataMu.RLock()
@ -271,7 +297,7 @@ func (c *memCache) Values() []interface{} {
return values
}
// 获得缓存对象的键值对数量
// Size returns the size of the cache.
func (c *memCache) Size() (size int) {
c.dataMu.RLock()
size = len(c.data)
@ -279,7 +305,7 @@ func (c *memCache) Size() (size int) {
return
}
// 删除缓存对象
// Close closes the cache.
func (c *memCache) Close() {
if c.cap > 0 {
c.lru.Close()
@ -287,9 +313,10 @@ func (c *memCache) Close() {
c.closed.Set(true)
}
// 数据异步任务循环:
// 1、将事件列表中的数据异步处理并同步结果到expireTimes和expireSets属性中
// 2、清理过期键值对数据
// Asynchronous task loop:
// 1. asynchronously process the data in the event list,
// and synchronize the results to the <expireTimes> and <expireSets> properties.
// 2. clean up the expired key-value pair data.
func (c *memCache) syncEventAndClearExpired() {
event := (*memCacheEvent)(nil)
oldExpireTime := int64(0)
@ -299,7 +326,7 @@ func (c *memCache) syncEventAndClearExpired() {
return
}
// ========================
// 数据同步处理
// Data Synchronization.
// ========================
for {
v := c.eventList.PopFront()
@ -307,28 +334,28 @@ func (c *memCache) syncEventAndClearExpired() {
break
}
event = v.(*memCacheEvent)
// 获得旧的过期时间分组
// Fetching the old expire set.
c.expireTimeMu.RLock()
oldExpireTime = c.expireTimes[event.k]
c.expireTimeMu.RUnlock()
// 计算新的过期时间分组
// Calculating the new expire set.
newExpireTime = c.makeExpireKey(event.e)
if newExpireTime != oldExpireTime {
c.getOrNewExpireSet(newExpireTime).Add(event.k)
if oldExpireTime != 0 {
c.getOrNewExpireSet(oldExpireTime).Remove(event.k)
}
// 重新设置对应键名的过期时间
// Updating the expire time for <event.k>.
c.expireTimeMu.Lock()
c.expireTimes[event.k] = newExpireTime
c.expireTimeMu.Unlock()
}
// 写入操作也会增加到LRU(Least Recently Used)操作记录
// Adding the key the LRU history by writing operations.
if c.cap > 0 {
c.lru.Push(event.k)
}
}
// 异步处理读取操作的LRU列表
// Processing expired keys from LRU.
if c.cap > 0 && c.lruGetList.Len() > 0 {
for {
if v := c.lruGetList.PopFront(); v != nil {
@ -339,18 +366,18 @@ func (c *memCache) syncEventAndClearExpired() {
}
}
// ========================
// 缓存过期处理
// Data Cleaning up.
// ========================
ek := c.makeExpireKey(gtime.Millisecond())
eks := []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000}
for _, expireTime := range eks {
if expireSet := c.getExpireSet(expireTime); expireSet != nil {
// 遍历Set执行数据过期删除
// Iterating the set to delete all keys in it.
expireSet.Iterator(func(key interface{}) bool {
c.clearByKey(key)
return true
})
// Set数据处理完之后删除该Set
// Deleting the set after all of its keys are deleted.
c.expireSetMu.Lock()
delete(c.expireSets, expireTime)
c.expireSetMu.Unlock()
@ -358,22 +385,22 @@ func (c *memCache) syncEventAndClearExpired() {
}
}
// 删除对应键名的缓存数据
// clearByKey deletes the key-value pair with given <key>.
// The parameter <force> specifies whether doing this deleting forcely.
func (c *memCache) clearByKey(key interface{}, force...bool) {
// 删除缓存数据
c.dataMu.Lock()
// 删除核对,真正的过期才删除
// Doubly check before really deleting it from cache.
if item, ok := c.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) {
delete(c.data, key)
}
c.dataMu.Unlock()
// 删除异步处理数据项
// Deleting its expire time from <expireTimes>.
c.expireTimeMu.Lock()
delete(c.expireTimes, key)
c.expireTimeMu.Unlock()
// 删除LRU管理对象中指定键名
// Deleting it from LRU.
if c.cap > 0 {
c.lru.Remove(key)
}

View File

@ -8,9 +8,10 @@ package gcache
import "github.com/gogf/gf/g/os/gtime"
// 判断缓存项是否已过期
// IsExpired checks whether <item> is expired.
func (item *memCacheItem) IsExpired() bool {
// 注意这里应当包含等于试想一下缓存时间只有最小粒度为1毫秒的情况
// Note that it should use greater than or equal judgement here
// imagining that the cache time is only 1 millisecond.
if item.e >= gtime.Millisecond() {
return false
}

View File

@ -7,24 +7,24 @@
package gcache
import (
"fmt"
"github.com/gogf/gf/g/container/glist"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gtimer"
"time"
"github.com/gogf/gf/g/container/glist"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gtimer"
"time"
)
// LRU算法实现对象底层双向链表使用了标准库的list.List
// LRU cache object.
// It uses list.List from stdlib for its underlying doubly linked list.
type memCacheLru struct {
cache *memCache // 所属Cache对象
data *gmap.Map // 记录键名与链表中的位置项指针
list *glist.List // 键名历史记录链表
rawList *glist.List // 事件列表
closed *gtype.Bool // 是否关闭
cache *memCache // Parent cache object.
data *gmap.Map // Key mapping to the item of the list.
list *glist.List // Key list.
rawList *glist.List // History for key adding.
closed *gtype.Bool // Closed or not.
}
// 创建LRU管理对象
// newMemCacheLru creates and returns a new LRU object.
func newMemCacheLru(cache *memCache) *memCacheLru {
lru := &memCacheLru {
cache : cache,
@ -37,12 +37,12 @@ func newMemCacheLru(cache *memCache) *memCacheLru {
return lru
}
// 关闭LRU对象
// Close closes the LRU object.
func (lru *memCacheLru) Close() {
lru.closed.Set(true)
}
// 删除指定数据项
// Remove deletes the <key> FROM <lru>.
func (lru *memCacheLru) Remove(key interface{}) {
if v := lru.data.Get(key); v != nil {
lru.data.Remove(key)
@ -50,17 +50,17 @@ func (lru *memCacheLru) Remove(key interface{}) {
}
}
// 当前LRU数据大小
// Size returns the size of <lru>.
func (lru *memCacheLru) Size() int {
return lru.data.Size()
}
// 添加LRU数据项
// Push pushes <key> to the tail of <lru>.
func (lru *memCacheLru) Push(key interface{}) {
lru.rawList.PushBack(key)
}
// 从链表尾删除LRU数据项并返回对应数据
// Pop deletes and returns the key from tail of <lru>.
func (lru *memCacheLru) Pop() interface{} {
if v := lru.list.PopBack(); v != nil {
lru.data.Remove(v)
@ -69,34 +69,36 @@ func (lru *memCacheLru) Pop() interface{} {
return nil
}
// 从链表头打印LRU链表值
func (lru *memCacheLru) Print() {
for _, v := range lru.list.FrontAll() {
fmt.Printf("%v ", v)
}
fmt.Println()
}
// Print is used for test only.
//func (lru *memCacheLru) Print() {
// for _, v := range lru.list.FrontAll() {
// fmt.Printf("%v ", v)
// }
// fmt.Println()
//}
// 异步执行协程将queue中的数据同步到list中
// SyncAndClear synchronizes the keys from <rawList> to <list> and <data>
// using Least Recently Used algorithm.
func (lru *memCacheLru) SyncAndClear() {
if lru.closed.Val() {
gtimer.Exit()
return
}
// 数据同步
// Data synchronization.
for {
if v := lru.rawList.PopFront(); v != nil {
// 删除对应链表项
// Deleting the key from list.
if v := lru.data.Get(v); v != nil {
lru.list.Remove(v.(*glist.Element))
}
// 将数据插入到链表头,并记录对应的链表项到哈希表中,便于检索
// Pushing key to the head of the list
// and setting its list item to hash table for quick indexing.
lru.data.Set(v, lru.list.PushFront(v))
} else {
break
}
}
// 数据清理
// Data cleaning up.
for i := lru.Size() - lru.cache.cap; i > 0; i-- {
if s := lru.Pop(); s != nil {
lru.cache.clearByKey(s, true)

View File

@ -9,57 +9,265 @@
package gcache_test
import (
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/grpool"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
//clear 用于清除全局缓存因gcache api 暂未暴露 Clear 方法
//暂定所有测试用例key的集合为1,2,3避免不同测试用例间因全局cache共享带来的问题每个测试用例在测试gcache.XXX之前先调用clear()
func clear() {
gcache.Removes(g.Slice{1, 2, 3})
}
func TestCache_Set(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.Set(1, 11, 0)
gtest.Assert(cache.Get(1), 11)
})
gtest.Case(t, func() {
cache := gcache.New()
cache.Set(1, 11, 0)
gtest.Assert(cache.Get(1), 11)
gtest.Assert(cache.Contains(1), true)
clear()
gcache.Set(1, 11, 0)
gtest.Assert(gcache.Get(1), 11)
gtest.Assert(gcache.Contains(1), true)
})
}
func TestCache_Set_Expire(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.Set(2, 22, 100)
gtest.Assert(cache.Get(2), 22)
time.Sleep(200*time.Millisecond)
gtest.Assert(cache.Get(2), nil)
time.Sleep(3*time.Second)
gtest.Assert(cache.Size(), 0)
})
gtest.Case(t, func() {
cache := gcache.New()
cache.Set(2, 22, 100)
gtest.Assert(cache.Get(2), 22)
time.Sleep(200 * time.Millisecond)
gtest.Assert(cache.Get(2), nil)
time.Sleep(3 * time.Second)
gtest.Assert(cache.Size(), 0)
cache.Close()
})
}
func TestCache_Keys_Values(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
for i := 0; i < 10; i++ {
cache.Set(i, i*10, 0)
}
gtest.Assert(len(cache.Keys()), 10)
gtest.Assert(len(cache.Values()), 10)
gtest.AssertIN(0, cache.Keys())
gtest.AssertIN(90, cache.Values())
})
gtest.Case(t, func() {
cache := gcache.New()
for i := 0; i < 10; i++ {
cache.Set(i, i*10, 0)
}
gtest.Assert(len(cache.Keys()), 10)
gtest.Assert(len(cache.Values()), 10)
gtest.AssertIN(0, cache.Keys())
gtest.AssertIN(90, cache.Values())
})
}
func TestCache_LRU(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New(2)
for i := 0; i < 10; i++ {
cache.Set(i, i, 0)
}
gtest.Case(t, func() {
cache := gcache.New(2)
for i := 0; i < 10; i++ {
cache.Set(i, i, 0)
}
gtest.Assert(cache.Size(), 10)
gtest.Assert(cache.Get(6), 6)
time.Sleep(4 * time.Second)
gtest.Assert(cache.Size(), 2)
gtest.Assert(cache.Get(6), 6)
gtest.Assert(cache.Get(1), nil)
cache.Close()
})
}
gtest.Assert(cache.Size(), 10)
gtest.Assert(cache.Get(6), 6)
time.Sleep(4*time.Second)
gtest.Assert(cache.Size(), 2)
gtest.Assert(cache.Get(6), 6)
gtest.Assert(cache.Get(1), nil)
})
}
func TestCache_LRU_expire(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New(2)
cache.Set(1, nil, 1000)
gtest.Assert(cache.Size(), 1)
gtest.Assert(cache.Get(1), nil)
})
}
func TestCache_SetIfNotExist(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.SetIfNotExist(1, 11, 0)
gtest.Assert(cache.Get(1), 11)
cache.SetIfNotExist(1, 22, 0)
gtest.Assert(cache.Get(1), 11)
cache.SetIfNotExist(2, 22, 0)
gtest.Assert(cache.Get(2), 22)
clear()
gcache.SetIfNotExist(1, 11, 0)
gtest.Assert(gcache.Get(1), 11)
gcache.SetIfNotExist(1, 22, 0)
gtest.Assert(gcache.Get(1), 11)
})
}
func TestCache_Sets(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
gtest.Assert(cache.Get(1), 11)
clear()
gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
gtest.Assert(gcache.Get(1), 11)
})
}
func TestCache_GetOrSet(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.GetOrSet(1, 11, 0)
gtest.Assert(cache.Get(1), 11)
cache.GetOrSet(1, 111, 0)
gtest.Assert(cache.Get(1), 11)
clear()
gcache.GetOrSet(1, 11, 0)
gtest.Assert(gcache.Get(1), 11)
gcache.GetOrSet(1, 111, 0)
gtest.Assert(gcache.Get(1), 11)
})
}
func TestCache_GetOrSetFunc(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.GetOrSetFunc(1, func() interface{} {
return 11
}, 0)
gtest.Assert(cache.Get(1), 11)
cache.GetOrSetFunc(1, func() interface{} {
return 111
}, 0)
gtest.Assert(cache.Get(1), 11)
clear()
gcache.GetOrSetFunc(1, func() interface{} {
return 11
}, 0)
gtest.Assert(gcache.Get(1), 11)
gcache.GetOrSetFunc(1, func() interface{} {
return 111
}, 0)
gtest.Assert(gcache.Get(1), 11)
})
}
func TestCache_GetOrSetFuncLock(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.GetOrSetFuncLock(1, func() interface{} {
return 11
}, 0)
gtest.Assert(cache.Get(1), 11)
cache.GetOrSetFuncLock(1, func() interface{} {
return 111
}, 0)
gtest.Assert(cache.Get(1), 11)
clear()
gcache.GetOrSetFuncLock(1, func() interface{} {
return 11
}, 0)
gtest.Assert(gcache.Get(1), 11)
gcache.GetOrSetFuncLock(1, func() interface{} {
return 111
}, 0)
gtest.Assert(gcache.Get(1), 11)
})
}
func TestCache_Clear(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
cache.Clear()
gtest.Assert(cache.Size(), 0)
})
}
func TestCache_SetConcurrency(t *testing.T) {
gtest.Case(t, func() {
cache := gcache.New()
pool := grpool.New(4)
go func() {
for {
pool.Add(func() {
cache.SetIfNotExist(1, 11, 10)
})
}
}()
select {
case <-time.After(2 * time.Second):
t.Log("first part end")
}
go func() {
for {
pool.Add(func() {
cache.SetIfNotExist(1, nil, 10)
})
}
}()
select {
case <-time.After(2 * time.Second):
t.Log("second part end")
}
})
}
func TestCache_Basic(t *testing.T) {
gtest.Case(t, func() {
{
cache := gcache.New()
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
gtest.Assert(cache.Contains(1), true)
gtest.Assert(cache.Get(1), 11)
data := cache.Data()
gtest.Assert(data[1], 11)
gtest.Assert(data[2], 22)
gtest.Assert(data[3], nil)
gtest.Assert(cache.Size(), 2)
keys := cache.Keys()
gtest.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
keyStrs := cache.KeyStrings()
gtest.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
values := cache.Values()
gtest.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
removeData1 := cache.Remove(1)
gtest.Assert(removeData1, 11)
gtest.Assert(cache.Size(), 1)
cache.Removes(g.Slice{2})
gtest.Assert(cache.Size(), 0)
}
clear()
{
gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
gtest.Assert(gcache.Contains(1), true)
gtest.Assert(gcache.Get(1), 11)
data := gcache.Data()
gtest.Assert(data[1], 11)
gtest.Assert(data[2], 22)
gtest.Assert(data[3], nil)
gtest.Assert(gcache.Size(), 2)
keys := gcache.Keys()
gtest.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
keyStrs := gcache.KeyStrings()
gtest.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
values := gcache.Values()
gtest.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
removeData1 := gcache.Remove(1)
gtest.Assert(removeData1, 11)
gtest.Assert(gcache.Size(), 1)
gcache.Removes(g.Slice{2})
gtest.Assert(gcache.Size(), 0)
}
})
}

View File

@ -40,7 +40,7 @@ type Config struct {
}
// New returns a new configuration management object.
// The param <file> specifies the default configuration file name for reading.
// The parameter <file> specifies the default configuration file name for reading.
func New(file...string) *Config {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
@ -55,20 +55,22 @@ func New(file...string) *Config {
// Customized dir path from env/cmd.
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" {
if gfile.Exists(envPath) {
c.SetPath(envPath)
_ = c.SetPath(envPath)
} else {
glog.Errorfln("Configuration directory path does not exist: %s", envPath)
if errorPrint() {
glog.Errorf("Configuration directory path does not exist: %s", envPath)
}
}
} else {
// Dir path of working dir.
c.SetPath(gfile.Pwd())
_ = c.SetPath(gfile.Pwd())
// Dir path of binary.
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
c.AddPath(selfPath)
_ = c.AddPath(selfPath)
}
// Dir path of main package.
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
c.AddPath(mainPath)
_ = c.AddPath(mainPath)
}
}
return c
@ -97,13 +99,15 @@ func (c *Config) filePath(file...string) (path string) {
} else {
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name))
}
glog.Error(buffer.String())
if errorPrint() {
glog.Error(buffer.String())
}
}
return path
}
// SetPath sets the configuration directory path for file search.
// The param <path> can be absolute or relative path,
// The parameter <path> can be absolute or relative path,
// but absolute path is strongly recommended.
func (c *Config) SetPath(path string) error {
// Absolute path.
@ -133,13 +137,17 @@ func (c *Config) SetPath(path string) error {
buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
if errorPrint() {
glog.Error(err)
}
return err
}
// Should be a directory.
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
if errorPrint() {
glog.Error(err)
}
return err
}
// Repeated path check.
@ -191,12 +199,16 @@ func (c *Config) AddPath(path string) error {
buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
if errorPrint() {
glog.Error(err)
}
return err
}
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
if errorPrint() {
glog.Error(err)
}
return err
}
// Repeated path check.
@ -269,17 +281,19 @@ func (c *Config) getJson(file...string) *gjson.Json {
// Add monitor for this configuration file,
// any changes of this file will refresh its cache in Config object.
if filePath != "" {
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
_, _ = gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
})
}
return j
} else {
if filePath != "" {
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
} else {
glog.Criticalfln(`[gcfg] Load configuration failed: %s`, err.Error())
}
if errorPrint() {
if filePath != "" {
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
} else {
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
}
}
}
return nil
})

22
g/os/gcfg/gcfg_error.go Normal file
View File

@ -0,0 +1,22 @@
// 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 gcfg
import (
"github.com/gogf/gf/g/internal/cmdenv"
)
const (
// gERROR_PRINT_KEY is used to specify the key controlling error printing to stdout.
// This error is designed not to be returned by functions.
gERROR_PRINT_KEY = "gf.gcfg.errorprint"
)
// errorPrint checks whether printing error to stdout.
func errorPrint() bool {
return cmdenv.Get(gERROR_PRINT_KEY, true).Bool()
}

View File

@ -20,7 +20,7 @@ var (
)
// Instance returns an instance of Config with default settings.
// The param <name> is the name for the instance.
// The parameter <name> is the name for the instance.
func Instance(name...string) *Config {
key := DEFAULT_GROUP_NAME
if len(name) > 0 {

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