mirror of
https://gitee.com/johng/gf
synced 2026-06-09 02:57:43 +08:00
Compare commits
228 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d04c2e50a | |||
| 7b26b7ea4c | |||
| 9d1063c6b2 | |||
| 5ff7632d32 | |||
| 9400457bf2 | |||
| 5060329721 | |||
| 07ab1d60e8 | |||
| 7377a82e19 | |||
| f82e3ac808 | |||
| 1a6cd1de04 | |||
| 0fc825dac1 | |||
| dbb27efe3e | |||
| 2d3d2e783e | |||
| 6ae1defa35 | |||
| d55e77fb90 | |||
| 7fb9ae71e7 | |||
| 9419555149 | |||
| ab634f8beb | |||
| 9503b80d57 | |||
| bc870226e5 | |||
| 7e106ae011 | |||
| 958e00e231 | |||
| ab187d225d | |||
| 9f5711d41d | |||
| cdb2773127 | |||
| d12532ccc1 | |||
| 53e9f05a10 | |||
| 60e7ab95bc | |||
| ae552e2b46 | |||
| 145b52f343 | |||
| 57b8fac0d5 | |||
| 3d37c83532 | |||
| b02fa701b8 | |||
| 37b7c82c64 | |||
| 1deb27df06 | |||
| 84ed3d5767 | |||
| fc909a3db2 | |||
| 2f7d4cd80d | |||
| 03ccbf3613 | |||
| 1868465319 | |||
| ff473e2fcc | |||
| a3c38eec86 | |||
| 2015c847e8 | |||
| 1b583ed984 | |||
| 11191c746a | |||
| abedd3c5bf | |||
| 501ba5135b | |||
| a76c98c348 | |||
| 9dcdc1a339 | |||
| 6d1f386203 | |||
| ff9bbf0a49 | |||
| 66e24c8d40 | |||
| 34f117c631 | |||
| 21f2f16889 | |||
| f6fa7c422d | |||
| 6903b84bb6 | |||
| 0978b8fb4f | |||
| d014583e88 | |||
| a747f51b9d | |||
| 8300885ab6 | |||
| 0b57771d76 | |||
| 8a32a8271c | |||
| c3e716dafd | |||
| 119a11eb8d | |||
| e464d14842 | |||
| 905f46359a | |||
| 8e7bf1f908 | |||
| 3a1524ae6d | |||
| 5c04befea3 | |||
| ae3584cdff | |||
| a60578c82c | |||
| 74558a500c | |||
| d963d8c8c1 | |||
| 853892c24f | |||
| 641f939e3d | |||
| af77504eaf | |||
| 62649d6468 | |||
| bb914e605e | |||
| 62ee88bbfa | |||
| 99c964bb4a | |||
| 09ceaef3e9 | |||
| a82900af55 | |||
| 5ef589f31a | |||
| cd719f134d | |||
| f69eb219b5 | |||
| 0ffe17ee3d | |||
| 4ac647a215 | |||
| 872536c035 | |||
| c7a6a6fff0 | |||
| 2534655bc8 | |||
| 0cb82d70fd | |||
| acac5a2ad6 | |||
| 9ec15ad2ca | |||
| a9b7d56d0b | |||
| e9ca1eb538 | |||
| 0532800895 | |||
| ad50ca6e60 | |||
| c1ad999c25 | |||
| 4accd1264d | |||
| e1f3da3aa8 | |||
| 4bf9a7950b | |||
| 8285c31bf1 | |||
| 230be66fa9 | |||
| 2c53f934c9 | |||
| 08785cb272 | |||
| bd0207c938 | |||
| 6fad737617 | |||
| af4148d985 | |||
| 922eaf4d42 | |||
| 4b5153950f | |||
| 78010d2bd7 | |||
| 6cc0017826 | |||
| 8a9131c3dc | |||
| 85c2ed1bf2 | |||
| aca1df634d | |||
| 053a3c1a53 | |||
| 429aa90e0d | |||
| 4f792b347d | |||
| 468c315087 | |||
| 6d8ced21b9 | |||
| b3d5fc149e | |||
| 277b7a4536 | |||
| 43886511b9 | |||
| 8b3eb5b02d | |||
| 054ef87886 | |||
| 39c65d9e9a | |||
| b97bbbfa3d | |||
| ace6ba8096 | |||
| a2b87d84e9 | |||
| b90d61b27c | |||
| 85606e3e7e | |||
| 1fc85d49bd | |||
| a5cfb4e638 | |||
| 38754bf062 | |||
| f1818ed2ff | |||
| 352ad17715 | |||
| e50b8d9632 | |||
| 2107061a46 | |||
| 61f57b4895 | |||
| d34273abff | |||
| 0aff0f0362 | |||
| 68949b69bc | |||
| c56c77d3a1 | |||
| dc82ce395a | |||
| fd63a2209b | |||
| 2a29483456 | |||
| 4f10562980 | |||
| a26ec37f59 | |||
| 779ad93bcb | |||
| 1ec0219473 | |||
| 6863928b06 | |||
| aa4dca11f0 | |||
| dc6ab820ce | |||
| be0fa4d60b | |||
| a86d2272af | |||
| 61a67892ac | |||
| 388d5954cb | |||
| 08550d413e | |||
| b89294561b | |||
| 20977558cc | |||
| 630d8fdb43 | |||
| c4c7e6caf4 | |||
| 8a8fea1257 | |||
| 0e0f297a3f | |||
| fd2c0f2b24 | |||
| ecc6e3888d | |||
| 47c073aaf3 | |||
| 07476a4349 | |||
| 817148f3a1 | |||
| bd4271cd8c | |||
| b1804fc346 | |||
| a3886c2179 | |||
| f258b5bf1c | |||
| a05361011f | |||
| 6b34a77251 | |||
| afb1adee3d | |||
| 22fa7a37f3 | |||
| 6a58bfc574 | |||
| 64124c60fc | |||
| 9a0066de62 | |||
| 22c7c7403b | |||
| 83db8e4b15 | |||
| 25f2e121e7 | |||
| 1325a145d8 | |||
| 9bc49c0b29 | |||
| 8e84e5b0f3 | |||
| a42e6b0c45 | |||
| 51bb7a9854 | |||
| 62580b5719 | |||
| 15cfd5ce5c | |||
| 11c89c4090 | |||
| 4a12cb9f27 | |||
| 32f575eddd | |||
| fbb4cb3b1c | |||
| c9d2d5e8ab | |||
| 93763192f2 | |||
| 80e0eae6b0 | |||
| 4e3d735b90 | |||
| 60e5a7da28 | |||
| 997b5ba889 | |||
| b3e7ca1963 | |||
| 5a82d695c1 | |||
| 64a0427150 | |||
| f6aafc1d6b | |||
| bca5532df8 | |||
| 72eeadd9aa | |||
| 0af55794f6 | |||
| 25a6c53533 | |||
| 9f9172c775 | |||
| 320e0db417 | |||
| cb8362d447 | |||
| 45a83fc53c | |||
| 3411bd1c1d | |||
| 6ab0a77364 | |||
| 281bae4116 | |||
| 218c692fe0 | |||
| fa69b581e1 | |||
| bd0baceeca | |||
| e71c837472 | |||
| 782aaabd07 | |||
| 9014325a7c | |||
| 8ae9276732 | |||
| 79a3aa5916 | |||
| 733c5db228 | |||
| 6171c621a7 | |||
| fc11856a28 | |||
| b0726b9733 | |||
| 4e4ea25e7f |
19
.travis.yml
19
.travis.yml
@ -10,28 +10,29 @@ branches:
|
||||
- develop
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- mysql
|
||||
- redis-server
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
- local
|
||||
- local
|
||||
|
||||
before_install:
|
||||
- pwd
|
||||
- pwd
|
||||
|
||||
install:
|
||||
- cat /etc/hosts
|
||||
- cat /etc/hosts
|
||||
|
||||
script:
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
|
||||
|
||||
63
README.MD
63
README.MD
@ -5,9 +5,8 @@
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
|
||||
<!--
|
||||
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.
|
||||
@ -31,7 +30,7 @@ golang version >= 1.9.2
|
||||
|
||||
# Documentation
|
||||
|
||||
* [GoDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
|
||||
# Architecture
|
||||
@ -65,42 +64,38 @@ func main() {
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Contributors(TOP 10)
|
||||
|
||||
<a href="https://gitee.com/johng" target="_blank" title="John"><img src="https://gitee.com/uploads/27/1309327_johng.png?1530630243" width="60" align="left"></a>
|
||||
# Contributors
|
||||
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://github.com/chenyang351" target="_blank" title="chenyang351"><img src="https://avatars1.githubusercontent.com/u/30063958?s=60&v=4" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/wxkj" target="_blank" title="wxkj"><img src="https://gitee.com/uploads/56/91356_wxkj.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://github.com/wxkj001" target="_blank" title="3wxkj001
|
||||
"><img src="https://avatars0.githubusercontent.com/u/7794279?s=60&v=4" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
<br /><br /><br />
|
||||
- [aloncn](https://github.com/aloncn)
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [johng](https://johng.cn)
|
||||
- [jroam](https://github.com/jroam)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
- [youyixiao](https://github.com/youyixiao)
|
||||
- [zhangjinfu](https://gitee.com/zhangjinfu)
|
||||
- [zhaopengme](https://github.com/zhaopengme)
|
||||
- [zseeker](https://gitee.com/zseeker)
|
||||
|
||||
# Donators
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/mg91" target="_blank" title="mg91"><img src="https://images.gitee.com/uploads/30/1410930_mg91.png" width="60" align="left"></a>
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [ireadx](https://github.com/ireadx)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [zhuhuan12](https://gitee.com/zhuhuan12)
|
||||
- [zfan_codes](https://gitee.com/zfan_codes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
52
README_ZH.MD
52
README_ZH.MD
@ -5,9 +5,8 @@
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
|
||||
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
|
||||
并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
@ -31,7 +30,6 @@ go get -u github.com/gogf/gf
|
||||
```
|
||||
require github.com/gogf/gf latest
|
||||
```
|
||||
> 如果您是从旧版本`1.x`升级到`1.5.0`那么请参考:[1.x升级到1.5.0](https://goframe.org/upgradeto150)
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
@ -85,30 +83,34 @@ func main() {
|
||||
<img src="https://goframe.org/images/donate.png" width="300"/>
|
||||
</a>
|
||||
|
||||
# 贡献者(TOP 10)
|
||||
# 贡献者
|
||||
|
||||
<a href="https://gitee.com/johng" target="_blank" title="John"><img src="https://gitee.com/uploads/27/1309327_johng.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/chenyang351" target="_blank" title="chenyang351"><img src="https://avatars1.githubusercontent.com/u/30063958?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wxkj" target="_blank" title="wxkj"><img src="https://gitee.com/uploads/56/91356_wxkj.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/wxkj001" target="_blank" title="3wxkj001
|
||||
"><img src="https://avatars0.githubusercontent.com/u/7794279?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
<br /><br /><br />
|
||||
- [aloncn](https://github.com/aloncn)
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [johng](https://johng.cn)
|
||||
- [jroam](https://github.com/jroam)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
- [youyixiao](https://github.com/youyixiao)
|
||||
- [zhangjinfu](https://gitee.com/zhangjinfu)
|
||||
- [zhaopengme](https://github.com/zhaopengme)
|
||||
- [zseeker](https://gitee.com/zseeker)
|
||||
|
||||
# 捐赠者
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [ireadx](https://github.com/ireadx)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
- [wxkj](https://gitee.com/wxkj)
|
||||
- [zhuhuan12](https://gitee.com/zhuhuan12)
|
||||
- [zfan_codes](https://gitee.com/zfan_codes)
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/mg91" target="_blank" title="mg91"><img src="https://images.gitee.com/uploads/30/1410930_mg91.png" width="60" align="left"></a>
|
||||
48
RELEASE.MD
48
RELEASE.MD
@ -1,3 +1,51 @@
|
||||
# `v1.6.0` (2019-04-09)
|
||||
|
||||
## 新功能/改进
|
||||
1. `gcron`定时任务模块增加运行日志记录功能:https://goframe.org/os/gcron/index
|
||||
1. `gredis`增加全局分组配置功能,并增加更多的配置选项`maxIdle/maxActive/idleTimeout/maxConnLifetime`:https://goframe.org/database/gredis/index
|
||||
1. `gcfg`模块增加更多的默认配置文件检索路径,并且增加全局分组配置特性,增加`Instance`单例方法:https://goframe.org/os/gcfg/index
|
||||
1. `gview`模块增加更多的默认配置文件检索路径,并且增加`Instance`单例方法:https://goframe.org/os/gview/index
|
||||
1. `ghttp`模块新功能及改进:
|
||||
- 新增`CORS`HTTP(S)跨域请求特性: https://goframe.org/net/ghttp/cors
|
||||
- 增加`TLSConfig`配置功能;
|
||||
- 去掉路由注册方法的`error`返回值,当产生注册错误时直接终端打印错误/输出到日志文件;
|
||||
- 增加在`HTTP Code 302`跳转时的`Set-Cookie`支持;
|
||||
- 增加对`SESSION ID`的安全性检查;
|
||||
- 增加对基于`HTTPS`的`WebSocket`支持(`WSS`):https://goframe.org/net/ghttp/websocket/index
|
||||
- `Request`对象增加`Error`方法,用于输出自定义错误信息到`WebServer`错误日志中;
|
||||
- 其他一些改进;
|
||||
1. `gdb`模块新功能及改进:
|
||||
- 新增`Instance`单例管理方法;
|
||||
- 新增`Structs/Scan`链式操作方法,`gdb.DB/TX`新增`GetStructs/GetScan`方法,用于结果集`struct`/`slice`映射转换:https://goframe.org/database/gdb/chaining
|
||||
- 新增`Safe`链式操作方法(默认非并发安全),用于链式安全控制:https://goframe.org/database/gdb/chaining
|
||||
- `Where`链式操作方法改进:
|
||||
- 方法支持任意的`string/map/slice/struct/*struct`类型;
|
||||
- 逻辑调整,当链式操作中存在多个`Where`方法调用时,自动转换为`And`条件;
|
||||
- 支持`slice`条件参数,常用在`SELECT IN`查询中,例如:`Where("uid IN(?)", g.Slice{1,2,3})`;
|
||||
- 支持在`map`类型条件参数的`key`中传递条件,例如:`Where(g.Map{"uid>?", uid})`;
|
||||
1. `gconv`及`gvalid`模块改进并去掉对私有`struct`方法属性的转换/校验;
|
||||
1. `gconv.Map`转换方法新增对`json tag`: `-`, `omitempty`的支持: https://goframe.org/util/gconv/map
|
||||
1. `gstr`模块新增 `ReplaceI/ReplaceIByArray/ReplaceIByMap`大小写非敏感替换方法;
|
||||
1. `gutil`模块增加`IsEmpty`方法,用于判断给定变量是否为空(整型0, 布尔false, slice/map长度为0, 其他为nil的情况,判断为空),并增加快捷方法`g.IsEmpty`;
|
||||
1. `gutil`模块增加`Export`方法,用于导出返回格式化打印的变量内容字符串,并增加快捷方法`g.Export`;
|
||||
1. `gspath`增加缓存及非缓存检索检索方法`Search`/`SearchWithCache`;
|
||||
1. `gjson`模块增加默认的`UseNumber`功能支持;
|
||||
1. `gmap`增加`SetIfNotExistFunc/SetIfNotExistFuncLock`方法;
|
||||
1. 迁移`greuseport`模块到新的仓库:https://github.com/gogf/greuseport
|
||||
1. 大量的单元测试完善;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gqueue`模块的资源竞争问题;
|
||||
1. 修复`gconv.GTime`转换失败问题;
|
||||
1. 修复`gconv.String`在转换`int`参数时字节溢出问题;
|
||||
1. 修复`ghttp.Request`的`HTTP Basic Auth`校验问题;
|
||||
1. 修复`gxml`针对于非`UTF-8`编码内容转换的并发安全问题;
|
||||
1. 修复`gtime`部分`Format`(`G`&`j`)格式失效问题;
|
||||
1. 修复`gudp.Conn`对象的`RemoteAddr`获取客户端连接地址方法问题;
|
||||
1. 修复`gmap/gcache`模块的`GetOrSetFuncLock`方法,增加对回调方法返回值的`nil`判断,只有非nil返回值才会被保存;
|
||||
|
||||
|
||||
|
||||
# `v1.5.8` (2019-02-28)
|
||||
|
||||
## 新特性
|
||||
|
||||
45
TODO.MD
45
TODO.MD
@ -1,23 +1,16 @@
|
||||
# ON THE WAY
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. 增加热编译工具,提高开发环境的开发/测试效率(媲美PHP开发效率);
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
1. ghttp.Response增加输出内容后自动退出当前请求机制,不需要用户手动return,参考beego如何实现;
|
||||
1. Cookie&Session数据池化处理;
|
||||
1. ghttp.Client增加proxy特性;
|
||||
1. gtime增加对时区转换的封装,并简化失去转换时对类似+80500时区的支持;
|
||||
1. orm增加sqlite对Save方法的支持(去掉触发器语句);
|
||||
1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps);
|
||||
1. ghttp路由功能增加分组路由特性;
|
||||
1. ghttp增加返回数据压缩机制;
|
||||
1. gview中的template标签失效问题;
|
||||
1. gfile文件stat信息使用gfsnotify进行缓存更新改进;
|
||||
1. ghttp.Server增加proxy功能特性,本地proxy和远程proxy,本地即将路由规则映射;远程即反向代理;
|
||||
1. gjson对大json数据的解析效率问题;
|
||||
1. ghttp增加route name特性,并同时支持backend和template(提供内置函数)引用,可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上;
|
||||
1. ghttp.Client自动Close机制;
|
||||
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则,最好做成链式操作;
|
||||
1. 检查ghttp.Server超时问题;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. ghttp.Request增加对输入参数的自动HtmlEncode机制;
|
||||
1. 常量命名风格根据golint进行修改;
|
||||
@ -36,31 +29,22 @@
|
||||
- https://github.com/Masterminds/sprig
|
||||
1. gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
|
||||
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题,并完善文档(参考:https://www.cnblogs.com/kex1n/p/6502002.html);
|
||||
1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性;
|
||||
1. 路由增加不区分大小写得匹配方式;
|
||||
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
|
||||
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
|
||||
1. 改进WebServer获取POST参数处理逻辑,当提交非form数据时,例如json数据,针对某些方法可以直接解析;
|
||||
1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. 权限管理模块;
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
|
||||
|
||||
|
||||
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持;
|
||||
1. 添加sqlite数据库的单元测试用例;
|
||||
1. gredis增加cluster支持;
|
||||
1. gset.Add/Remove/Contains方法增加批量操作支持;
|
||||
1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁;
|
||||
|
||||
# DONE
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
@ -119,4 +103,19 @@
|
||||
1. gform对于MySQL字段类型为datetime类型的时区问题分析;
|
||||
1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行;
|
||||
1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性;
|
||||
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp.Response增加输出内容后自动退出当前请求机制,不需要用户手动return,参考beego如何实现;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
1. gdb执行数据写入时,如果参数为struct/[]struct,自动映射与表字段对应关系,不再使用gconv标签标识;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
|
||||
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
|
||||
1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性;
|
||||
1. ghttp.Client自动Close机制;
|
||||
1. ghttp路由功能增加分组路由特性;
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
@ -4,8 +4,6 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides kinds of concurrent-safe(alternative) arrays.
|
||||
//
|
||||
// 并发安全数组.
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
package garray
|
||||
|
||||
|
||||
@ -441,8 +441,8 @@ func (a *IntArray) Unique() *IntArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -451,8 +451,8 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -433,8 +433,8 @@ func (a *Array) Unique() *Array {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -443,8 +443,8 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -440,8 +440,8 @@ func (a *StringArray) Unique() *StringArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -450,8 +450,8 @@ func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -415,8 +415,8 @@ func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -425,8 +425,8 @@ func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -422,8 +422,8 @@ func (a *SortedArray) Clear() *SortedArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -432,8 +432,8 @@ func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -410,8 +410,8 @@ func (a *SortedStringArray) Clear() *SortedStringArray {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -420,8 +420,8 @@ func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
// 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 operations for channel.
|
||||
// Package gchan provides graceful channel for safe operations.
|
||||
//
|
||||
// 优雅的Channel操作.
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
|
||||
import (
|
||||
@ -15,39 +15,48 @@ import (
|
||||
)
|
||||
|
||||
type Chan struct {
|
||||
list chan interface{}
|
||||
closed *gtype.Bool
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given limit.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan {
|
||||
list : make(chan interface{}, limit),
|
||||
closed : gtype.NewBool(),
|
||||
channel : make(chan interface{}, limit),
|
||||
closed : gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
|
||||
// 将数据压入队列
|
||||
func (q *Chan) Push(v interface{}) error {
|
||||
if q.closed.Val() {
|
||||
// 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")
|
||||
}
|
||||
q.list <- v
|
||||
c.channel <- value
|
||||
return nil
|
||||
}
|
||||
|
||||
// 先进先出地从队列取出一项数据,当没有数据可获取时,阻塞等待
|
||||
func (q *Chan) Pop() interface{} {
|
||||
return <- q.list
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <- c.channel
|
||||
}
|
||||
|
||||
// 关闭队列(通知所有通过Pop阻塞的协程退出)
|
||||
func (q *Chan) Close() {
|
||||
if !q.closed.Set(true) {
|
||||
close(q.list)
|
||||
// Close closes the channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Close() {
|
||||
if !c.closed.Set(true) {
|
||||
close(c.channel)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前队列大小
|
||||
func (q *Chan) Size() int {
|
||||
return len(q.list)
|
||||
// See Len.
|
||||
func (c *Chan) Size() int {
|
||||
return c.Len()
|
||||
}
|
||||
|
||||
// Len returns the length of the channel.
|
||||
func (c *Chan) Len() int {
|
||||
return len(c.channel)
|
||||
}
|
||||
@ -5,9 +5,7 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides a concurrent-safe(alternative) doubly linked list.
|
||||
//
|
||||
// 并发安全双向链表.
|
||||
// Package glist provides a concurrent-safe/unsafe doubly linked list.
|
||||
package glist
|
||||
|
||||
import (
|
||||
@ -15,14 +13,14 @@ import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
// 变长双向链表
|
||||
type List struct {
|
||||
mu *rwmutex.RWMutex
|
||||
list *list.List
|
||||
}
|
||||
|
||||
type Element = list.Element
|
||||
type (
|
||||
List struct {
|
||||
mu *rwmutex.RWMutex
|
||||
list *list.List
|
||||
}
|
||||
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
// 获得一个变长链表指针
|
||||
func New(unsafe...bool) *List {
|
||||
|
||||
@ -4,46 +4,34 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides kinds of concurrent-safe(alternative) maps.
|
||||
//
|
||||
// 并发安全MAP.
|
||||
// Package gmap provides concurrent-safe/unsafe maps.
|
||||
package gmap
|
||||
|
||||
import "github.com/gogf/gf/g/internal/rwmutex"
|
||||
|
||||
// 注意:
|
||||
// 1、这个Map是所有并发安全Map中效率最低的,如果对效率要求比较高的场合,请合理选择对应数据类型的Map;
|
||||
// 2、这个Map的优点是使用简便,由于键值都是interface{}类型,因此对键值的数据类型要求不高;
|
||||
// 3、底层实现比较类似于sync.Map;
|
||||
type Map struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// Create an empty hash map.
|
||||
// New returns an empty hash map.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的哈希表,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func New(unsafe...bool) *Map {
|
||||
// which is false in default, means concurrent-safe.
|
||||
func New(unsafe ...bool) *Map {
|
||||
return NewMap(unsafe...)
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New方法。
|
||||
func NewMap(unsafe...bool) *Map {
|
||||
// Alias of New. See New.
|
||||
func NewMap(unsafe ...bool) *Map {
|
||||
return &Map{
|
||||
m : make(map[interface{}]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a hash map from given map.
|
||||
// Be aware that, the param map is a type of pointer,
|
||||
// NewFrom returns a hash map from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
//
|
||||
// 基于给定的map变量创建哈希表对象,注意由于map是指针类型,外部对map有操作时会有并发安全问题。
|
||||
func NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
return &Map{
|
||||
m : m,
|
||||
@ -51,12 +39,12 @@ func NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a hash map from given arrays.
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as the corresponding values.
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// 基于给定的数组变量创建哈希表对象,keys作为键名, values作为键值。
|
||||
// 当keys数组大小比values数组大时,多余的键名将会使用对应类型默认的键值。
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map {
|
||||
m := make(map[interface{}]interface{})
|
||||
l := len(values)
|
||||
@ -73,10 +61,8 @@ func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (gm *Map) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -87,17 +73,12 @@ func (gm *Map) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clone current hash map with copy of underlying data,
|
||||
// return a new hash map.
|
||||
//
|
||||
// 哈希表克隆.
|
||||
func (gm *Map) Clone() *Map {
|
||||
return NewFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *Map) Clone(unsafe ...bool) *Map {
|
||||
return NewFrom(gm.Map(), unsafe ...)
|
||||
}
|
||||
|
||||
// Returns copy of the data of the hash map.
|
||||
//
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *Map) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
gm.mu.RLock()
|
||||
@ -108,18 +89,14 @@ func (gm *Map) Map() map[interface{}]interface{} {
|
||||
return m
|
||||
}
|
||||
|
||||
// Set key-value to the hash map.
|
||||
//
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *Map) Set(key interface{}, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Batch set key-values to the hash map.
|
||||
//
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *Map) BatchSet(m map[interface{}]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -128,9 +105,7 @@ func (gm *Map) BatchSet(m map[interface{}]interface{}) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get value by key.
|
||||
//
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *Map) Get(key interface{}) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -138,8 +113,15 @@ func (gm *Map) Get(key interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -153,9 +135,8 @@ func (gm *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{
|
||||
return value
|
||||
}
|
||||
|
||||
// Get the value by key, or set it with given key-value if not exist.
|
||||
//
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
@ -164,9 +145,9 @@ func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value by key, or set the it with return of callback function <f> if not exist.
|
||||
//
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
@ -175,10 +156,12 @@ func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value by key, or set the it with return of callback function <f> if not exist.
|
||||
// The difference with GetOrSetFunc is, it locks in executing callback function <f>.
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
@ -187,9 +170,8 @@ func (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface
|
||||
}
|
||||
}
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
//
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -198,9 +180,30 @@ func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Batch remove by keys.
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *Map) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *Map) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *Map) BatchRemove(keys []interface{}) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -209,9 +212,7 @@ func (gm *Map) BatchRemove(keys []interface{}) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove by given key.
|
||||
//
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *Map) Remove(key interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -222,9 +223,7 @@ func (gm *Map) Remove(key interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// Return all the keys of hash map as a slice.
|
||||
//
|
||||
// 返回键列表.
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *Map) Keys() []interface{} {
|
||||
gm.mu.RLock()
|
||||
keys := make([]interface{}, 0)
|
||||
@ -235,9 +234,7 @@ func (gm *Map) Keys() []interface{} {
|
||||
return keys
|
||||
}
|
||||
|
||||
// Return all the values of hash map as a slice.
|
||||
//
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *Map) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
@ -248,9 +245,8 @@ func (gm *Map) Values() []interface{} {
|
||||
return vals
|
||||
}
|
||||
|
||||
// Check whether a key exist.
|
||||
//
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *Map) Contains(key interface{}) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -258,9 +254,7 @@ func (gm *Map) Contains(key interface{}) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// Get the size of hash map.
|
||||
//
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *Map) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -268,9 +262,8 @@ func (gm *Map) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Check whether the hash map is empty.
|
||||
//
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *Map) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -278,36 +271,28 @@ func (gm *Map) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear the hash map, it will remake a new underlying map data map.
|
||||
//
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *Map) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[interface{}]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Lock writing with given callback <f>.
|
||||
//
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *Map) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Lock reading with given callback <f>.
|
||||
//
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *Map) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Exchange key-value in the hash map, it will change key-value to value-key.
|
||||
//
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *Map) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -318,17 +303,16 @@ func (gm *Map) Flip() {
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge two hash maps.
|
||||
//
|
||||
// 合并两个Map.
|
||||
func (gm *Map) Merge(m *Map) {
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *Map) Merge(other *Map) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,9 @@ type IntBoolMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
}
|
||||
|
||||
// NewIntBoolMap returns an empty IntBoolMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntBoolMap(unsafe...bool) *IntBoolMap {
|
||||
return &IntBoolMap{
|
||||
m : make(map[int]bool),
|
||||
@ -23,6 +26,9 @@ func NewIntBoolMap(unsafe...bool) *IntBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntBoolMapFrom returns an IntBoolMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap {
|
||||
return &IntBoolMap{
|
||||
m : m,
|
||||
@ -30,6 +36,12 @@ func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntBoolMapFromArray returns an IntBoolMap from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMap {
|
||||
m := make(map[int]bool)
|
||||
l := len(values)
|
||||
@ -46,12 +58,12 @@ func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMa
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntBoolMap) Clone() *IntBoolMap {
|
||||
return NewIntBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntBoolMap) Map() map[int]bool {
|
||||
m := make(map[int]bool)
|
||||
gm.mu.RLock()
|
||||
@ -62,7 +74,8 @@ func (gm *IntBoolMap) Map() map[int]bool {
|
||||
return m
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -73,14 +86,14 @@ func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntBoolMap) Set(key int, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntBoolMap) BatchSet(m map[int]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -89,7 +102,7 @@ func (gm *IntBoolMap) BatchSet(m map[int]bool) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntBoolMap) Get(key int) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -97,8 +110,11 @@ func (gm *IntBoolMap) Get(key int) bool {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
@ -110,7 +126,8 @@ func (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntBoolMap) GetOrSet(key int, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -122,7 +139,8 @@ func (gm *IntBoolMap) GetOrSet(key int, value bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -134,7 +152,11 @@ func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
@ -143,10 +165,9 @@ func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
@ -154,7 +175,8 @@ func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -163,7 +185,34 @@ func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntBoolMap) SetIfNotExistFunc(key int, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntBoolMap) SetIfNotExistFuncLock(key int, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntBoolMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -172,7 +221,7 @@ func (gm *IntBoolMap) BatchRemove(keys []int) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntBoolMap) Remove(key int) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -183,7 +232,7 @@ func (gm *IntBoolMap) Remove(key int) bool {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntBoolMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
@ -194,18 +243,8 @@ func (gm *IntBoolMap) Keys() []int {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
//func (gm *IntBoolMap) Values() []bool {
|
||||
// gm.mu.RLock()
|
||||
// vals := make([]bool, 0)
|
||||
// for _, val := range gm.m {
|
||||
// vals = append(vals, val)
|
||||
// }
|
||||
// gm.mu.RUnlock()
|
||||
// return vals
|
||||
//}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntBoolMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -213,7 +252,7 @@ func (gm *IntBoolMap) Contains(key int) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -221,7 +260,8 @@ func (gm *IntBoolMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -229,36 +269,37 @@ func (gm *IntBoolMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntBoolMap) LockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntBoolMap) Merge(m *IntBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntBoolMap) Merge(other *IntBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,9 @@ type IntIntMap struct {
|
||||
m map[int]int
|
||||
}
|
||||
|
||||
// NewIntIntMap returns an empty IntIntMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntIntMap(unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
m : make(map[int]int),
|
||||
@ -23,6 +26,9 @@ func NewIntIntMap(unsafe...bool) *IntIntMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFrom returns an IntIntMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
m : m,
|
||||
@ -30,6 +36,12 @@ func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFromArray returns an IntIntMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap {
|
||||
m := make(map[int]int)
|
||||
l := len(values)
|
||||
@ -46,7 +58,8 @@ func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -57,12 +70,12 @@ func (gm *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntIntMap) Map() map[int]int {
|
||||
m := make(map[int]int)
|
||||
gm.mu.RLock()
|
||||
@ -73,14 +86,14 @@ func (gm *IntIntMap) Map() map[int]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntIntMap) Set(key int, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntIntMap) BatchSet(m map[int]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -89,7 +102,7 @@ func (gm *IntIntMap) BatchSet(m map[int]int) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntIntMap) Get(key int) (int) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -97,8 +110,11 @@ func (gm *IntIntMap) Get(key int) (int) {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
@ -110,7 +126,8 @@ func (gm *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntIntMap) GetOrSet(key int, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -122,7 +139,8 @@ func (gm *IntIntMap) GetOrSet(key int, value int) int {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -134,7 +152,11 @@ func (gm *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
@ -143,7 +165,6 @@ func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
@ -154,7 +175,8 @@ func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -163,7 +185,34 @@ func (gm *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntIntMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -172,7 +221,7 @@ func (gm *IntIntMap) BatchRemove(keys []int) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntIntMap) Remove(key int) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -183,7 +232,7 @@ func (gm *IntIntMap) Remove(key int) int {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntIntMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
@ -194,7 +243,7 @@ func (gm *IntIntMap) Keys() []int {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
@ -205,7 +254,8 @@ func (gm *IntIntMap) Values() []int {
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntIntMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -213,7 +263,7 @@ func (gm *IntIntMap) Contains(key int) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -221,7 +271,8 @@ func (gm *IntIntMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -229,28 +280,28 @@ func (gm *IntIntMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -261,15 +312,16 @@ func (gm *IntIntMap) Flip() {
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntIntMap) Merge(m *IntIntMap) {
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntIntMap) Merge(other *IntIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@ type IntInterfaceMap struct {
|
||||
m map[int]interface{}
|
||||
}
|
||||
|
||||
// NewIntInterfaceMap returns an empty IntInterfaceMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
|
||||
return &IntInterfaceMap{
|
||||
m : make(map[int]interface{}),
|
||||
@ -24,6 +27,9 @@ func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntInterfaceMapFrom returns an IntInterfaceMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
return &IntInterfaceMap{
|
||||
m : m,
|
||||
@ -31,6 +37,12 @@ func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceM
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
m := make(map[int]interface{})
|
||||
l := len(values)
|
||||
@ -47,7 +59,8 @@ func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -58,12 +71,12 @@ func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntInterfaceMap) Clone() *IntInterfaceMap {
|
||||
return NewIntInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntInterfaceMap) Map() map[int]interface{} {
|
||||
m := make(map[int]interface{})
|
||||
gm.mu.RLock()
|
||||
@ -74,14 +87,14 @@ func (gm *IntInterfaceMap) Map() map[int]interface{} {
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntInterfaceMap) Set(key int, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -90,7 +103,7 @@ func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntInterfaceMap) Get(key int) (interface{}) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -98,8 +111,16 @@ func (gm *IntInterfaceMap) Get(key int) (interface{}) {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -109,11 +130,14 @@ func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interf
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
gm.m[key] = value
|
||||
if value != nil {
|
||||
gm.m[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
@ -122,7 +146,8 @@ func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
@ -131,7 +156,11 @@ func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
@ -140,7 +169,8 @@ func (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) inter
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -149,7 +179,31 @@ func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntInterfaceMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntInterfaceMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntInterfaceMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -158,7 +212,7 @@ func (gm *IntInterfaceMap) BatchRemove(keys []int) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntInterfaceMap) Remove(key int) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -169,7 +223,7 @@ func (gm *IntInterfaceMap) Remove(key int) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntInterfaceMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
@ -180,7 +234,7 @@ func (gm *IntInterfaceMap) Keys() []int {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
@ -191,7 +245,8 @@ func (gm *IntInterfaceMap) Values() []interface{} {
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntInterfaceMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -199,7 +254,7 @@ func (gm *IntInterfaceMap) Contains(key int) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -207,7 +262,8 @@ func (gm *IntInterfaceMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -215,28 +271,28 @@ func (gm *IntInterfaceMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -247,15 +303,16 @@ func (gm *IntInterfaceMap) Flip() {
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntInterfaceMap) Merge(m *IntInterfaceMap) {
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntInterfaceMap) Merge(other *IntInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,11 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntStringMap struct {
|
||||
@ -17,71 +16,84 @@ type IntStringMap struct {
|
||||
m map[int]string
|
||||
}
|
||||
|
||||
func NewIntStringMap(unsafe...bool) *IntStringMap {
|
||||
// NewIntStringMap returns an empty IntStringMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntStringMap(unsafe ...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m : make(map[int]string),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
m: make(map[int]string),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntStringMapFrom(m map[int]string, unsafe...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewIntStringMapFrom returns an IntStringMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStringMapFrom(m map[int]string, unsafe ...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntStringMapFromArray(keys []int, values []string, unsafe...bool) *IntStringMap {
|
||||
m := make(map[int]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &IntStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewIntStringMapFromArray returns an IntStringMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntStringMapFromArray(keys []int, values []string, unsafe ...bool) *IntStringMap {
|
||||
m := make(map[int]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &IntStringMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (gm *IntStringMap) Iterator(f func (k int, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntStringMap) Iterator(f func(k int, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntStringMap) Clone() *IntStringMap {
|
||||
return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntStringMap) Map() map[int]string {
|
||||
m := make(map[int]string)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntStringMap) Set(key int, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntStringMap) BatchSet(m map[int]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -90,7 +102,7 @@ func (gm *IntStringMap) BatchSet(m map[int]string) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntStringMap) Get(key int) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -98,179 +110,218 @@ func (gm *IntStringMap) Get(key int) string {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntStringMap) doSetWithLockCheck(key int, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntStringMap) GetOrSet(key int, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntStringMap) SetIfNotExist(key int, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntStringMap) SetIfNotExistFunc(key int, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntStringMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntStringMap) Remove(key int) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntStringMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntStringMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]string)
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntStringMap) LockFunc(f func(m map[int]string)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
gm.m = n
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntStringMap) Merge(m *IntStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntStringMap) Merge(other *IntStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,9 @@ type StringBoolMap struct {
|
||||
m map[string]bool
|
||||
}
|
||||
|
||||
// NewStringBoolMap returns an empty StringBoolMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringBoolMap(unsafe...bool) *StringBoolMap {
|
||||
return &StringBoolMap{
|
||||
m : make(map[string]bool),
|
||||
@ -23,6 +26,9 @@ func NewStringBoolMap(unsafe...bool) *StringBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringBoolMapFrom returns an StringBoolMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap {
|
||||
return &StringBoolMap{
|
||||
m : m,
|
||||
@ -30,6 +36,12 @@ func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *StringBoolMap {
|
||||
m := make(map[string]bool)
|
||||
l := len(values)
|
||||
@ -46,7 +58,8 @@ func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *Str
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringBoolMap) Iterator(f func (k string, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -57,12 +70,12 @@ func (gm *StringBoolMap) Iterator(f func (k string, v bool) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringBoolMap) Clone() *StringBoolMap {
|
||||
return NewStringBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringBoolMap) Map() map[string]bool {
|
||||
m := make(map[string]bool)
|
||||
gm.mu.RLock()
|
||||
@ -73,14 +86,14 @@ func (gm *StringBoolMap) Map() map[string]bool {
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringBoolMap) Set(key string, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringBoolMap) BatchSet(m map[string]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -89,7 +102,7 @@ func (gm *StringBoolMap) BatchSet(m map[string]bool) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringBoolMap) Get(key string) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -97,8 +110,11 @@ func (gm *StringBoolMap) Get(key string) bool {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
@ -110,7 +126,8 @@ func (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringBoolMap) GetOrSet(key string, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -122,7 +139,9 @@ func (gm *StringBoolMap) GetOrSet(key string, value bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -134,7 +153,12 @@ func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
@ -143,7 +167,6 @@ func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
@ -154,7 +177,9 @@ func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -163,7 +188,34 @@ func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringBoolMap) SetIfNotExistFunc(key string, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringBoolMap) SetIfNotExistFuncLock(key string, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringBoolMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -172,7 +224,7 @@ func (gm *StringBoolMap) BatchRemove(keys []string) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringBoolMap) Remove(key string) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -183,7 +235,7 @@ func (gm *StringBoolMap) Remove(key string) bool {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringBoolMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
@ -194,18 +246,8 @@ func (gm *StringBoolMap) Keys() []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
//func (gm *StringBoolMap) Values() []bool {
|
||||
// gm.mu.RLock()
|
||||
// vals := make([]bool, 0)
|
||||
// for _, val := range gm.m {
|
||||
// vals = append(vals, val)
|
||||
// }
|
||||
// gm.mu.RUnlock()
|
||||
// return vals
|
||||
//}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringBoolMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -213,7 +255,7 @@ func (gm *StringBoolMap) Contains(key string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -221,7 +263,8 @@ func (gm *StringBoolMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -229,36 +272,37 @@ func (gm *StringBoolMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringBoolMap) LockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringBoolMap) Merge(m *StringBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringBoolMap) Merge(other *StringBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StringIntMap struct {
|
||||
@ -17,71 +17,84 @@ type StringIntMap struct {
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
func NewStringIntMap(unsafe...bool) *StringIntMap {
|
||||
// NewStringIntMap returns an empty StringIntMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringIntMap(unsafe ...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m : make(map[string]int),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
m: make(map[string]int),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringIntMapFrom(m map[string]int, unsafe...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewStringIntMapFrom returns an StringIntMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringIntMapFrom(m map[string]int, unsafe ...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringIntMapFromArray(keys []string, values []int, unsafe...bool) *StringIntMap {
|
||||
m := make(map[string]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &StringIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewStringIntMapFromArray returns an StringIntMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringIntMapFromArray(keys []string, values []int, unsafe ...bool) *StringIntMap {
|
||||
m := make(map[string]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &StringIntMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (gm *StringIntMap) Iterator(f func (k string, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringIntMap) Iterator(f func(k string, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringIntMap) Clone() *StringIntMap {
|
||||
return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringIntMap) Map() map[string]int {
|
||||
m := make(map[string]int)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
m := make(map[string]int)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringIntMap) Set(key string, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringIntMap) BatchSet(m map[string]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -90,187 +103,228 @@ func (gm *StringIntMap) BatchSet(m map[string]int) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringIntMap) Get(key string) int {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringIntMap) GetOrSet(key string, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringIntMap) SetIfNotExistFunc(key string, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringIntMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringIntMap) Remove(key string) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringIntMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringIntMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]int)
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringIntMap) LockFunc(f func(m map[string]int)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
gm.m = n
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringIntMap) Merge(m *StringIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringIntMap) Merge(other *StringIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,38 +17,51 @@ type StringInterfaceMap struct {
|
||||
m map[string]interface{}
|
||||
}
|
||||
|
||||
func NewStringInterfaceMap(unsafe...bool) *StringInterfaceMap {
|
||||
// NewStringInterfaceMap returns an empty StringInterfaceMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringInterfaceMap(unsafe ...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m : make(map[string]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
m: make(map[string]interface{}),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewStringInterfaceMapFrom returns an StringInterfaceMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe ...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe...bool) *StringInterfaceMap {
|
||||
m := make(map[string]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &StringInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe ...bool) *StringInterfaceMap {
|
||||
m := make(map[string]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &StringInterfaceMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) {
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
@ -58,39 +71,39 @@ func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringInterfaceMap) Clone() *StringInterfaceMap {
|
||||
return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringInterfaceMap) Map() map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
m := make(map[string]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringInterfaceMap) Set(key string, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringInterfaceMap) BatchSet(m map[string]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringInterfaceMap) Get(key string) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -98,67 +111,108 @@ func (gm *StringInterfaceMap) Get(key string) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
gm.m[key] = value
|
||||
return value
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
gm.m[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringInterfaceMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringInterfaceMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringInterfaceMap) Remove(key string) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -169,18 +223,18 @@ func (gm *StringInterfaceMap) Remove(key string) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringInterfaceMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
@ -191,7 +245,8 @@ func (gm *StringInterfaceMap) Values() []interface{} {
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringInterfaceMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -199,7 +254,7 @@ func (gm *StringInterfaceMap) Contains(key string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -207,7 +262,8 @@ func (gm *StringInterfaceMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -215,28 +271,28 @@ func (gm *StringInterfaceMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]interface{})
|
||||
gm.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -247,15 +303,16 @@ func (gm *StringInterfaceMap) Flip() {
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringInterfaceMap) Merge(m *StringInterfaceMap) {
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,9 @@ type StringStringMap struct {
|
||||
m map[string]string
|
||||
}
|
||||
|
||||
// NewStringStringMap returns an empty StringStringMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringStringMap(unsafe...bool) *StringStringMap {
|
||||
return &StringStringMap{
|
||||
m : make(map[string]string),
|
||||
@ -23,6 +26,9 @@ func NewStringStringMap(unsafe...bool) *StringStringMap {
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringStringMapFrom returns an StringStringMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap {
|
||||
return &StringStringMap{
|
||||
m : m,
|
||||
@ -30,6 +36,12 @@ func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringStringMapFromArray returns an StringStringMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool) *StringStringMap {
|
||||
m := make(map[string]string)
|
||||
l := len(values)
|
||||
@ -46,7 +58,8 @@ func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool)
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringStringMap) Iterator(f func (k string, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
@ -57,12 +70,12 @@ func (gm *StringStringMap) Iterator(f func (k string, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringStringMap) Clone() *StringStringMap {
|
||||
return NewStringStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringStringMap) Map() map[string]string {
|
||||
m := make(map[string]string)
|
||||
gm.mu.RLock()
|
||||
@ -73,14 +86,14 @@ func (gm *StringStringMap) Map() map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringStringMap) Set(key string, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringStringMap) BatchSet(m map[string]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
@ -89,7 +102,7 @@ func (gm *StringStringMap) BatchSet(m map[string]string) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringStringMap) Get(key string) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
@ -97,8 +110,11 @@ func (gm *StringStringMap) Get(key string) string {
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringStringMap) doSetWithLockCheck(key string, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
@ -110,7 +126,8 @@ func (gm *StringStringMap) doSetWithLockCheck(key string, value string) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringStringMap) GetOrSet(key string, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -122,7 +139,9 @@ func (gm *StringStringMap) GetOrSet(key string, value string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringStringMap) GetOrSetFunc(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
@ -134,7 +153,12 @@ func (gm *StringStringMap) GetOrSetFunc(key string, f func() string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
@ -143,10 +167,9 @@ func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
@ -154,7 +177,8 @@ func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringStringMap) SetIfNotExist(key string, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
@ -163,7 +187,34 @@ func (gm *StringStringMap) SetIfNotExist(key string, value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringStringMap) SetIfNotExistFunc(key string, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringStringMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringStringMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
@ -172,7 +223,7 @@ func (gm *StringStringMap) BatchRemove(keys []string) {
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringStringMap) Remove(key string) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
@ -183,7 +234,7 @@ func (gm *StringStringMap) Remove(key string) string {
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringStringMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
@ -194,7 +245,7 @@ func (gm *StringStringMap) Keys() []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
@ -205,7 +256,8 @@ func (gm *StringStringMap) Values() []string {
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringStringMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
@ -213,7 +265,7 @@ func (gm *StringStringMap) Contains(key string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
@ -221,7 +273,8 @@ func (gm *StringStringMap) Size() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
@ -229,28 +282,28 @@ func (gm *StringStringMap) IsEmpty() bool {
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringStringMap) LockFunc(f func(m map[string]string)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
@ -261,15 +314,16 @@ func (gm *StringStringMap) Flip() {
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringStringMap) Merge(m *StringStringMap) {
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringStringMap) Merge(other *StringStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
87
g/container/gmap/gmap_z_int_bool_test.go
Normal file
87
g/container/gmap/gmap_z_int_bool_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getBool() bool {
|
||||
return true
|
||||
}
|
||||
func intBoolCallBack(int, bool) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntBoolMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntBoolMap()
|
||||
m.Set(1, true)
|
||||
|
||||
gtest.Assert(m.Get(1), true)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, false), false)
|
||||
gtest.Assert(m.SetIfNotExist(2, false), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, false), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), false)
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
|
||||
gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false})
|
||||
m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false})
|
||||
gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntBoolMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntBoolMap()
|
||||
|
||||
m.GetOrSetFunc(1, getBool)
|
||||
m.GetOrSetFuncLock(2, getBool)
|
||||
gtest.Assert(m.Get(1), true)
|
||||
gtest.Assert(m.Get(2), true)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getBool), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false)
|
||||
}
|
||||
|
||||
func Test_IntBoolMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntBoolMap()
|
||||
|
||||
m.BatchSet(map[int]bool{1: true, 2: false, 3: true})
|
||||
m.Iterator(intBoolCallBack)
|
||||
gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]bool{3: true})
|
||||
}
|
||||
|
||||
func Test_IntBoolMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntBoolMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntBoolMap()
|
||||
m2 := gmap.NewIntBoolMap()
|
||||
m1.Set(1, true)
|
||||
m2.Set(2, false)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false})
|
||||
}
|
||||
91
g/container/gmap/gmap_z_int_int_test.go
Normal file
91
g/container/gmap/gmap_z_int_int_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getInt() int {
|
||||
return 123
|
||||
}
|
||||
func intIntCallBack(int, int) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntIntMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, 2), 2)
|
||||
gtest.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), 2)
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
m3 := gmap.NewIntIntMapFromArray([]int{1, 2}, []int{1, 2})
|
||||
gtest.Assert(m3.Map(), map[int]int{1: 1, 2: 2})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.GetOrSetFunc(1, getInt)
|
||||
m.GetOrSetFuncLock(2, getInt)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Iterator(intIntCallBack)
|
||||
gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]int{3: 3})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntIntMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntIntMap()
|
||||
m2 := gmap.NewIntIntMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, 2)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
|
||||
}
|
||||
91
g/container/gmap/gmap_z_int_interface_test.go
Normal file
91
g/container/gmap/gmap_z_int_interface_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getInterface() interface{} {
|
||||
return 123
|
||||
}
|
||||
func intInterfaceCallBack(int, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntInterfaceMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "2")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
m3 := gmap.NewIntInterfaceMapFromArray([]int{1, 2}, []interface{}{1, "2"})
|
||||
gtest.Assert(m3.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntInterfaceMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
|
||||
m.GetOrSetFunc(1, getInterface)
|
||||
m.GetOrSetFuncLock(2, getInterface)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false)
|
||||
}
|
||||
|
||||
func Test_IntInterfaceMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
|
||||
m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Iterator(intInterfaceCallBack)
|
||||
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
}
|
||||
|
||||
func Test_IntInterfaceMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntInterfaceMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntInterfaceMap()
|
||||
m2 := gmap.NewIntInterfaceMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
}
|
||||
96
g/container/gmap/gmap_z_int_string_test.go
Normal file
96
g/container/gmap/gmap_z_int_string_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getString() string {
|
||||
return "z"
|
||||
}
|
||||
func intStringCallBack(int, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntStringMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStringMap()
|
||||
m.Set(1, "a")
|
||||
|
||||
gtest.Assert(m.Get(1), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "b")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
//反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStringMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
m3 := gmap.NewIntStringMapFromArray([]int{1, 2}, []string{"a", "b"})
|
||||
gtest.Assert(m3.Map(), map[int]string{1: "a", 2: "b"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntStringMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntStringMap()
|
||||
|
||||
m.GetOrSetFunc(1, getString)
|
||||
m.GetOrSetFuncLock(2, getString)
|
||||
gtest.Assert(m.Get(1), "z")
|
||||
gtest.Assert(m.Get(2), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getString), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false)
|
||||
}
|
||||
|
||||
func Test_IntStringMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntStringMap()
|
||||
|
||||
m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
m.Iterator(intStringCallBack)
|
||||
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
}
|
||||
|
||||
func Test_IntStringMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntStringMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntStringMap()
|
||||
m2 := gmap.NewIntStringMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
}
|
||||
91
g/container/gmap/gmap_z_interface_interface_basic_test.go
Normal file
91
g/container/gmap/gmap_z_interface_interface_basic_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
func callBack(k interface{}, v interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_Map_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m3 := gmap.NewFromArray([]interface{}{1, "key1"}, []interface{}{1, "val1"})
|
||||
gtest.Assert(m3.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_Map_Set_Fun(t *testing.T) {
|
||||
m := gmap.New()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_Map_Batch(t *testing.T) {
|
||||
m := gmap.New()
|
||||
m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Iterator(callBack)
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.BatchRemove([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
|
||||
func Test_Map_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
func Test_Map_Basic_Merge(t *testing.T) {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
}
|
||||
71
g/container/gmap/gmap_z_normal_example_test.go
Normal file
71
g/container/gmap/gmap_z_normal_example_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
)
|
||||
|
||||
func Example_Normal_Basic() {
|
||||
m := gmap.New()
|
||||
|
||||
//Add data
|
||||
m.Set("key1", "val1")
|
||||
|
||||
//Print size
|
||||
fmt.Println(m.Size())
|
||||
//output 1
|
||||
|
||||
add_map := make(map[interface{}]interface{})
|
||||
add_map["key2"] = "val2"
|
||||
add_map["key3"] = "val3"
|
||||
add_map[1] = 1
|
||||
|
||||
fmt.Println(m.Values())
|
||||
|
||||
//Batch add data
|
||||
m.BatchSet(add_map)
|
||||
|
||||
//Gets the value of the corresponding key
|
||||
key3_val := m.Get("key3")
|
||||
fmt.Println(key3_val)
|
||||
|
||||
//Get the value by key, or set it with given key-value if not exist.
|
||||
get_or_set_val := m.GetOrSet("key4", "val4")
|
||||
fmt.Println(get_or_set_val)
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
is_set := m.SetIfNotExist("key3", "val3")
|
||||
fmt.Println(is_set)
|
||||
|
||||
//Remove key
|
||||
m.Remove("key2")
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
//Batch remove keys
|
||||
remove_keys := []interface{}{"key1", 1}
|
||||
m.BatchRemove(remove_keys)
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
//Contains checks whether a key exists.
|
||||
is_contain := m.Contains("key3")
|
||||
fmt.Println(is_contain)
|
||||
|
||||
//Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Clear deletes all data of the map,
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
}
|
||||
func Example_Normal_Merge(){
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1","val1")
|
||||
m2.Set("key2","val2")
|
||||
m1.Merge(m2)
|
||||
fmt.Println(m1.Map())
|
||||
}
|
||||
|
||||
85
g/container/gmap/gmap_z_string_bool_test.go
Normal file
85
g/container/gmap/gmap_z_string_bool_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func StringBoolCallBack( string, bool) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringBoolMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringBoolMap()
|
||||
m.Set("a", true)
|
||||
|
||||
gtest.Assert(m.Get("a"), true)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", false), false)
|
||||
gtest.Assert(m.SetIfNotExist("b", false), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", false), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), false)
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
|
||||
gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false})
|
||||
m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false})
|
||||
gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringBoolMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringBoolMap()
|
||||
|
||||
m.GetOrSetFunc("a", getBool)
|
||||
m.GetOrSetFuncLock("b", getBool)
|
||||
gtest.Assert(m.Get("a"), true)
|
||||
gtest.Assert(m.Get("b"), true)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getBool), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false)
|
||||
}
|
||||
|
||||
func Test_StringBoolMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringBoolMap()
|
||||
|
||||
m.BatchSet(map[string]bool{"a": true, "b": false, "c": true})
|
||||
m.Iterator(StringBoolCallBack)
|
||||
gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]bool{"c": true})
|
||||
}
|
||||
|
||||
func Test_StringBoolMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringBoolMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringBoolMap()
|
||||
m2 := gmap.NewStringBoolMap()
|
||||
m1.Set("a", true)
|
||||
m2.Set("b", false)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false})
|
||||
}
|
||||
91
g/container/gmap/gmap_z_string_int_test.go
Normal file
91
g/container/gmap/gmap_z_string_int_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringIntCallBack(string, int) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringIntMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringIntMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", 2), 2)
|
||||
gtest.Assert(m.SetIfNotExist("b", 2), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), 2)
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m_f := gmap.NewStringIntMap()
|
||||
m_f.Set("1", 2)
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[string]int{"2": 1})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2})
|
||||
gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
|
||||
m3 := gmap.NewStringIntMapFromArray([]string{"a", "b"}, []int{1, 2})
|
||||
gtest.Assert(m3.Map(), map[string]int{"a": 1, "b": 2})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringIntMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringIntMap()
|
||||
|
||||
m.GetOrSetFunc("a", getInt)
|
||||
m.GetOrSetFuncLock("b", getInt)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
|
||||
}
|
||||
|
||||
func Test_StringIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringIntMap()
|
||||
|
||||
m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
m.Iterator(stringIntCallBack)
|
||||
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]int{"c": 3})
|
||||
}
|
||||
|
||||
func Test_StringIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringIntMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringIntMap()
|
||||
m2 := gmap.NewStringIntMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", 2)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
|
||||
}
|
||||
89
g/container/gmap/gmap_z_string_interface_test.go
Normal file
89
g/container/gmap/gmap_z_string_interface_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringInterfaceCallBack(string, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringInterfaceMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "2")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
m3 := gmap.NewStringInterfaceMapFromArray([]string{"a", "b"}, []interface{}{1, "2"})
|
||||
gtest.Assert(m3.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringInterfaceMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
|
||||
m.GetOrSetFunc("a", getInterface)
|
||||
m.GetOrSetFuncLock("b", getInterface)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false)
|
||||
}
|
||||
|
||||
func Test_StringInterfaceMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
|
||||
m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.Iterator(stringInterfaceCallBack)
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
|
||||
}
|
||||
|
||||
func Test_StringInterfaceMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringInterfaceMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringInterfaceMap()
|
||||
m2 := gmap.NewStringInterfaceMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
}
|
||||
90
g/container/gmap/gmap_z_string_string_test.go
Normal file
90
g/container/gmap/gmap_z_string_string_test.go
Normal file
@ -0,0 +1,90 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringStringCallBack(string, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringStringMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringStringMap()
|
||||
m.Set("a", "a")
|
||||
|
||||
gtest.Assert(m.Get("a"), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist("b", "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "b")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"})
|
||||
gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
m3 := gmap.NewStringStringMapFromArray([]string{"a", "b"}, []string{"a", "b"})
|
||||
gtest.Assert(m3.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringStringMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringStringMap()
|
||||
|
||||
m.GetOrSetFunc("a", getString)
|
||||
m.GetOrSetFuncLock("b", getString)
|
||||
gtest.Assert(m.Get("a"), "z")
|
||||
gtest.Assert(m.Get("b"), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getString), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false)
|
||||
}
|
||||
|
||||
func Test_StringStringMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringStringMap()
|
||||
|
||||
m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
m.Iterator(stringStringCallBack)
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]string{"c": "c"})
|
||||
}
|
||||
|
||||
func Test_StringStringMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringStringMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringStringMap()
|
||||
m2 := gmap.NewStringStringMap()
|
||||
m1.Set("a", "a")
|
||||
m2.Set("b", "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
}
|
||||
@ -5,8 +5,6 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gpool provides a object-reusable concurrent-safe pool.
|
||||
//
|
||||
// 对象复用池.
|
||||
package gpool
|
||||
|
||||
import (
|
||||
|
||||
@ -4,9 +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 gring provides a concurrent-safe(alternative) ring(circular lists).
|
||||
//
|
||||
// 并发安全环.
|
||||
// Package gring provides a concurrent-safe/unsafe ring(circular lists).
|
||||
package gring
|
||||
|
||||
import (
|
||||
@ -149,8 +147,8 @@ func (r *Ring) Unlink(n int) *Ring {
|
||||
|
||||
// 读锁遍历,往后只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
|
||||
r.mu.RLock(true)
|
||||
defer r.mu.RUnlock(true)
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
if !f(r.ring.Value) {
|
||||
return
|
||||
}
|
||||
@ -163,8 +161,8 @@ func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
|
||||
|
||||
// 读锁遍历,往前只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
|
||||
r.mu.RLock(true)
|
||||
defer r.mu.RUnlock(true)
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
if !f(r.ring.Value) {
|
||||
return
|
||||
}
|
||||
@ -177,8 +175,8 @@ func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
|
||||
|
||||
// 写锁遍历,往后写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
|
||||
r.mu.RLock(true)
|
||||
defer r.mu.RUnlock(true)
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
if !f(r.ring) {
|
||||
return
|
||||
}
|
||||
@ -191,8 +189,8 @@ func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
|
||||
|
||||
// 写锁遍历,往前写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
|
||||
r.mu.RLock(true)
|
||||
defer r.mu.RUnlock(true)
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
if !f(r.ring) {
|
||||
return
|
||||
}
|
||||
|
||||
229
g/container/gring/gring_unit_test.go
Normal file
229
g/container/gring/gring_unit_test.go
Normal file
@ -0,0 +1,229 @@
|
||||
package gring_test
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
type Student struct {
|
||||
position int
|
||||
name string
|
||||
upgrade bool
|
||||
}
|
||||
|
||||
func TestRing_Val(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
//定义cap 为3的ring类型数据
|
||||
r := gring.New(3, true)
|
||||
//分别给3个元素初始化赋值
|
||||
r.Put(&Student{1,"jimmy", true})
|
||||
r.Put(&Student{2,"tom", true})
|
||||
r.Put(&Student{3,"alon", false})
|
||||
|
||||
//元素取值并判断和预设值是否相等
|
||||
gtest.Assert(r.Val().(*Student).name,"jimmy")
|
||||
//从当前位置往后移两个元素
|
||||
r.Move(2)
|
||||
gtest.Assert(r.Val().(*Student).name,"alon")
|
||||
//更新元素值
|
||||
//测试 value == nil
|
||||
r.Set(nil)
|
||||
gtest.Assert(r.Val(),nil)
|
||||
//测试value != nil
|
||||
r.Set(&Student{3, "jack", true})
|
||||
})
|
||||
}
|
||||
func TestRing_CapLen(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
r := gring.New(10)
|
||||
r.Put("goframe")
|
||||
//cap长度 10
|
||||
gtest.Assert(r.Cap(), 10)
|
||||
//已有数据项 1
|
||||
gtest.Assert(r.Len(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_Position(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
r := gring.New(2)
|
||||
r.Put(1)
|
||||
r.Put(2)
|
||||
//往后移动1个元素
|
||||
r.Next()
|
||||
gtest.Assert(r.Val(),2)
|
||||
//往前移动1个元素
|
||||
r.Prev()
|
||||
gtest.Assert(r.Val(),1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_Link(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
r := gring.New(3)
|
||||
r.Put(1)
|
||||
r.Put(2)
|
||||
r.Put(3)
|
||||
s := gring.New(2)
|
||||
s.Put("a")
|
||||
s.Put("b")
|
||||
|
||||
rs := r.Link(s)
|
||||
gtest.Assert(rs.Move(2).Val(), "b")
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_Unlink(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
r := gring.New(5)
|
||||
for i := 0; i< 5; i++ {
|
||||
r.Put(i+1)
|
||||
}
|
||||
// 1 2 3 4
|
||||
// 删除当前位置往后的2个数据,返回被删除的数据
|
||||
// 重新计算s len
|
||||
s := r.Unlink(2) // 2 3
|
||||
gtest.Assert(s.Val(), 2)
|
||||
gtest.Assert(s.Len(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_Slice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
ringLen := 5
|
||||
r := gring.New(ringLen)
|
||||
for i := 0; i< ringLen; i++ {
|
||||
r.Put(i+1)
|
||||
}
|
||||
r.Move(2) // 3
|
||||
array := r.SliceNext() // [3 4 5 1 2]
|
||||
gtest.Assert(array[0], 3)
|
||||
gtest.Assert(len(array), 5)
|
||||
|
||||
//判断array是否等于[3 4 5 1 2]
|
||||
ra := []int{3,4,5,1,2}
|
||||
gtest.Assert(ra, array)
|
||||
|
||||
//第3个元素设为nil
|
||||
r.Set(nil)
|
||||
array2 := r.SliceNext() //[4 5 1 2]
|
||||
//返回当前位置往后不为空的元素数组,长度为4
|
||||
gtest.Assert(array2, g.Slice{4,5,1,2})
|
||||
|
||||
array3 := r.SlicePrev() //[2 1 5 4]
|
||||
gtest.Assert(array3, g.Slice{2,1,5,4})
|
||||
|
||||
s := gring.New(ringLen)
|
||||
for i := 0; i< ringLen; i++ {
|
||||
s.Put(i+1)
|
||||
}
|
||||
array4 := s.SlicePrev() // []
|
||||
gtest.Assert(array4, g.Slice{1,5,4,3,2})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_RLockIterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
ringLen := 5
|
||||
r := gring.New(ringLen)
|
||||
|
||||
//ring不存在有值元素
|
||||
r.RLockIteratorNext(func(v interface{}) bool {
|
||||
gtest.Assert(v, nil)
|
||||
return false
|
||||
})
|
||||
r.RLockIteratorNext(func(v interface{}) bool {
|
||||
gtest.Assert(v, nil)
|
||||
return true
|
||||
})
|
||||
|
||||
r.RLockIteratorPrev(func(v interface{}) bool {
|
||||
gtest.Assert(v, nil)
|
||||
return true
|
||||
})
|
||||
|
||||
for i := 0; i< ringLen; i++ {
|
||||
r.Put(i+1)
|
||||
}
|
||||
|
||||
//回调函数返回true,RLockIteratorNext遍历5次,期望值分别是1、2、3、4、5
|
||||
i := 0
|
||||
r.RLockIteratorNext(func(v interface{}) bool {
|
||||
gtest.Assert(v, i+1)
|
||||
i++;
|
||||
return true
|
||||
})
|
||||
|
||||
//RLockIteratorPrev遍历1次返回 false,退出遍历
|
||||
r.RLockIteratorPrev(func(v interface{}) bool {
|
||||
gtest.Assert(v, 1)
|
||||
return false
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestRing_LockIterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
ringLen := 5
|
||||
r := gring.New(ringLen)
|
||||
|
||||
//不存在有值元素
|
||||
r.LockIteratorNext(func(item *ring.Ring) bool {
|
||||
gtest.Assert(item.Value, nil)
|
||||
return false
|
||||
})
|
||||
r.LockIteratorNext(func(item *ring.Ring) bool {
|
||||
gtest.Assert(item.Value, nil)
|
||||
return false
|
||||
})
|
||||
r.LockIteratorNext(func(item *ring.Ring) bool {
|
||||
gtest.Assert(item.Value, nil)
|
||||
return true
|
||||
})
|
||||
|
||||
r.LockIteratorPrev(func(item *ring.Ring) bool {
|
||||
gtest.Assert(item.Value, nil)
|
||||
return false
|
||||
})
|
||||
r.LockIteratorPrev(func(item *ring.Ring) bool {
|
||||
gtest.Assert(item.Value, nil)
|
||||
return true
|
||||
})
|
||||
|
||||
//ring初始化元素值
|
||||
for i := 0; i< ringLen; i++ {
|
||||
r.Put(i+1)
|
||||
}
|
||||
|
||||
//往后遍历组成数据 [1,2,3,4,5]
|
||||
array1 := g.Slice{1,2,3,4,5}
|
||||
ii := 0
|
||||
r.LockIteratorNext(func(item *ring.Ring) bool {
|
||||
//校验每一次遍历取值是否是期望值
|
||||
gtest.Assert(item.Value, array1[ii])
|
||||
ii++;
|
||||
return true
|
||||
})
|
||||
|
||||
//往后取3个元素组成数组
|
||||
//获得 [1,5,4]
|
||||
i := 0
|
||||
a := g.Slice{1,5,4}
|
||||
r.LockIteratorPrev(func(item *ring.Ring) bool {
|
||||
if i > 2 {
|
||||
return false
|
||||
}
|
||||
gtest.Assert(item.Value, a[i])
|
||||
i++;
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
@ -4,9 +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 gset provides kinds of concurrent-safe(alternative) sets.
|
||||
//
|
||||
// 并发安全集合.
|
||||
// Package gset provides kinds of concurrent-safe/unsafe sets.
|
||||
package gset
|
||||
|
||||
import (
|
||||
@ -139,8 +137,8 @@ func (set *Set) String() string {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
@ -149,8 +147,8 @@ func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ func (set *IntSet) String() string {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
@ -140,8 +140,8 @@ func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ func (set *StringSet) String() string {
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
@ -140,8 +140,8 @@ func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
@ -11,35 +11,39 @@ import (
|
||||
)
|
||||
|
||||
type Bool struct {
|
||||
val int32
|
||||
value int32
|
||||
}
|
||||
|
||||
// NewBool returns a concurrent-safe object for bool type,
|
||||
// with given initial value <value>.
|
||||
func NewBool(value...bool) *Bool {
|
||||
t := &Bool{}
|
||||
if len(value) > 0 {
|
||||
if value[0] {
|
||||
t.val = 1
|
||||
t.value = 1
|
||||
} else {
|
||||
t.val = 0
|
||||
t.value = 0
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for bool type.
|
||||
func (t *Bool) Clone() *Bool {
|
||||
return NewBool(t.Val())
|
||||
}
|
||||
|
||||
// 并发安全设置变量值,返回之前的旧值
|
||||
// Set atomically stores value into t.valueue and returns the previous t.value value.
|
||||
func (t *Bool) Set(value bool) (old bool) {
|
||||
if value {
|
||||
old = atomic.SwapInt32(&t.val, 1) == 1
|
||||
old = atomic.SwapInt32(&t.value, 1) == 1
|
||||
} else {
|
||||
old = atomic.SwapInt32(&t.val, 0) == 1
|
||||
old = atomic.SwapInt32(&t.value, 0) == 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Val atomically loads t.valueue.
|
||||
func (t *Bool) Val() bool {
|
||||
return atomic.LoadInt32(&t.val) > 0
|
||||
return atomic.LoadInt32(&t.value) > 0
|
||||
}
|
||||
|
||||
@ -11,29 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Byte struct {
|
||||
val int32
|
||||
value int32
|
||||
}
|
||||
|
||||
// NewByte returns a concurrent-safe object for byte type,
|
||||
// with given initial value <value>.
|
||||
func NewByte(value...byte) *Byte {
|
||||
if len(value) > 0 {
|
||||
return &Byte{val : int32(value[0])}
|
||||
return &Byte{
|
||||
value : int32(value[0]),
|
||||
}
|
||||
}
|
||||
return &Byte{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for byte type.
|
||||
func (t *Byte) Clone() *Byte {
|
||||
return NewByte(t.Val())
|
||||
}
|
||||
|
||||
// 并发安全设置变量值,返回之前的旧值
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Byte) Set(value byte) (old byte) {
|
||||
return byte(atomic.SwapInt32(&t.val, int32(value)))
|
||||
return byte(atomic.SwapInt32(&t.value, int32(value)))
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Byte) Val() byte {
|
||||
return byte(atomic.LoadInt32(&t.val))
|
||||
return byte(atomic.LoadInt32(&t.value))
|
||||
}
|
||||
|
||||
func (t *Byte) Add(delta int) byte {
|
||||
return byte(atomic.AddInt32(&t.val, int32(delta)))
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Byte) Add(delta int) (new byte) {
|
||||
return byte(atomic.AddInt32(&t.value, int32(delta)))
|
||||
}
|
||||
|
||||
@ -9,29 +9,35 @@ package gtype
|
||||
import "sync/atomic"
|
||||
|
||||
type Bytes struct {
|
||||
val atomic.Value
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
// NewBytes returns a concurrent-safe object for []byte type,
|
||||
// with given initial value <value>.
|
||||
func NewBytes(value...[]byte) *Bytes {
|
||||
t := &Bytes{}
|
||||
if len(value) > 0 {
|
||||
t.val.Store(value[0])
|
||||
t.value.Store(value[0])
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for []byte type.
|
||||
func (t *Bytes) Clone() *Bytes {
|
||||
return NewBytes(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Note: The parameter <value> cannot be nil.
|
||||
func (t *Bytes) Set(value []byte) (old []byte) {
|
||||
old = t.Val()
|
||||
t.val.Store(value)
|
||||
t.value.Store(value)
|
||||
return
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Bytes) Val() []byte {
|
||||
if s := t.val.Load(); s != nil {
|
||||
if s := t.value.Load(); s != nil {
|
||||
return s.([]byte)
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -7,47 +7,53 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Float32 struct {
|
||||
val uint32
|
||||
value uint32
|
||||
}
|
||||
|
||||
// NewFloat32 returns a concurrent-safe object for float32 type,
|
||||
// with given initial value <value>.
|
||||
func NewFloat32(value...float32) *Float32 {
|
||||
if len(value) > 0 {
|
||||
return &Float32{ val : float32ToUint32InBits(value[0]) }
|
||||
return &Float32{
|
||||
value : math.Float32bits(value[0]),
|
||||
}
|
||||
}
|
||||
return &Float32{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for float32 type.
|
||||
func (t *Float32) Clone() *Float32 {
|
||||
return NewFloat32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Float32) Set(value float32) (old float32) {
|
||||
return uint32ToFloat32InBits(atomic.SwapUint32(&t.val, float32ToUint32InBits(value)))
|
||||
return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value)))
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Float32) Val() float32 {
|
||||
return uint32ToFloat32InBits(atomic.LoadUint32(&t.val))
|
||||
return math.Float32frombits(atomic.LoadUint32(&t.value))
|
||||
}
|
||||
|
||||
func (t *Float32) Add(delta float32) float32 {
|
||||
return uint32ToFloat32InBits(atomic.AddUint32(&t.val, float32ToUint32InBits(delta)))
|
||||
}
|
||||
|
||||
// 通过二进制的方式将float32转换为uint32(都是32bits)
|
||||
func float32ToUint32InBits(value float32) uint32 {
|
||||
b := gbinary.Encode(value)
|
||||
i := gbinary.DecodeToUint32(b)
|
||||
return i
|
||||
}
|
||||
|
||||
// 通过二进制的方式将uint32转换为float32(都是32bits)
|
||||
func uint32ToFloat32InBits(value uint32) float32 {
|
||||
b := gbinary.Encode(value)
|
||||
f := gbinary.DecodeToFloat32(b)
|
||||
return f
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Float32) Add(delta float32) (new float32) {
|
||||
for {
|
||||
old := math.Float32frombits(t.value)
|
||||
new = old + delta
|
||||
if atomic.CompareAndSwapUint32(
|
||||
(*uint32)(unsafe.Pointer(&t.value)),
|
||||
math.Float32bits(old),
|
||||
math.Float32bits(new),
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -7,47 +7,53 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Float64 struct {
|
||||
val uint64
|
||||
value uint64
|
||||
}
|
||||
|
||||
// NewFloat64 returns a concurrent-safe object for float64 type,
|
||||
// with given initial value <value>.
|
||||
func NewFloat64(value...float64) *Float64 {
|
||||
if len(value) > 0 {
|
||||
return &Float64{ val : float64ToUint64InBits(value[0]) }
|
||||
return &Float64{
|
||||
value : math.Float64bits(value[0]),
|
||||
}
|
||||
}
|
||||
return &Float64{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for float64 type.
|
||||
func (t *Float64) Clone() *Float64 {
|
||||
return NewFloat64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Float64) Set(value float64) (old float64) {
|
||||
return uint64ToFloat64InBits(atomic.SwapUint64(&t.val, float64ToUint64InBits(value)))
|
||||
return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value)))
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Float64) Val() float64 {
|
||||
return uint64ToFloat64InBits(atomic.LoadUint64(&t.val))
|
||||
return math.Float64frombits(atomic.LoadUint64(&t.value))
|
||||
}
|
||||
|
||||
func (t *Float64) Add(delta float64) float64 {
|
||||
return uint64ToFloat64InBits(atomic.AddUint64(&t.val, float64ToUint64InBits(delta)))
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Float64) Add(delta float64) (new float64) {
|
||||
for {
|
||||
old := math.Float64frombits(t.value)
|
||||
new = old + delta
|
||||
if atomic.CompareAndSwapUint64(
|
||||
(*uint64)(unsafe.Pointer(&t.value)),
|
||||
math.Float64bits(old),
|
||||
math.Float64bits(new),
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 通过二进制的方式将float64转换为uint64(都是64bits)
|
||||
func float64ToUint64InBits(value float64) uint64 {
|
||||
b := gbinary.Encode(value)
|
||||
i := gbinary.DecodeToUint64(b)
|
||||
return i
|
||||
}
|
||||
|
||||
// 通过二进制的方式将uint64转换为float64(都是64bits)
|
||||
func uint64ToFloat64InBits(value uint64) float64 {
|
||||
b := gbinary.Encode(value)
|
||||
f := gbinary.DecodeToFloat64(b)
|
||||
return f
|
||||
}
|
||||
@ -4,13 +4,12 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gtype provides kinds of high performance, concurrent-safe basic variable types.
|
||||
//
|
||||
// 并发安全基本类型.
|
||||
// Package gtype provides kinds of high performance and concurrent-safe basic variable types.
|
||||
package gtype
|
||||
|
||||
type Type = Interface
|
||||
|
||||
// See NewInterface.
|
||||
func New(value ... interface{}) *Type {
|
||||
return NewInterface(value...)
|
||||
}
|
||||
@ -11,30 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Int struct {
|
||||
val int64
|
||||
value int64
|
||||
}
|
||||
|
||||
// NewInt returns a concurrent-safe object for int type,
|
||||
// with given initial value <value>.
|
||||
func NewInt(value...int) *Int {
|
||||
if len(value) > 0 {
|
||||
return &Int{val:int64(value[0])}
|
||||
return &Int{
|
||||
value : int64(value[0]),
|
||||
}
|
||||
}
|
||||
return &Int{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for int type.
|
||||
func (t *Int) Clone() *Int {
|
||||
return NewInt(t.Val())
|
||||
}
|
||||
|
||||
// 并发安全设置变量值,返回之前的旧值
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Int) Set(value int) (old int) {
|
||||
return int(atomic.SwapInt64(&t.val, int64(value)))
|
||||
return int(atomic.SwapInt64(&t.value, int64(value)))
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Int) Val() int {
|
||||
return int(atomic.LoadInt64(&t.val))
|
||||
return int(atomic.LoadInt64(&t.value))
|
||||
}
|
||||
|
||||
// 数值增加delta,并返回**新**的数值
|
||||
func (t *Int) Add(delta int) int {
|
||||
return int(atomic.AddInt64(&t.val, int64(delta)))
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Int) Add(delta int) (new int) {
|
||||
return int(atomic.AddInt64(&t.value, int64(delta)))
|
||||
}
|
||||
@ -11,28 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Int32 struct {
|
||||
val int32
|
||||
value int32
|
||||
}
|
||||
|
||||
// NewInt32 returns a concurrent-safe object for int32 type,
|
||||
// with given initial value <value>.
|
||||
func NewInt32(value...int32) *Int32 {
|
||||
if len(value) > 0 {
|
||||
return &Int32{val: value[0]}
|
||||
return &Int32{
|
||||
value : value[0],
|
||||
}
|
||||
}
|
||||
return &Int32{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for int32 type.
|
||||
func (t *Int32) Clone() *Int32 {
|
||||
return NewInt32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Int32) Set(value int32) (old int32) {
|
||||
return atomic.SwapInt32(&t.val, value)
|
||||
return atomic.SwapInt32(&t.value, value)
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Int32) Val() int32 {
|
||||
return atomic.LoadInt32(&t.val)
|
||||
return atomic.LoadInt32(&t.value)
|
||||
}
|
||||
|
||||
func (t *Int32) Add(delta int32) int32 {
|
||||
return atomic.AddInt32(&t.val, delta)
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Int32) Add(delta int32) (new int32) {
|
||||
return atomic.AddInt32(&t.value, delta)
|
||||
}
|
||||
@ -11,28 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Int64 struct {
|
||||
val int64
|
||||
value int64
|
||||
}
|
||||
|
||||
// NewInt64 returns a concurrent-safe object for int64 type,
|
||||
// with given initial value <value>.
|
||||
func NewInt64(value...int64) *Int64 {
|
||||
if len(value) > 0 {
|
||||
return &Int64{val:value[0]}
|
||||
return &Int64{
|
||||
value : value[0],
|
||||
}
|
||||
}
|
||||
return &Int64{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for int64 type.
|
||||
func (t *Int64) Clone() *Int64 {
|
||||
return NewInt64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Int64) Set(value int64) (old int64) {
|
||||
return atomic.SwapInt64(&t.val, value)
|
||||
return atomic.SwapInt64(&t.value, value)
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Int64) Val() int64 {
|
||||
return atomic.LoadInt64(&t.val)
|
||||
return atomic.LoadInt64(&t.value)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Int64) Add(delta int64) int64 {
|
||||
return atomic.AddInt64(&t.val, delta)
|
||||
return atomic.AddInt64(&t.value, delta)
|
||||
}
|
||||
@ -10,32 +10,34 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// 比较通用的并发安全数据类型
|
||||
type Interface struct {
|
||||
val atomic.Value
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
// NewInterface returns a concurrent-safe object for interface{} type,
|
||||
// with given initial value <value>.
|
||||
func NewInterface(value...interface{}) *Interface {
|
||||
t := &Interface{}
|
||||
if len(value) > 0 && value[0] != nil {
|
||||
t.val.Store(value[0])
|
||||
t.value.Store(value[0])
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for interface{} type.
|
||||
func (t *Interface) Clone() *Interface {
|
||||
return NewInterface(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Note: The parameter <value> cannot be nil.
|
||||
func (t *Interface) Set(value interface{}) (old interface{}) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
old = t.Val()
|
||||
t.val.Store(value)
|
||||
t.value.Store(value)
|
||||
return
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Interface) Val() interface{} {
|
||||
return t.val.Load()
|
||||
return t.value.Load()
|
||||
}
|
||||
@ -11,29 +11,34 @@ import (
|
||||
)
|
||||
|
||||
type String struct {
|
||||
val atomic.Value
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
// NewString returns a concurrent-safe object for string type,
|
||||
// with given initial value <value>.
|
||||
func NewString(value...string) *String {
|
||||
t := &String{}
|
||||
if len(value) > 0 {
|
||||
t.val.Store(value[0])
|
||||
t.value.Store(value[0])
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for string type.
|
||||
func (t *String) Clone() *String {
|
||||
return NewString(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *String) Set(value string) (old string) {
|
||||
old = t.Val()
|
||||
t.val.Store(value)
|
||||
t.value.Store(value)
|
||||
return
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *String) Val() string {
|
||||
s := t.val.Load()
|
||||
s := t.value.Load()
|
||||
if s != nil {
|
||||
return s.(string)
|
||||
}
|
||||
|
||||
@ -11,28 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Uint struct {
|
||||
val uint64
|
||||
value uint64
|
||||
}
|
||||
|
||||
// NewUint returns a concurrent-safe object for uint type,
|
||||
// with given initial value <value>.
|
||||
func NewUint(value...uint) *Uint {
|
||||
if len(value) > 0 {
|
||||
return &Uint{val:uint64(value[0])}
|
||||
return &Uint{
|
||||
value : uint64(value[0]),
|
||||
}
|
||||
}
|
||||
return &Uint{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for uint type.
|
||||
func (t *Uint) Clone() *Uint {
|
||||
return NewUint(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Uint) Set(value uint) (old uint) {
|
||||
return uint(atomic.SwapUint64(&t.val, uint64(value)))
|
||||
return uint(atomic.SwapUint64(&t.value, uint64(value)))
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Uint) Val() uint {
|
||||
return uint(atomic.LoadUint64(&t.val))
|
||||
return uint(atomic.LoadUint64(&t.value))
|
||||
}
|
||||
|
||||
func (t *Uint) Add(delta uint) int {
|
||||
return int(atomic.AddUint64(&t.val, uint64(delta)))
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Uint) Add(delta uint) (new uint) {
|
||||
return uint(atomic.AddUint64(&t.value, uint64(delta)))
|
||||
}
|
||||
@ -11,28 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Uint32 struct {
|
||||
val uint32
|
||||
value uint32
|
||||
}
|
||||
|
||||
// NewUint32 returns a concurrent-safe object for uint32 type,
|
||||
// with given initial value <value>.
|
||||
func NewUint32(value...uint32) *Uint32 {
|
||||
if len(value) > 0 {
|
||||
return &Uint32{val:value[0]}
|
||||
return &Uint32{
|
||||
value : value[0],
|
||||
}
|
||||
}
|
||||
return &Uint32{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for uint32 type.
|
||||
func (t *Uint32) Clone() *Uint32 {
|
||||
return NewUint32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Uint32) Set(value uint32) (old uint32) {
|
||||
return atomic.SwapUint32(&t.val, value)
|
||||
return atomic.SwapUint32(&t.value, value)
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Uint32) Val() uint32 {
|
||||
return atomic.LoadUint32(&t.val)
|
||||
return atomic.LoadUint32(&t.value)
|
||||
}
|
||||
|
||||
func (t *Uint32) Add(delta uint32) uint32 {
|
||||
return atomic.AddUint32(&t.val, delta)
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Uint32) Add(delta uint32) (new uint32) {
|
||||
return atomic.AddUint32(&t.value, delta)
|
||||
}
|
||||
@ -11,28 +11,36 @@ import (
|
||||
)
|
||||
|
||||
type Uint64 struct {
|
||||
val uint64
|
||||
value uint64
|
||||
}
|
||||
|
||||
// NewUint64 returns a concurrent-safe object for uint64 type,
|
||||
// with given initial value <value>.
|
||||
func NewUint64(value...uint64) *Uint64 {
|
||||
if len(value) > 0 {
|
||||
return &Uint64{val:value[0]}
|
||||
return &Uint64{
|
||||
value : value[0],
|
||||
}
|
||||
}
|
||||
return &Uint64{}
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for uint64 type.
|
||||
func (t *Uint64) Clone() *Uint64 {
|
||||
return NewUint64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
func (t *Uint64) Set(value uint64) (old uint64) {
|
||||
return atomic.SwapUint64(&t.val, value)
|
||||
return atomic.SwapUint64(&t.value, value)
|
||||
}
|
||||
|
||||
// Val atomically loads t.value.
|
||||
func (t *Uint64) Val() uint64 {
|
||||
return atomic.LoadUint64(&t.val)
|
||||
return atomic.LoadUint64(&t.value)
|
||||
}
|
||||
|
||||
func (t *Uint64) Add(delta uint64) uint64 {
|
||||
return atomic.AddUint64(&t.val, delta)
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
func (t *Uint64) Add(delta uint64) (new uint64) {
|
||||
return atomic.AddUint64(&t.value, delta)
|
||||
}
|
||||
@ -5,8 +5,6 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gvar provides an universal variable type, like generics.
|
||||
//
|
||||
// 通用动态变量.
|
||||
package gvar
|
||||
|
||||
import (
|
||||
@ -18,7 +16,7 @@ import (
|
||||
|
||||
type Var struct {
|
||||
value interface{} // 变量值
|
||||
safe bool // 当为true时,value为 *gtype.Interface 类型
|
||||
safe bool // 当为true时, value为 *gtype.Interface 类型
|
||||
}
|
||||
|
||||
// 创建一个动态变量,value参数可以为nil
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
// 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 crypto
|
||||
62
g/crypto/gaes/gaes_test.go
Normal file
62
g/crypto/gaes/gaes_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gaes_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gaes"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
var (
|
||||
content = []byte("pibigstar")
|
||||
// iv 长度必须等于blockSize,只能为16
|
||||
iv = []byte("Hello My GoFrame")
|
||||
key_16 = []byte("1234567891234567")
|
||||
key_24 = []byte("123456789123456789123456")
|
||||
key_32 = []byte("12345678912345678912345678912345")
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
_, err := gaes.Encrypt(content, key_16)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_24)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_32)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_16, iv)
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
encrypt, err := gaes.Encrypt(content, key_16)
|
||||
decrypt, err := gaes.Decrypt(encrypt, key_16)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_24)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_24)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_32)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32, iv)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
})
|
||||
}
|
||||
27
g/crypto/gcrc32/gcrc32_test.go
Normal file
27
g/crypto/gcrc32/gcrc32_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gcrc32_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gcrc32"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := "pibigstar"
|
||||
result := 693191136
|
||||
encrypt1 := gcrc32.EncryptString(s)
|
||||
encrypt2 := gcrc32.EncryptBytes([]byte(s))
|
||||
gtest.AssertEQ(int(encrypt1), result)
|
||||
gtest.AssertEQ(int(encrypt2), result)
|
||||
})
|
||||
}
|
||||
@ -1,185 +1,239 @@
|
||||
package gdes_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gdes"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func TestDesECB(t *testing.T){
|
||||
{
|
||||
var (
|
||||
errKey = []byte("1111111111111234123456789")
|
||||
errIv = []byte("123456789")
|
||||
errPadding = 5
|
||||
)
|
||||
|
||||
func TestDesECB(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("11111111")
|
||||
text := []byte("12345678")
|
||||
padding := gdes.NOPADDING
|
||||
result := "858b176da8b12503"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.DesECBEncrypt(key, text, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err, nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.DesECBDecrypt(key, cipherText, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
gtest.AssertEQ(err, nil)
|
||||
gtest.AssertEQ(string(clearText), "12345678")
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
// encrypt err test. when throw exception,the err is not equal nil and the string is nil
|
||||
errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
errEncrypt, err = gdes.DesECBEncrypt(errKey, text, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// err decrypt test.
|
||||
errDecrypt, err := gdes.DesECBDecrypt(errKey, cipherText, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
errDecrypt, err = gdes.DesECBDecrypt(key, cipherText, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("11111111")
|
||||
text := []byte("12345678")
|
||||
padding := gdes.PKCS5PADDING
|
||||
errPadding := 5
|
||||
result := "858b176da8b12503ad6a88b4fa37833d"
|
||||
cipherText, err := gdes.DesECBEncrypt(key, text, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.DesECBDecrypt(key, cipherText, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"12345678")
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
}
|
||||
// err test
|
||||
errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
errDecrypt, err := gdes.DesECBDecrypt(errKey, cipherText, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test3DesECB(t *testing.T){
|
||||
{
|
||||
func Test3DesECB(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("1111111111111234")
|
||||
text := []byte("1234567812345678")
|
||||
padding := gdes.NOPADDING
|
||||
result := "a23ee24b98c26263a23ee24b98c26263"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"1234567812345678")
|
||||
// err test
|
||||
errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
})
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("111111111111123412345678")
|
||||
text := []byte("123456789")
|
||||
padding := gdes.PKCS5PADDING
|
||||
errPadding := 5
|
||||
result := "37989b1effc07a6d00ff89a7d052e79f"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"123456789")
|
||||
// err test, when key is err, but text and padding is right
|
||||
errEncrypt, err := gdes.TripleDesECBEncrypt(errKey, text, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// when padding is err,but key and text is right
|
||||
errEncrypt, err = gdes.TripleDesECBEncrypt(key, text, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// decrypt err test,when key is err
|
||||
errEncrypt, err = gdes.TripleDesECBDecrypt(errKey, text, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDesCBC(t *testing.T){
|
||||
{
|
||||
func TestDesCBC(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("11111111")
|
||||
text := []byte("1234567812345678")
|
||||
padding := gdes.NOPADDING
|
||||
iv := []byte("12345678")
|
||||
cipherText, err := gdes.DesCBCEncrypt(key, text, iv,padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
result := "40826a5800608c87585ca7c9efabee47"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding)
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"1234567812345678")
|
||||
// encrypt err test.
|
||||
errEncrypt, err := gdes.DesCBCEncrypt(errKey, text, iv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// the iv is err
|
||||
errEncrypt, err = gdes.DesCBCEncrypt(key, text, errIv, padding)
|
||||
//gtest.AssertNE(err,nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// the padding is err
|
||||
errEncrypt, err = gdes.DesCBCEncrypt(key, text, iv, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// decrypt err test. the key is err
|
||||
errDecrypt, err := gdes.DesCBCDecrypt(errKey, cipherText, iv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
// the iv is err
|
||||
errDecrypt, err = gdes.DesCBCDecrypt(key, cipherText, errIv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
// the padding is err
|
||||
errDecrypt, err = gdes.DesCBCDecrypt(key, cipherText, iv, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
})
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("11111111")
|
||||
text := []byte("12345678")
|
||||
padding := gdes.PKCS5PADDING
|
||||
iv := []byte("12345678")
|
||||
result := "40826a5800608c87100a25d86ac7c52c"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"12345678")
|
||||
// err test
|
||||
errEncrypt, err := gdes.DesCBCEncrypt(key, text, errIv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test3DesCBC(t *testing.T){
|
||||
{
|
||||
func Test3DesCBC(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("1111111112345678")
|
||||
text := []byte("1234567812345678")
|
||||
padding := gdes.NOPADDING
|
||||
iv := []byte("12345678")
|
||||
cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv,padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
result := "bfde1394e265d5f738d5cab170c77c88"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding)
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"1234567812345678")
|
||||
// encrypt err test
|
||||
errEncrypt, err := gdes.TripleDesCBCEncrypt(errKey, text, iv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// the iv is err
|
||||
errEncrypt, err = gdes.TripleDesCBCEncrypt(key, text, errIv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// the padding is err
|
||||
errEncrypt, err = gdes.TripleDesCBCEncrypt(key, text, iv, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errEncrypt, nil)
|
||||
// decrypt err test
|
||||
errDecrypt, err := gdes.TripleDesCBCDecrypt(errKey, cipherText, iv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
// the iv is err
|
||||
errDecrypt, err = gdes.TripleDesCBCDecrypt(key, cipherText, errIv, padding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
// the padding is err
|
||||
errDecrypt, err = gdes.TripleDesCBCDecrypt(key, cipherText, iv, errPadding)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.AssertEQ(errDecrypt, nil)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
key := []byte("111111111234567812345678")
|
||||
text := []byte("12345678")
|
||||
padding := gdes.PKCS5PADDING
|
||||
iv := []byte("12345678")
|
||||
result := "40826a5800608c87100a25d86ac7c52c"
|
||||
// encrypt test
|
||||
cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
|
||||
// decrypt test
|
||||
clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
gtest.AssertEQ(err,nil)
|
||||
gtest.AssertEQ(string(clearText),"12345678")
|
||||
})
|
||||
|
||||
if bytes.Equal(clearText, text) == false {
|
||||
t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText))
|
||||
}
|
||||
fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
78
g/crypto/gmd5/gmd5_test.go
Normal file
78
g/crypto/gmd5/gmd5_test.go
Normal file
@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gmd5_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gmd5"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
var (
|
||||
s = "pibigstar"
|
||||
// online generated MD5 value
|
||||
result = "d175a1ff66aedde64344785f7f7a3df8"
|
||||
)
|
||||
|
||||
type user struct {
|
||||
name string
|
||||
password string
|
||||
age int
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
encryptString := gmd5.Encrypt(s)
|
||||
gtest.Assert(encryptString, result)
|
||||
|
||||
result := "1427562bb29f88a1161590b76398ab72"
|
||||
encrypt := gmd5.Encrypt(123456)
|
||||
gtest.AssertEQ(encrypt,result)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
user := &user{
|
||||
name: "派大星",
|
||||
password: "123456",
|
||||
age: 23,
|
||||
}
|
||||
result := "70917ebce8bd2f78c736cda63870fb39"
|
||||
encrypt := gmd5.Encrypt(user)
|
||||
gtest.AssertEQ(encrypt,result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncryptString(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
encryptString := gmd5.EncryptString(s)
|
||||
gtest.Assert(encryptString, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncryptFile(t *testing.T) {
|
||||
path := "test.text"
|
||||
errorPath := "err.txt"
|
||||
result := "e6e6e1cd41895beebff16d5452dfce12"
|
||||
gtest.Case(t, func() {
|
||||
file, err := os.Create(path)
|
||||
defer os.Remove(path)
|
||||
defer file.Close()
|
||||
gtest.Assert(err, nil)
|
||||
file.Write([]byte("Hello Go Frame"))
|
||||
encryptFile := gmd5.EncryptFile(path)
|
||||
gtest.AssertEQ(encryptFile, result)
|
||||
// when the file is not exist,encrypt will return empty string
|
||||
errEncrypt := gmd5.EncryptFile(errorPath)
|
||||
gtest.AssertEQ(errEncrypt, "")
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
67
g/crypto/gsha1/gsha1_test.go
Normal file
67
g/crypto/gsha1/gsha1_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gsha1_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gsha1"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
type user struct {
|
||||
name string
|
||||
password string
|
||||
age int
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
user := &user{
|
||||
name: "派大星",
|
||||
password: "123456",
|
||||
age: 23,
|
||||
}
|
||||
result := "97386736e3ee4adee5ca595c78c12129f6032cad"
|
||||
encrypt := gsha1.Encrypt(user)
|
||||
gtest.AssertEQ(encrypt, result)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result := "5b4c1c2a08ca85ddd031ef8627414f4cb2620b41"
|
||||
s := gsha1.Encrypt("pibigstar")
|
||||
gtest.AssertEQ(s, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncryptString(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
result := "5b4c1c2a08ca85ddd031ef8627414f4cb2620b41"
|
||||
s := gsha1.EncryptString("pibigstar")
|
||||
gtest.AssertEQ(s, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncryptFile(t *testing.T) {
|
||||
path := "test.text"
|
||||
errPath := "err.text"
|
||||
gtest.Case(t, func() {
|
||||
result := "8b05d3ba24b8d2374b8f5149d9f3fbada14ea984"
|
||||
file, err := os.Create(path)
|
||||
defer os.Remove(path)
|
||||
defer file.Close()
|
||||
gtest.Assert(err, nil)
|
||||
file.Write([]byte("Hello Go Frame"))
|
||||
encryptFile := gsha1.EncryptFile(path)
|
||||
gtest.AssertEQ(encryptFile, result)
|
||||
// when the file is not exist,encrypt will return empty string
|
||||
errEncrypt := gsha1.EncryptFile(errPath)
|
||||
gtest.AssertEQ(errEncrypt,"")
|
||||
})
|
||||
}
|
||||
@ -1,7 +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.
|
||||
|
||||
package database
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
@ -39,8 +40,8 @@ type DB interface {
|
||||
doPrepare(link dbLink, query string) (*sql.Stmt, error)
|
||||
doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
doDelete(link dbLink, table string, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
|
||||
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
|
||||
|
||||
// 数据库查询
|
||||
GetAll(query string, args ...interface{}) (Result, error)
|
||||
@ -147,30 +148,34 @@ type Map = map[string]interface{}
|
||||
type List = []Map
|
||||
|
||||
const (
|
||||
OPTION_INSERT = 0
|
||||
OPTION_REPLACE = 1
|
||||
OPTION_SAVE = 2
|
||||
OPTION_IGNORE = 3
|
||||
// 默认批量操作的数量值(Batch*操作)
|
||||
gDEFAULT_BATCH_NUM = 10
|
||||
// 默认的连接池连接存活时间(秒)
|
||||
gDEFAULT_CONN_MAX_LIFE_TIME = 30
|
||||
|
||||
OPTION_INSERT = 0
|
||||
OPTION_REPLACE = 1
|
||||
OPTION_SAVE = 2
|
||||
OPTION_IGNORE = 3
|
||||
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
|
||||
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
|
||||
)
|
||||
|
||||
// 使用默认/指定分组配置进行连接,数据库集群配置项:default
|
||||
func New(groupName ...string) (db DB, err error) {
|
||||
group := config.d
|
||||
if len(groupName) > 0 {
|
||||
group = groupName[0]
|
||||
}
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
var (
|
||||
// Instance map.
|
||||
instances = gmap.NewStringInterfaceMap()
|
||||
)
|
||||
|
||||
if len(config.c) < 1 {
|
||||
// New creates ORM DB object with global configurations.
|
||||
// The param <name> specifies the configuration group name,
|
||||
// which is DEFAULT_GROUP_NAME in default.
|
||||
func New(name ...string) (db DB, err error) {
|
||||
group := configs.defaultGroup
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
configs.RLock()
|
||||
defer configs.RUnlock()
|
||||
|
||||
if len(configs.config) < 1 {
|
||||
return nil, errors.New("empty database configuration")
|
||||
}
|
||||
if _, ok := config.c[group]; ok {
|
||||
if _, ok := configs.config[group]; ok {
|
||||
if node, err := getConfigNodeByGroup(group, true); err == nil {
|
||||
base := &dbBase {
|
||||
group : group,
|
||||
@ -204,9 +209,27 @@ func New(groupName ...string) (db DB, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Instance returns an instance for DB operations.
|
||||
// The param <name> specifies the configuration group name,
|
||||
// which is DEFAULT_GROUP_NAME in default.
|
||||
func Instance(name ...string) (db DB, err error) {
|
||||
group := configs.defaultGroup
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
v := instances.GetOrSetFuncLock(group, func() interface{} {
|
||||
db, err = New(group)
|
||||
return db
|
||||
})
|
||||
if v != nil {
|
||||
return v.(DB), nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
|
||||
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
if list, ok := config.c[group]; ok {
|
||||
if list, ok := configs.config[group]; ok {
|
||||
// 将master, slave集群列表拆分出来
|
||||
masterList := make(ConfigGroup, 0)
|
||||
slaveList := make(ConfigGroup, 0)
|
||||
@ -319,17 +342,17 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 切换操作的数据库(注意该切换是全局的)
|
||||
// 切换当前数据库对象操作的数据库。
|
||||
func (bs *dbBase) SetSchema(schema string) {
|
||||
bs.schema.Set(schema)
|
||||
}
|
||||
|
||||
// 创建底层数据库master链接对象
|
||||
// 创建底层数据库master链接对象。
|
||||
func (bs *dbBase) Master() (*sql.DB, error) {
|
||||
return bs.getSqlDb(true)
|
||||
}
|
||||
|
||||
// 创建底层数据库slave链接对象
|
||||
// 创建底层数据库slave链接对象。
|
||||
func (bs *dbBase) Slave() (*sql.DB, error) {
|
||||
return bs.getSqlDb(false)
|
||||
}
|
||||
|
||||
@ -312,7 +312,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
|
||||
return bs.db.doBatchInsert(link, table, data, option, batch...)
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
dataMap = Map(gconv.Map(data))
|
||||
dataMap = gconv.Map(data)
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
@ -320,7 +320,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL + k + charR)
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
params = append(params, convertParam(v))
|
||||
}
|
||||
operation := getInsertOperationByOption(option)
|
||||
updateStr := ""
|
||||
@ -369,6 +369,10 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
var params []interface{}
|
||||
listMap := (List)(nil)
|
||||
switch v := list.(type) {
|
||||
case Result:
|
||||
listMap = v.ToList()
|
||||
case Record:
|
||||
listMap = List{v.ToMap()}
|
||||
case List:
|
||||
listMap = v
|
||||
case Map:
|
||||
@ -436,7 +440,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
}
|
||||
for i := 0; i < len(listMap); i++ {
|
||||
for _, k := range keys {
|
||||
params = append(params, listMap[i][k])
|
||||
params = append(params, convertParam(listMap[i][k]))
|
||||
}
|
||||
values = append(values, valueHolderStr)
|
||||
if len(values) == batchNum {
|
||||
@ -479,17 +483,13 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型。
|
||||
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
link, err := bs.db.Master()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bs.db.doUpdate(link, table, data, condition, args ...)
|
||||
newWhere, newArgs := formatCondition(condition, args)
|
||||
return bs.db.doUpdate(nil, table, data, newWhere, newArgs ...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型类型。
|
||||
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
params := ([]interface{})(nil)
|
||||
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
updates := ""
|
||||
charL, charR := bs.db.getChars()
|
||||
// 使用反射进行类型判断
|
||||
@ -499,43 +499,51 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
params := []interface{}(nil)
|
||||
switch kind {
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
var fields []string
|
||||
for k, v := range gconv.Map(data) {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR))
|
||||
params = append(params, gconv.String(v))
|
||||
params = append(params, convertParam(v))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
for _, v := range args {
|
||||
params = append(params, gconv.String(v))
|
||||
if len(params) > 0 {
|
||||
args = append(params, args...)
|
||||
}
|
||||
// 如果没有传递link,那么使用默认的写库对象
|
||||
if link == nil {
|
||||
if link, err = bs.db.Master(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newWhere, newArgs := formatCondition(condition, params)
|
||||
return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s WHERE %s", table, updates, newWhere), newArgs...)
|
||||
if len(condition) == 0 {
|
||||
return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s", table, updates), args...)
|
||||
}
|
||||
return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s WHERE %s", table, updates, condition), args...)
|
||||
}
|
||||
|
||||
// CURD操作:删除数据
|
||||
func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
link, err := bs.db.Master()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bs.db.doDelete(link, table, condition, args ...)
|
||||
newWhere, newArgs := formatCondition(condition, args)
|
||||
return bs.db.doDelete(nil, table, newWhere, newArgs ...)
|
||||
}
|
||||
|
||||
// CURD操作:删除数据
|
||||
func (bs *dbBase) doDelete(link dbLink, table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
newWhere, newArgs := formatCondition(condition, args)
|
||||
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s WHERE %s", table, newWhere), newArgs...)
|
||||
func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
if link == nil {
|
||||
if link, err = bs.db.Master(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(condition) == 0 {
|
||||
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s", table), args...)
|
||||
}
|
||||
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s WHERE %s", table, condition), args...)
|
||||
}
|
||||
|
||||
// 获得缓存对象
|
||||
|
||||
@ -17,14 +17,7 @@ const (
|
||||
DEFAULT_GROUP_NAME = "default" // 默认配置名称
|
||||
)
|
||||
|
||||
// 数据库配置包内对象
|
||||
var config struct {
|
||||
sync.RWMutex
|
||||
c Config // 数据库配置
|
||||
d string // 默认数据库分组名称
|
||||
}
|
||||
|
||||
// 数据库配置
|
||||
// 数据库分组配置
|
||||
type Config map[string]ConfigGroup
|
||||
|
||||
// 数据库集群配置
|
||||
@ -41,12 +34,19 @@ type ConfigNode struct {
|
||||
Role string // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
|
||||
Charset string // (可选,默认为 utf8)编码,默认为 utf8
|
||||
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
Linkinfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
MaxIdleConnCount int // (可选)连接池最大限制的连接数
|
||||
MaxOpenConnCount int // (可选)连接池最大打开的连接数
|
||||
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
|
||||
}
|
||||
|
||||
// 数据库配置包内对象
|
||||
var configs struct {
|
||||
sync.RWMutex // 并发安全互斥锁
|
||||
config Config // 数据库分组配置
|
||||
defaultGroup string // 默认数据库分组名称
|
||||
}
|
||||
|
||||
// 数据库集群配置示例,支持主从处理,多数据库集群支持
|
||||
/*
|
||||
var DatabaseConfiguration = Config {
|
||||
@ -80,29 +80,32 @@ var DatabaseConfiguration = Config {
|
||||
|
||||
// 包初始化
|
||||
func init() {
|
||||
config.c = make(Config)
|
||||
config.d = DEFAULT_GROUP_NAME
|
||||
configs.config = make(Config)
|
||||
configs.defaultGroup = DEFAULT_GROUP_NAME
|
||||
}
|
||||
|
||||
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
|
||||
func SetConfig (c Config) {
|
||||
config.Lock()
|
||||
defer config.Unlock()
|
||||
config.c = c
|
||||
func SetConfig (config Config) {
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.config = config
|
||||
}
|
||||
|
||||
// 添加数据库服务器集群配置
|
||||
func AddConfigGroup (group string, nodes ConfigGroup) {
|
||||
config.Lock()
|
||||
config.c[group] = nodes
|
||||
config.Unlock()
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.config[group] = nodes
|
||||
}
|
||||
|
||||
// 添加一台数据库服务器配置
|
||||
func AddConfigNode (group string, node ConfigNode) {
|
||||
config.Lock()
|
||||
config.c[group] = append(config.c[group], node)
|
||||
config.Unlock()
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.config[group] = append(configs.config[group], node)
|
||||
}
|
||||
|
||||
// 添加默认链接的一台数据库服务器配置
|
||||
@ -117,16 +120,25 @@ func AddDefaultConfigGroup (nodes ConfigGroup) {
|
||||
|
||||
// 添加一台数据库服务器配置
|
||||
func GetConfig (group string) ConfigGroup {
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
return config.c[group]
|
||||
configs.RLock()
|
||||
defer configs.RUnlock()
|
||||
return configs.config[group]
|
||||
}
|
||||
|
||||
// 设置默认链接的数据库链接配置项(默认是 default)
|
||||
func SetDefaultGroup (groupName string) {
|
||||
config.Lock()
|
||||
config.d = groupName
|
||||
config.Unlock()
|
||||
func SetDefaultGroup (name string) {
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.defaultGroup = name
|
||||
}
|
||||
|
||||
// 获取默认链接的数据库链接配置项(默认是 default)
|
||||
func GetDefaultGroup() string {
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
return configs.defaultGroup
|
||||
}
|
||||
|
||||
// 设置数据库连接池中空闲链接的大小
|
||||
@ -147,8 +159,8 @@ func (bs *dbBase) SetConnMaxLifetime(n int) {
|
||||
|
||||
// 节点配置转换为字符串
|
||||
func (node *ConfigNode) String() string {
|
||||
if node.Linkinfo != "" {
|
||||
return node.Linkinfo
|
||||
if node.LinkInfo != "" {
|
||||
return node.LinkInfo
|
||||
}
|
||||
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
|
||||
node.Name, node.Type, node.Role, node.Charset,
|
||||
|
||||
@ -78,8 +78,9 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
// 没有任何条件查询参数,直接返回
|
||||
if buffer.Len() == 0 {
|
||||
buffer.WriteString("1=1")
|
||||
return "", args
|
||||
}
|
||||
newWhere = buffer.String()
|
||||
tmpArgs = append(tmpArgs, args...)
|
||||
@ -125,6 +126,22 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne
|
||||
return
|
||||
}
|
||||
|
||||
// 将预处理参数转换为底层数据库引擎支持的格式。
|
||||
// 主要是判断参数是否为复杂数据类型,如果是,那么转换为基础类型。
|
||||
func convertParam(value interface{}) interface{} {
|
||||
rv := reflect.ValueOf(value)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
return gconv.String(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 打印SQL对象(仅在debug=true时有效)
|
||||
func printSql(v *Sql) {
|
||||
s := fmt.Sprintf("%s, %v, %s, %s, %d ms, %s", v.Sql, v.Args,
|
||||
|
||||
@ -247,13 +247,17 @@ func (md *Model) Data(data ...interface{}) *Model {
|
||||
}
|
||||
model.data = m
|
||||
} else {
|
||||
switch data[0].(type) {
|
||||
switch params := data[0].(type) {
|
||||
case Result:
|
||||
model.data = params.ToList()
|
||||
case Record:
|
||||
model.data = params.ToMap()
|
||||
case List:
|
||||
model.data = data[0]
|
||||
model.data = params
|
||||
case Map:
|
||||
model.data = data[0]
|
||||
model.data = params
|
||||
default:
|
||||
rv := reflect.ValueOf(data[0])
|
||||
rv := reflect.ValueOf(params)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
@ -420,9 +424,9 @@ func (md *Model) Update() (result sql.Result, err error) {
|
||||
}
|
||||
}
|
||||
if md.tx == nil {
|
||||
return md.db.Update(md.tables, md.data, md.where, md.whereArgs ...)
|
||||
return md.db.doUpdate(nil, md.tables, md.data, md.where, md.whereArgs ...)
|
||||
} else {
|
||||
return md.tx.Update(md.tables, md.data, md.where, md.whereArgs ...)
|
||||
return md.tx.doUpdate(md.tables, md.data, md.where, md.whereArgs ...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,9 +438,9 @@ func (md *Model) Delete() (result sql.Result, err error) {
|
||||
}
|
||||
}()
|
||||
if md.tx == nil {
|
||||
return md.db.Delete(md.tables, md.where, md.whereArgs...)
|
||||
return md.db.doDelete(nil, md.tables, md.where, md.whereArgs...)
|
||||
} else {
|
||||
return md.tx.Delete(md.tables, md.where, md.whereArgs...)
|
||||
return md.tx.doDelete(md.tables, md.where, md.whereArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -30,8 +30,8 @@ type dbMssql struct {
|
||||
// 创建SQL操作对象
|
||||
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
source := ""
|
||||
if config.Linkinfo != "" {
|
||||
source = config.Linkinfo
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name)
|
||||
|
||||
@ -20,8 +20,8 @@ type dbMysql struct {
|
||||
// 创建SQL操作对象,内部采用了lazy link处理
|
||||
func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.Linkinfo != "" {
|
||||
source = config.Linkinfo
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
|
||||
|
||||
@ -30,8 +30,8 @@ type dbOracle struct {
|
||||
// 创建SQL操作对象
|
||||
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.Linkinfo != "" {
|
||||
source = config.Linkinfo
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name)
|
||||
}
|
||||
|
||||
@ -26,8 +26,8 @@ type dbPgsql struct {
|
||||
// 创建SQL操作对象,内部采用了lazy link处理
|
||||
func (db *dbPgsql) Open (config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.Linkinfo != "" {
|
||||
source = config.Linkinfo
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
|
||||
}
|
||||
|
||||
@ -24,8 +24,8 @@ type dbSqlite struct {
|
||||
|
||||
func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.Linkinfo != "" {
|
||||
source = config.Linkinfo
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = config.Name
|
||||
}
|
||||
|
||||
@ -162,14 +162,28 @@ func (tx *TX) BatchSave(table string, list interface{}, batch...int) (sql.Result
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理
|
||||
// CURD操作:数据更新,统一采用sql预处理,
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理.
|
||||
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
newWhere, newArgs := formatCondition(condition, args)
|
||||
return tx.doUpdate(table, data, newWhere, newArgs ...)
|
||||
}
|
||||
|
||||
// 与Update方法的区别是不处理条件参数
|
||||
func (tx *TX) doUpdate(table string, data interface{}, condition string, args ...interface{}) (sql.Result, error) {
|
||||
return tx.db.doUpdate(tx.tx, table, data, condition, args ...)
|
||||
}
|
||||
|
||||
// CURD操作:删除数据
|
||||
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
newWhere, newArgs := formatCondition(condition, args)
|
||||
return tx.doDelete(table, newWhere, newArgs ...)
|
||||
}
|
||||
|
||||
// 与Delete方法的区别是不处理条件参数
|
||||
func (tx *TX) doDelete(table string, condition string, args ...interface{}) (sql.Result, error) {
|
||||
return tx.db.doDelete(tx.tx, table, condition, args ...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
28
g/database/gdb/gdb_unit_basic_test.go
Normal file
28
g/database/gdb/gdb_unit_basic_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
// 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 gdb_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Instance(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
_, err := gdb.Instance("none")
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
db, err := gdb.Instance()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
@ -1,11 +1,26 @@
|
||||
// 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 gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// 初始化表数据量
|
||||
INIT_DATA_SIZE = 10
|
||||
)
|
||||
|
||||
var (
|
||||
// 数据库对象/接口
|
||||
db gdb.DB
|
||||
@ -26,10 +41,12 @@ func init() {
|
||||
Priority: 1,
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
// 本地测试hack
|
||||
if hostname == "ijohn" {
|
||||
node.Pass = "12345678"
|
||||
}
|
||||
gdb.AddDefaultConfigNode(node)
|
||||
gdb.AddConfigNode("test", node)
|
||||
gdb.AddConfigNode(gdb.DEFAULT_GROUP_NAME, node)
|
||||
if r, err := gdb.New(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
@ -39,12 +56,25 @@ func init() {
|
||||
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS `test` CHARACTER SET UTF8"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
// 选择操作数据库
|
||||
db.SetSchema("test")
|
||||
if _, err := db.Exec("DROP TABLE IF EXISTS `user`"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
// 创建默认用户表
|
||||
createTable("user")
|
||||
}
|
||||
|
||||
// 创建指定名称的user测试表,当table为空时,创建随机的表名。
|
||||
// 创建的测试表默认没有任何数据。
|
||||
// 执行完成后返回该表名。
|
||||
// TODO 支持更多数据库
|
||||
func createTable(table...string) (name string) {
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = fmt.Sprintf(`user_%d`, gtime.Nanosecond())
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE user (
|
||||
dropTable(name)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||
passport varchar(45) NOT NULL COMMENT '账号',
|
||||
password char(32) NOT NULL COMMENT '密码',
|
||||
@ -52,7 +82,38 @@ func init() {
|
||||
create_time timestamp NOT NULL COMMENT '创建时间/注册时间',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`); err != nil {
|
||||
`, name)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 删除指定表.
|
||||
func dropTable(table string) {
|
||||
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// See createTable.
|
||||
// 创建测试表,并初始化默认数据。
|
||||
func createInitTable(table...string) (name string) {
|
||||
name = createTable(table...)
|
||||
array := garray.New(true)
|
||||
for i := 1; i <= INIT_DATA_SIZE; i++ {
|
||||
array.Append(g.Map{
|
||||
"id" : i,
|
||||
"passport" : fmt.Sprintf(`t%d`, i),
|
||||
"password" : fmt.Sprintf(`p%d`, i),
|
||||
"nickname" : fmt.Sprintf(`T%d`, i),
|
||||
"create_time" : gtime.Now().String(),
|
||||
})
|
||||
}
|
||||
result, err := db.Table(name).Data(array.Slice()).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE)
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
// 方法操作
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
@ -9,6 +13,15 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDbBase_Ping(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Query(t *testing.T) {
|
||||
if _, err := db.Query("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
@ -144,57 +157,102 @@ func TestDbBase_Insert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDbBase_BatchInsert(t *testing.T) {
|
||||
if r, err := db.BatchInsert("user", g.List {
|
||||
{
|
||||
"id" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
if r, err := db.BatchInsert("user", g.List {
|
||||
{
|
||||
"id" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
result, err := db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
// []interface{}
|
||||
if r, err := db.BatchInsert("user", []interface{} {
|
||||
map[interface{}]interface{} {
|
||||
"id" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{} {
|
||||
"id" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
result, err := db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
// []interface{}
|
||||
if r, err := db.BatchInsert("user", []interface{} {
|
||||
map[interface{}]interface{} {
|
||||
"id" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{} {
|
||||
"id" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
})
|
||||
// batch insert map
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.BatchInsert(table, g.Map{
|
||||
"id" : 1,
|
||||
"passport" : "t1",
|
||||
"password" : "p1",
|
||||
"nickname" : "T1",
|
||||
"create_time" : gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
// batch insert struct
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `gconv:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
NickName string `gconv:"nickname"`
|
||||
CreateTime *gtime.Time `gconv:"create_time"`
|
||||
}
|
||||
user := &User{
|
||||
Id : 1,
|
||||
Passport : "t1",
|
||||
Password : "p1",
|
||||
NickName : "T1",
|
||||
CreateTime : gtime.Now(),
|
||||
}
|
||||
result, err := db.BatchInsert(table, user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Save(t *testing.T) {
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
// 链式操作
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
@ -90,29 +94,66 @@ func TestModel_Insert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModel_Batch(t *testing.T) {
|
||||
result, err := db.Table("user").Filter().Data(g.List{
|
||||
{
|
||||
"id" : 2,
|
||||
"uid" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id" : 3,
|
||||
"uid" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}).Batch(1).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
// batch insert
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Filter().Data(g.List{
|
||||
{
|
||||
"id" : 2,
|
||||
"uid" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id" : 3,
|
||||
"uid" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}).Batch(1).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
// batch save
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Save()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
|
||||
// batch replace
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Replace()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Replace(t *testing.T) {
|
||||
@ -146,12 +187,23 @@ func TestModel_Save(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModel_Update(t *testing.T) {
|
||||
result, err := db.Table("user").Data("passport", "t22").Where("passport=?", "t2").Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Data("passport", "t22").Where("passport=?", "t2").Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Data("passport", "t2").Where("passport='t22'").Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Clone(t *testing.T) {
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
// 事务操作
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
|
||||
@ -4,37 +4,45 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gredis provides client for redis server.
|
||||
// Package gredis provides convenient client for redis server.
|
||||
//
|
||||
// Redis客户端.
|
||||
// Redis中文手册文档请参考:http://redisdoc.com/ , Redis官方命令请参考:https://redis.io/commands
|
||||
// Redis中文手册请参考:http://redisdoc.com/
|
||||
// Redis官方命令请参考:https://redis.io/commands
|
||||
package gredis
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_POOL_MAX_IDLE = 1
|
||||
gDEFAULT_POOL_MAX_ACTIVE = 10
|
||||
gDEFAULT_POOL_IDLE_TIMEOUT = 180 * time.Second
|
||||
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
|
||||
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
|
||||
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
|
||||
)
|
||||
|
||||
// Redis客户端
|
||||
// Redis客户端(管理连接池)
|
||||
type Redis struct {
|
||||
pool *redis.Pool
|
||||
pool *redis.Pool // 底层连接池
|
||||
group string // 配置分组
|
||||
config Config // 配置对象
|
||||
}
|
||||
|
||||
// Redis连接对象(连接池中的单个连接)
|
||||
type Conn redis.Conn
|
||||
|
||||
// Redis服务端但节点连接配置信息
|
||||
type Config struct {
|
||||
Host string // IP/域名
|
||||
Port int // 端口
|
||||
Db int // db
|
||||
Pass string // 密码
|
||||
Host string // 地址
|
||||
Port int // 端口
|
||||
Db int // 数据库
|
||||
Pass string // 授权密码
|
||||
MaxIdle int // 最大允许空闲存在的连接数(默认为0表示不存在闲置连接)
|
||||
MaxActive int // 最大连接数量限制(默认为0表示不限制)
|
||||
IdleTimeout time.Duration // 连接最大空闲时间(默认为60秒,不允许设置为0)
|
||||
MaxConnLifetime time.Duration // 连接最长存活时间(默认为60秒,不允许设置为0)
|
||||
}
|
||||
|
||||
// Redis链接池统计信息
|
||||
@ -42,91 +50,160 @@ type PoolStats struct {
|
||||
redis.PoolStats
|
||||
}
|
||||
|
||||
// 连接池map
|
||||
var pools = gmap.NewStringInterfaceMap()
|
||||
var (
|
||||
// 单例对象Map
|
||||
instances = gmap.NewStringInterfaceMap()
|
||||
// 连接池Map
|
||||
pools = gmap.NewStringInterfaceMap()
|
||||
)
|
||||
|
||||
// 创建redis操作对象.
|
||||
// New creates a redis client object with given configuration.
|
||||
// Redis client maintains a connection pool automatically.
|
||||
//
|
||||
// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。
|
||||
func New(config Config) *Redis {
|
||||
r := &Redis{}
|
||||
poolKey := fmt.Sprintf("%s:%d,%d", config.Host, config.Port, config.Db)
|
||||
if v := pools.Get(poolKey); v == nil {
|
||||
pool := &redis.Pool {
|
||||
MaxIdle : gDEFAULT_POOL_MAX_IDLE,
|
||||
MaxActive : gDEFAULT_POOL_MAX_ACTIVE,
|
||||
IdleTimeout : gDEFAULT_POOL_IDLE_TIMEOUT,
|
||||
MaxConnLifetime : gDEFAULT_POOL_MAX_LIFE_TIME,
|
||||
Dial : func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(config.Pass) > 0 {
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
if config.IdleTimeout == 0 {
|
||||
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
|
||||
}
|
||||
if config.MaxConnLifetime == 0 {
|
||||
config.MaxConnLifetime = gDEFAULT_POOL_MAX_LIFE_TIME
|
||||
}
|
||||
return &Redis{
|
||||
config : config,
|
||||
pool : pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} {
|
||||
return &redis.Pool {
|
||||
IdleTimeout : config.IdleTimeout,
|
||||
MaxConnLifetime : config.MaxConnLifetime,
|
||||
Dial : func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := c.Do("SELECT", config.Db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
// 用来测试连接是否可用
|
||||
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
||||
_, err := c.Do("PING")
|
||||
return err
|
||||
},
|
||||
}
|
||||
pools.Set(poolKey, pool)
|
||||
r.pool = pool
|
||||
} else {
|
||||
r.pool = v.(*redis.Pool)
|
||||
// 密码设置
|
||||
if len(config.Pass) > 0 {
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// 数据库设置
|
||||
if _, err := c.Do("SELECT", config.Db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
// 在被应用从连接池中获取出来之后,用以测试连接是否可用,
|
||||
// 如果返回error那么关闭该连接对象重新创建新的连接。
|
||||
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
||||
_, err := c.Do("PING")
|
||||
return err
|
||||
},
|
||||
}
|
||||
}).(*redis.Pool),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// 关闭redis管理对象,将会关闭底层的
|
||||
// Instance returns an instance of redis client with specified group.
|
||||
// The <group> param is unnecessary, if <group> is not passed,
|
||||
// return redis instance with default group.
|
||||
//
|
||||
// 获取指定分组名称的Redis单例对象,底层根据配置信息公用的连接池(连接池单例)。
|
||||
func Instance(name ...string) *Redis {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
v := instances.GetOrSetFuncLock(group, func() interface{} {
|
||||
if config, ok := GetConfig(group); ok {
|
||||
r := New(config)
|
||||
r.group = group
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if v != nil {
|
||||
return v.(*Redis)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the redis connection pool,
|
||||
// it will release all connections reserved by this pool.
|
||||
// It always not necessary to call Close manually.
|
||||
//
|
||||
// 关闭redis管理对象,将会关闭底层的连接池。
|
||||
// 往往没必要手动调用,跟随进程销毁即可。
|
||||
func (r *Redis) Close() error {
|
||||
if r.group != "" {
|
||||
// 如果是单例对象,那么需要从单例对象Map中删除
|
||||
instances.Remove(r.group)
|
||||
}
|
||||
pools.Remove(fmt.Sprintf("%v", r.config))
|
||||
return r.pool.Close()
|
||||
}
|
||||
|
||||
// 获得一个原生的redis连接对象,用于自定义连接操作,
|
||||
// 但是需要注意的是如果不再使用该连接对象时,需要手动Close连接,否则会造成连接数超限。
|
||||
func (r *Redis) GetConn() redis.Conn {
|
||||
return r.pool.Get()
|
||||
// See GetConn.
|
||||
func (r *Redis) Conn() Conn {
|
||||
return r.GetConn()
|
||||
}
|
||||
|
||||
// GetConn returns a raw connection object,
|
||||
// which expose more methods communication with server.
|
||||
// **You should call Close function manually if you do not use this connection any further.**
|
||||
//
|
||||
// 获得一个原生的redis连接对象,用于自定义连接操作,
|
||||
// 但是需要注意的是如果不再使用该连接对象时,需要手动Close连接,否则会造成连接数超限。
|
||||
func (r *Redis) GetConn() Conn {
|
||||
return r.pool.Get().(Conn)
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxIdle attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxIdle
|
||||
func (r *Redis) SetMaxIdle(value int) {
|
||||
r.pool.MaxIdle = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxActive attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxActive
|
||||
func (r *Redis) SetMaxActive(value int) {
|
||||
r.pool.MaxActive = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the IdleTimeout attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - IdleTimeout
|
||||
func (r *Redis) SetIdleTimeout(value time.Duration) {
|
||||
r.pool.IdleTimeout = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxConnLifetime attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxConnLifetime
|
||||
func (r *Redis) SetMaxConnLifetime(value time.Duration) {
|
||||
r.pool.MaxConnLifetime = value
|
||||
}
|
||||
|
||||
// 获取当前连接池统计信息
|
||||
// Stats returns pool's statistics.
|
||||
//
|
||||
// 获取当前连接池统计信息。
|
||||
func (r *Redis) Stats() *PoolStats {
|
||||
return &PoolStats{r.pool.Stats()}
|
||||
}
|
||||
|
||||
// 执行同步命令 - Do
|
||||
// Do sends a command to the server and returns the received reply.
|
||||
// Do automatically get a connection from pool, and close it when reply received.
|
||||
//
|
||||
// 执行同步命令,自动从连接池中获取连接,使用完毕后关闭连接(丢回连接池),开发者不用自行Close.
|
||||
func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
|
||||
conn := r.pool.Get()
|
||||
defer conn.Close()
|
||||
return conn.Do(command, args...)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Send writes the command to the client's output buffer.
|
||||
//
|
||||
// 执行异步命令 - Send
|
||||
func (r *Redis) Send(command string, args ...interface{}) error {
|
||||
conn := r.pool.Get()
|
||||
|
||||
61
g/database/gredis/gredis_config.go
Normal file
61
g/database/gredis/gredis_config.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gredis
|
||||
|
||||
import "github.com/gogf/gf/g/container/gmap"
|
||||
|
||||
const (
|
||||
// Default configuration group name.
|
||||
DEFAULT_GROUP_NAME = "default"
|
||||
)
|
||||
var (
|
||||
// Configuration groups.
|
||||
configs = gmap.NewStringInterfaceMap()
|
||||
)
|
||||
|
||||
// SetConfig sets the global configuration for specified group.
|
||||
// If <name> is not passed, it sets configuration for the default group name.
|
||||
func SetConfig(config Config, name...string) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
configs.Set(group, config)
|
||||
instances.Remove(group)
|
||||
}
|
||||
|
||||
// GetConfig returns the global configuration with specified group name.
|
||||
// If <name> is not passed, it returns configuration of the default group name.
|
||||
func GetConfig(name...string) (config Config, ok bool) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
if v := configs.Get(group); v != nil {
|
||||
return v.(Config), true
|
||||
}
|
||||
return Config{}, false
|
||||
}
|
||||
|
||||
// RemoveConfig removes the global configuration with specified group.
|
||||
// If <name> is not passed, it removes configuration of the default group name.
|
||||
func RemoveConfig(name...string) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
configs.Remove(group)
|
||||
instances.Remove(group)
|
||||
}
|
||||
|
||||
// ClearConfig removes all configurations and instances of redis.
|
||||
func ClearConfig() {
|
||||
configs.Clear()
|
||||
instances.Clear()
|
||||
}
|
||||
|
||||
|
||||
139
g/database/gredis/gredis_unit_test.go
Normal file
139
g/database/gredis/gredis_unit_test.go
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gredis_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/database/gredis"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
conn := redis.Conn()
|
||||
defer conn.Close()
|
||||
|
||||
_, 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"))
|
||||
|
||||
_, err = conn.Do("DEL", "k")
|
||||
gtest.Assert(err, nil)
|
||||
r, err = conn.Do("GET", "k")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(r, nil)
|
||||
})
|
||||
}
|
||||
@ -1,7 +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.
|
||||
|
||||
package encoding
|
||||
51
g/encoding/gbase64/gbase64_test.go
Normal file
51
g/encoding/gbase64/gbase64_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
package gbase64_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbase64"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testpair struct {
|
||||
decoded, encoded string
|
||||
}
|
||||
|
||||
var pairs = []testpair{
|
||||
// RFC 3548 examples
|
||||
{"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"},
|
||||
{"\x14\xfb\x9c\x03\xd9", "FPucA9k="},
|
||||
{"\x14\xfb\x9c\x03", "FPucAw=="},
|
||||
|
||||
// RFC 4648 examples
|
||||
{"", ""},
|
||||
{"f", "Zg=="},
|
||||
{"fo", "Zm8="},
|
||||
{"foo", "Zm9v"},
|
||||
{"foob", "Zm9vYg=="},
|
||||
{"fooba", "Zm9vYmE="},
|
||||
{"foobar", "Zm9vYmFy"},
|
||||
|
||||
// Wikipedia examples
|
||||
{"sure.", "c3VyZS4="},
|
||||
{"sure", "c3VyZQ=="},
|
||||
{"sur", "c3Vy"},
|
||||
{"su", "c3U="},
|
||||
{"leasure.", "bGVhc3VyZS4="},
|
||||
{"easure.", "ZWFzdXJlLg=="},
|
||||
{"asure.", "YXN1cmUu"},
|
||||
{"sure.", "c3VyZS4="},
|
||||
}
|
||||
|
||||
func TestBase64(t *testing.T) {
|
||||
for k := range pairs{
|
||||
gtest.Assert(gbase64.Encode(pairs[k].decoded), pairs[k].encoded)
|
||||
|
||||
e, _ := gbase64.Decode(pairs[k].encoded)
|
||||
gtest.Assert(e, pairs[k].decoded)
|
||||
}
|
||||
}
|
||||
@ -8,10 +8,10 @@
|
||||
package gbinary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// 二进制位(0|1)
|
||||
@ -20,304 +20,308 @@ type Bit int8
|
||||
// 针对基本类型进行二进制打包,支持的基本数据类型包括:int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte
|
||||
// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理
|
||||
func Encode(vs ...interface{}) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
switch value := vs[i].(type) {
|
||||
case int: buf.Write(EncodeInt(value))
|
||||
case int8: buf.Write(EncodeInt8(value))
|
||||
case int16: buf.Write(EncodeInt16(value))
|
||||
case int32: buf.Write(EncodeInt32(value))
|
||||
case int64: buf.Write(EncodeInt64(value))
|
||||
case uint: buf.Write(EncodeUint(value))
|
||||
case uint8: buf.Write(EncodeUint8(value))
|
||||
case uint16: buf.Write(EncodeUint16(value))
|
||||
case uint32: buf.Write(EncodeUint32(value))
|
||||
case uint64: buf.Write(EncodeUint64(value))
|
||||
case bool: buf.Write(EncodeBool(value))
|
||||
case string: buf.Write(EncodeString(value))
|
||||
case []byte: buf.Write(value)
|
||||
case float32: buf.Write(EncodeFloat32(value))
|
||||
case float64: buf.Write(EncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(EncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if vs[i] == nil {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
switch value := vs[i].(type) {
|
||||
case int: buf.Write(EncodeInt(value))
|
||||
case int8: buf.Write(EncodeInt8(value))
|
||||
case int16: buf.Write(EncodeInt16(value))
|
||||
case int32: buf.Write(EncodeInt32(value))
|
||||
case int64: buf.Write(EncodeInt64(value))
|
||||
case uint: buf.Write(EncodeUint(value))
|
||||
case uint8: buf.Write(EncodeUint8(value))
|
||||
case uint16: buf.Write(EncodeUint16(value))
|
||||
case uint32: buf.Write(EncodeUint32(value))
|
||||
case uint64: buf.Write(EncodeUint64(value))
|
||||
case bool: buf.Write(EncodeBool(value))
|
||||
case string: buf.Write(EncodeString(value))
|
||||
case []byte: buf.Write(value)
|
||||
case float32: buf.Write(EncodeFloat32(value))
|
||||
case float64: buf.Write(EncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(EncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// 将变量转换为二进制[]byte,并指定固定的[]byte长度返回,长度单位为字节(byte);
|
||||
// 如果转换的二进制长度超过指定长度,那么进行截断处理
|
||||
func EncodeByLength(length int, vs ...interface{}) []byte {
|
||||
b := Encode(vs...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length - len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0 : length]
|
||||
}
|
||||
return b
|
||||
b := Encode(vs...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length - len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0 : length]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64等等
|
||||
func Decode(b []byte, vs ...interface{}) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, vs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, vs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func EncodeString(s string) []byte {
|
||||
return []byte(s)
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func DecodeToString(b []byte) string {
|
||||
return string(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func EncodeBool(b bool) []byte {
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别int类型长度,转换为[]byte
|
||||
func EncodeInt(i int) []byte {
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别uint类型长度,转换为[]byte
|
||||
func EncodeUint(i uint) []byte {
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeInt8(i int8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func EncodeUint8(i uint8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func EncodeInt16(i int16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, uint16(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, uint16(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint16(i uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeInt32(i int32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, uint32(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, uint32(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint32(i uint32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeInt64(i int64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, uint64(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, uint64(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint64(i uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeFloat32(f float32) []byte {
|
||||
bits := math.Float32bits(f)
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, bits)
|
||||
return bytes
|
||||
bits := math.Float32bits(f)
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, bits)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeFloat64(f float64) []byte {
|
||||
bits := math.Float64bits(f)
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, bits)
|
||||
return bytes
|
||||
bits := math.Float64bits(f)
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, bits)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// 当b位数不够时,进行高位补0
|
||||
func fillUpSize(b []byte, l int) []byte {
|
||||
if len(b) >= l {
|
||||
return b
|
||||
}
|
||||
c := make([]byte, 0)
|
||||
c = append(c, b...)
|
||||
for i := 0; i < l - len(b); i++ {
|
||||
c = append(c, 0x00)
|
||||
}
|
||||
return c
|
||||
if len(b) >= l {
|
||||
return b
|
||||
}
|
||||
c := make([]byte, 0)
|
||||
c = append(c, b...)
|
||||
for i := 0; i < l - len(b); i++ {
|
||||
c = append(c, 0x00)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 将二进制解析为int类型,根据[]byte的长度进行自动转换.
|
||||
// 注意内部使用的是uint*,使用int会造成位丢失。
|
||||
func DecodeToInt(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return int(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(DecodeToUint32(b))
|
||||
} else {
|
||||
return int(DecodeToUint64(b))
|
||||
}
|
||||
if len(b) < 2 {
|
||||
return int(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(DecodeToUint32(b))
|
||||
} else {
|
||||
return int(DecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为uint类型,根据[]byte的长度进行自动转换
|
||||
func DecodeToUint(b []byte) uint {
|
||||
if len(b) < 2 {
|
||||
return uint(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(DecodeToUint32(b))
|
||||
} else {
|
||||
return uint(DecodeToUint64(b))
|
||||
}
|
||||
if len(b) < 2 {
|
||||
return uint(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(DecodeToUint32(b))
|
||||
} else {
|
||||
return uint(DecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为bool类型,识别标准是判断二进制中数值是否都为0,或者为空
|
||||
func DecodeToBool(b []byte) bool {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func DecodeToInt8(b []byte) int8 {
|
||||
return int8(b[0])
|
||||
return int8(b[0])
|
||||
}
|
||||
|
||||
func DecodeToUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
return uint8(b[0])
|
||||
}
|
||||
|
||||
func DecodeToInt16(b []byte) int16 {
|
||||
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
|
||||
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
|
||||
}
|
||||
|
||||
func DecodeToUint16(b []byte) uint16 {
|
||||
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
|
||||
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
|
||||
}
|
||||
|
||||
func DecodeToInt32(b []byte) int32 {
|
||||
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func DecodeToUint32(b []byte) uint32 {
|
||||
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
|
||||
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
|
||||
}
|
||||
|
||||
func DecodeToInt64(b []byte) int64 {
|
||||
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
func DecodeToUint64(b []byte) uint64 {
|
||||
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
|
||||
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
|
||||
}
|
||||
|
||||
func DecodeToFloat32(b []byte) float32 {
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func DecodeToFloat64(b []byte) float64 {
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
// 默认编码
|
||||
func EncodeBits(bits []Bit, i int, l int) []Bit {
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
}
|
||||
|
||||
// 将ui按位合并到bits数组中,并占length长度位(注意:uis数组中存放的是二进制的0|1数字)
|
||||
func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit {
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
}
|
||||
// 将bits转换为[]byte,从左至右进行编码,不足1 byte按0往末尾补充
|
||||
func EncodeBitsToBytes(bits []Bit) []byte {
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i : i + 8])))
|
||||
}
|
||||
return b
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i : i + 8])))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 解析为int
|
||||
func DecodeBits(bits []Bit) int {
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | int(i)
|
||||
}
|
||||
return v
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | int(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析为uint
|
||||
func DecodeBitsToUint(bits []Bit) uint {
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | uint(i)
|
||||
}
|
||||
return v
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | uint(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析[]byte为字位数组[]uint8
|
||||
func DecodeBytesToBits(bs []byte) []Bit {
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
}
|
||||
131
g/encoding/gbinary/gbinary_test.go
Normal file
131
g/encoding/gbinary/gbinary_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testData = map[string]interface{}{
|
||||
//"nil": nil,
|
||||
"int": int(123),
|
||||
"int8": int8(-99),
|
||||
"int8.max": math.MaxInt8,
|
||||
"int16": int16(123),
|
||||
"int16.max": math.MaxInt16,
|
||||
"int32": int32(-199),
|
||||
"int32.max": math.MaxInt32,
|
||||
"int64": int64(123),
|
||||
"uint": uint(123),
|
||||
"uint8": uint8(123),
|
||||
"uint8.max": math.MaxUint8,
|
||||
"uint16": uint16(9999),
|
||||
"uint16.max": math.MaxUint16,
|
||||
"uint32": uint32(123),
|
||||
"uint64": uint64(123),
|
||||
"bool.true": true,
|
||||
"bool.false": false,
|
||||
"string": "hehe haha",
|
||||
"byte": []byte("hehe haha"),
|
||||
"float32": float32(123.456),
|
||||
"float32.max": math.MaxFloat32,
|
||||
"float64": float64(123.456),
|
||||
}
|
||||
|
||||
func TestEncodeAndDecode(t *testing.T) {
|
||||
for k, v := range testData {
|
||||
ve := gbinary.Encode(v)
|
||||
ve1 := gbinary.EncodeByLength(len(ve), v)
|
||||
|
||||
//t.Logf("%s:%v, encoded:%v\n", k, v, ve)
|
||||
switch v.(type) {
|
||||
case int:
|
||||
gtest.Assert(gbinary.DecodeToInt(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt(ve1), v)
|
||||
case int8:
|
||||
gtest.Assert(gbinary.DecodeToInt8(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt8(ve1), v)
|
||||
case int16:
|
||||
gtest.Assert(gbinary.DecodeToInt16(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt16(ve1), v)
|
||||
case int32:
|
||||
gtest.Assert(gbinary.DecodeToInt32(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt32(ve1), v)
|
||||
case int64:
|
||||
gtest.Assert(gbinary.DecodeToInt64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt64(ve1), v)
|
||||
case uint:
|
||||
gtest.Assert(gbinary.DecodeToUint(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint(ve1), v)
|
||||
case uint8:
|
||||
gtest.Assert(gbinary.DecodeToUint8(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint8(ve1), v)
|
||||
case uint16:
|
||||
gtest.Assert(gbinary.DecodeToUint16(ve1), v)
|
||||
gtest.Assert(gbinary.DecodeToUint16(ve), v)
|
||||
case uint32:
|
||||
gtest.Assert(gbinary.DecodeToUint32(ve1), v)
|
||||
gtest.Assert(gbinary.DecodeToUint32(ve), v)
|
||||
case uint64:
|
||||
gtest.Assert(gbinary.DecodeToUint64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint64(ve1), v)
|
||||
case bool:
|
||||
gtest.Assert(gbinary.DecodeToBool(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToBool(ve1), v)
|
||||
case string:
|
||||
gtest.Assert(gbinary.DecodeToString(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToString(ve1), v)
|
||||
case float32:
|
||||
gtest.Assert(gbinary.DecodeToFloat32(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToFloat32(ve1), v)
|
||||
case float64:
|
||||
gtest.Assert(gbinary.DecodeToFloat64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToFloat64(ve1), v)
|
||||
default:
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
res := make([]byte, len(ve))
|
||||
err := gbinary.Decode(ve, res)
|
||||
if err != nil {
|
||||
t.Errorf("test data: %s, %v, error:%v", k, v, err)
|
||||
}
|
||||
gtest.Assert(res, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Url string
|
||||
}
|
||||
|
||||
func TestEncodeStruct(t *testing.T) {
|
||||
user := User{"wenzi1", 999, "www.baidu.com"}
|
||||
ve := gbinary.Encode(user)
|
||||
s := gbinary.DecodeToString(ve)
|
||||
gtest.Assert(string(s), s)
|
||||
}
|
||||
|
||||
var testBitData = []int{0, 99, 122, 129, 222, 999, 22322}
|
||||
|
||||
func TestBits(t *testing.T) {
|
||||
for i := range testBitData {
|
||||
bits := make([]gbinary.Bit, 0)
|
||||
res := gbinary.EncodeBits(bits, testBitData[i], 64)
|
||||
|
||||
gtest.Assert(gbinary.DecodeBits(res), testBitData[i])
|
||||
gtest.Assert(gbinary.DecodeBitsToUint(res), uint(testBitData[i]))
|
||||
|
||||
gtest.Assert(gbinary.DecodeBytesToBits(gbinary.EncodeBitsToBytes(res)), res)
|
||||
}
|
||||
|
||||
}
|
||||
@ -51,5 +51,5 @@ func ToUTF8(charset string, src string) (dst string, err error) {
|
||||
|
||||
// UTF8转指定字符集
|
||||
func UTF8To(charset string, src string) (dst string, err error) {
|
||||
return Convert(charset, "UTF-8", src)
|
||||
return Convert(charset, "UTF-8", src)
|
||||
}
|
||||
@ -1,10 +1,16 @@
|
||||
package gcharset
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gcharset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gcharset"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
var testData = []struct {
|
||||
utf8, other, otherEncoding string
|
||||
}{
|
||||
@ -52,7 +58,7 @@ var testData = []struct {
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, data := range testData {
|
||||
str := ""
|
||||
str, err := Convert("UTF-8", data.otherEncoding, data.other)
|
||||
str, err := gcharset.Convert("UTF-8", data.otherEncoding, data.other)
|
||||
if err != nil {
|
||||
t.Errorf("Could not create decoder for %v", err)
|
||||
continue
|
||||
@ -68,7 +74,7 @@ func TestDecode(t *testing.T) {
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, data := range testData {
|
||||
str := ""
|
||||
str, err := Convert(data.otherEncoding, "UTF-8", data.utf8)
|
||||
str, err := gcharset.Convert(data.otherEncoding, "UTF-8", data.utf8)
|
||||
if err != nil {
|
||||
t.Errorf("Could not create decoder for %v", err)
|
||||
continue
|
||||
@ -86,7 +92,7 @@ func TestConvert(t *testing.T) {
|
||||
dstCharset := "gbk"
|
||||
dst := "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed"
|
||||
|
||||
str, err := Convert(dstCharset, srcCharset, src)
|
||||
str, err := gcharset.Convert(dstCharset, srcCharset, src)
|
||||
if err != nil {
|
||||
t.Errorf("convert error. %v", err)
|
||||
return
|
||||
|
||||
42
g/encoding/gcompress/gcompress_test.go
Normal file
42
g/encoding/gcompress/gcompress_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
package gcompress_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gcompress"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZlib(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
src := "hello, world\n"
|
||||
dst := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
|
||||
gtest.Assert(gcompress.Zlib([]byte(src)), dst)
|
||||
|
||||
gtest.Assert(gcompress.UnZlib(dst), []byte(src))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGzip(t *testing.T) {
|
||||
src := "Hello World!!"
|
||||
|
||||
gzip := []byte{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0xf2, 0x48, 0xcd, 0xc9, 0xc9,
|
||||
0x57, 0x08, 0xcf, 0x2f, 0xca,
|
||||
0x49, 0x51, 0x54, 0x04, 0x04,
|
||||
0x00, 0x00, 0xff, 0xff, 0x9d,
|
||||
0x24, 0xa8, 0xd1, 0x0d, 0x00,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
gtest.Assert(gcompress.Gzip([]byte(src)), gzip)
|
||||
|
||||
gtest.Assert(gcompress.UnGzip(gzip), []byte(src))
|
||||
}
|
||||
32
g/encoding/ghtml/ghtml_test.go
Normal file
32
g/encoding/ghtml/ghtml_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
package ghtml_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/ghtml"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStripTags(t *testing.T) {
|
||||
src := `<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>`
|
||||
dst := `Test paragraph. Other text`
|
||||
gtest.Assert(ghtml.StripTags(src), dst)
|
||||
}
|
||||
|
||||
func TestEntities(t *testing.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
gtest.Assert(ghtml.Entities(src), dst)
|
||||
gtest.Assert(ghtml.EntitiesDecode(dst), src)
|
||||
}
|
||||
|
||||
func TestSpecialChars(t *testing.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
gtest.Assert(ghtml.SpecialChars(src), dst)
|
||||
gtest.Assert(ghtml.SpecialCharsDecode(dst), src)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
30
g/encoding/gjson/gjson_bench_test.go
Normal file
30
g/encoding/gjson/gjson_bench_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gjson"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_Set1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New(map[string]string{
|
||||
"k1" : "v1",
|
||||
"k2" : "v2",
|
||||
})
|
||||
p.Set("k1.k11", []int{1,2,3})
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1,2,3})
|
||||
}
|
||||
}
|
||||
|
||||
253
g/encoding/gjson/gjson_unit_basic_test.go
Normal file
253
g/encoding/gjson/gjson_unit_basic_test.go
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
"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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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]`))
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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"), "")
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := gjson.New(nil)
|
||||
p.Append("a", 1)
|
||||
p.Append("a", 2)
|
||||
gtest.Assert(p.Get("a"), g.Slice{1, 2})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gjson.New(nil)
|
||||
p.Append("a.b", 1)
|
||||
p.Append("a.c", 2)
|
||||
gtest.Assert(p.Get("a"), g.Map{
|
||||
"b" : g.Slice{1},
|
||||
"c" : g.Slice{2},
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gjson.New(nil)
|
||||
p.Set("a", 1)
|
||||
err := p.Append("a", 2)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(p.Get("a"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
156
g/encoding/gjson/gjson_unit_load_test.go
Normal file
156
g/encoding/gjson/gjson_unit_load_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gjson_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"
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_YAML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML1(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
230
g/encoding/gjson/gjson_unit_set_test.go
Normal file
230
g/encoding/gjson/gjson_unit_set_test.go
Normal file
@ -0,0 +1,230 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/encoding/gjson"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Set1(t *testing.T) {
|
||||
e := []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)
|
||||
p := gjson.New(map[string]string{
|
||||
"k1" : "v1",
|
||||
"k2" : "v2",
|
||||
})
|
||||
p.Set("k1.k11", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set2(t *testing.T) {
|
||||
e := []byte(`[[null,1]]`)
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("0.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set3(t *testing.T) {
|
||||
e := []byte(`{"kv":{"k1":"v1"}}`)
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("kv", map[string]string {
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set4(t *testing.T) {
|
||||
e := []byte(`["a",[{"k1":"v1"}]]`)
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("1.0", map[string]string{
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set5(t *testing.T) {
|
||||
e := []byte(`[[[[[[[[[[[[[[[[[[[[[1,2,3]]]]]]]]]]]]]]]]]]]]]`)
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set6(t *testing.T) {
|
||||
e := []byte(`["a",[1,2,3]]`)
|
||||
p := gjson.New([]string{"a"})
|
||||
p.Set("1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set7(t *testing.T) {
|
||||
e := []byte(`{"0":[null,[1,2,3]],"k1":"v1","k2":"v2"}`)
|
||||
p := gjson.New(map[string]string{
|
||||
"k1" : "v1",
|
||||
"k2" : "v2",
|
||||
})
|
||||
p.Set("0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set8(t *testing.T) {
|
||||
e := []byte(`{"0":[[[[[[null,[1,2,3]]]]]]],"k1":"v1","k2":"v2"}`)
|
||||
p := gjson.New(map[string]string{
|
||||
"k1" : "v1",
|
||||
"k2" : "v2",
|
||||
})
|
||||
p.Set("0.0.0.0.0.0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set9(t *testing.T) {
|
||||
e := []byte(`{"k1":[null,[1,2,3]],"k2":"v2"}`)
|
||||
p := gjson.New(map[string]string{
|
||||
"k1" : "v1",
|
||||
"k2" : "v2",
|
||||
})
|
||||
p.Set("k1.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func Test_Set10(t *testing.T) {
|
||||
e := []byte(`{"a":{"b":{"c":1}}}`)
|
||||
p := gjson.New(nil)
|
||||
p.Set("a.b.c", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func Test_Set11(t *testing.T) {
|
||||
e := []byte(`{"a":{"b":{}}}`)
|
||||
p, _ := gjson.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
|
||||
p.Remove("a.b.c")
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set12(t *testing.T) {
|
||||
e := []byte(`[0,1]`)
|
||||
p := gjson.New(nil)
|
||||
p.Set("0", 0)
|
||||
p.Set("1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set13(t *testing.T) {
|
||||
e := []byte(`{"array":[0,1]}`)
|
||||
p := gjson.New(nil)
|
||||
p.Set("array.0", 0)
|
||||
p.Set("array.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Set14(t *testing.T) {
|
||||
e := []byte(`{"f":{"a":1}}`)
|
||||
p := gjson.New(nil)
|
||||
p.Set("f", "m")
|
||||
p.Set("f.a", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gp.
|
||||
|
||||
// Package gparser provides a flexible and easy way for accessing/converting variable and JSON/XML/YAML/TOML contents.
|
||||
// Package gparser provides convenient API for accessing/converting variable and JSON/XML/YAML/TOML.
|
||||
package gparser
|
||||
|
||||
import (
|
||||
@ -16,13 +16,16 @@ type Parser struct {
|
||||
json *gjson.Json
|
||||
}
|
||||
|
||||
// 将变量转换为Parser对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义
|
||||
// value可以传递nil, 表示创建一个空的Parser对象
|
||||
func New (value interface{}, unsafe...bool) *Parser {
|
||||
// New creates a Parser object with any variable type of <data>,
|
||||
// but <data> should be a map or slice for data access reason,
|
||||
// or it will make no sense.
|
||||
// The <unsafe> param specifies whether using this Parser object
|
||||
// in un-concurrent-safe context, which is false in default.
|
||||
func New(value interface{}, unsafe...bool) *Parser {
|
||||
return &Parser{gjson.New(value, unsafe...)}
|
||||
}
|
||||
|
||||
// 非并发安全Parser对象
|
||||
// NewUnsafe creates a un-concurrent-safe Parser object.
|
||||
func NewUnsafe (value...interface{}) *Parser {
|
||||
if len(value) > 0 {
|
||||
return &Parser{gjson.New(value[0], false)}
|
||||
@ -30,57 +33,64 @@ func NewUnsafe (value...interface{}) *Parser {
|
||||
return &Parser{gjson.New(nil, false)}
|
||||
}
|
||||
|
||||
func Load (path string) (*Parser, error) {
|
||||
if j, e := gjson.Load(path); e == nil {
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Parser object from its content.
|
||||
func Load (path string, unsafe...bool) (*Parser, error) {
|
||||
if j, e := gjson.Load(path, unsafe...); e == nil {
|
||||
return &Parser{j}, nil
|
||||
} else {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
// 支持的数据内容格式:json(默认), xml, yaml/yml, toml
|
||||
func LoadContent (data []byte, dataType...string) (*Parser, error) {
|
||||
if j, e := gjson.LoadContent(data, dataType...); e == nil {
|
||||
// LoadContent creates a Parser object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent (data []byte, unsafe...bool) (*Parser, error) {
|
||||
if j, e := gjson.LoadContent(data, unsafe...); e == nil {
|
||||
return &Parser{j}, nil
|
||||
} else {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
// 设置自定义的层级分隔符号
|
||||
// SetSplitChar sets the separator char for hierarchical data access.
|
||||
func (p *Parser) SetSplitChar(char byte) {
|
||||
p.json.SetSplitChar(char)
|
||||
}
|
||||
|
||||
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
|
||||
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
|
||||
// SetViolenceCheck enables/disables violence check for hierarchical data access.
|
||||
func (p *Parser) SetViolenceCheck(check bool) {
|
||||
p.json.SetViolenceCheck(check)
|
||||
}
|
||||
|
||||
// 将指定的json内容转换为指定结构返回,查找失败或者转换失败,目标对象转换为nil
|
||||
// 注意第二个参数需要给的是变量地址
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func (p *Parser) GetToVar(pattern string, v interface{}) error {
|
||||
return p.json.GetToVar(pattern, v)
|
||||
}
|
||||
|
||||
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
|
||||
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
||||
// GetMap gets the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
func (p *Parser) GetMap(pattern string) map[string]interface{} {
|
||||
return p.json.GetMap(pattern)
|
||||
}
|
||||
|
||||
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
|
||||
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
||||
// GetArray gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (p *Parser) GetArray(pattern string) []interface{} {
|
||||
return p.json.GetArray(pattern)
|
||||
}
|
||||
|
||||
// 返回指定json中的string
|
||||
// GetString gets the value by specified <pattern>,
|
||||
// and converts it to string.
|
||||
func (p *Parser) GetString(pattern string) string {
|
||||
return p.json.GetString(pattern)
|
||||
}
|
||||
|
||||
// GetStrings gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []string.
|
||||
func (p *Parser) GetStrings(pattern string) []string {
|
||||
return p.json.GetStrings(pattern)
|
||||
}
|
||||
@ -97,7 +107,10 @@ func (p *Parser) GetTimeDuration(pattern string) time.Duration {
|
||||
return p.json.GetTimeDuration(pattern)
|
||||
}
|
||||
|
||||
// 返回指定json中的bool(false:"", 0, false, off)
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
func (p *Parser) GetBool(pattern string) bool {
|
||||
return p.json.GetBool(pattern)
|
||||
}
|
||||
@ -158,51 +171,60 @@ func (p *Parser) GetFloats(pattern string) []float64 {
|
||||
return p.json.GetFloats(pattern)
|
||||
}
|
||||
|
||||
// 将指定变量转换为struct对象(对象属性赋值)
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error {
|
||||
return p.json.GetToStruct(pattern, objPointer)
|
||||
}
|
||||
|
||||
// 根据pattern查找并设置数据
|
||||
// 注意:写入的时候"."符号只能表示层级,不能使用带"."符号的键名
|
||||
// Set sets value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (p *Parser) Set(pattern string, value interface{}) error {
|
||||
return p.json.Set(pattern, value)
|
||||
}
|
||||
|
||||
// 计算指定pattern的元素长度(pattern对应数据类型为map[string]interface{}/[]interface{}时有效)
|
||||
// Len returns the length/size of the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice or map.
|
||||
// It returns -1 if the target value is not found, or its type is invalid.
|
||||
func (p *Parser) Len(pattern string) int {
|
||||
return p.json.Len(pattern)
|
||||
}
|
||||
|
||||
// 指定pattern追加元素
|
||||
// Append appends value to the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice.
|
||||
func (p *Parser) Append(pattern string, value interface{}) error {
|
||||
return p.json.Append(pattern, value)
|
||||
}
|
||||
|
||||
// 动态删除变量节点
|
||||
// Remove deletes value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (p *Parser) Remove(pattern string) error {
|
||||
return p.json.Remove(pattern)
|
||||
}
|
||||
|
||||
// 根据约定字符串方式访问json解析数据,参数形如: "items.name.first", "list.0"; 当pattern为空时,表示获取所有数据
|
||||
// 返回的结果类型的interface{},因此需要自己做类型转换;
|
||||
// 如果找不到对应节点的数据,返回nil;
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
func (p *Parser) Get(pattern...string) interface{} {
|
||||
return p.json.Get(pattern...)
|
||||
}
|
||||
|
||||
// 转换为map[string]interface{}类型,如果转换失败,返回nil
|
||||
// ToMap converts current object values to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (p *Parser) ToMap() map[string]interface{} {
|
||||
return p.json.ToMap()
|
||||
}
|
||||
|
||||
// 转换为[]interface{}类型,如果转换失败,返回nil
|
||||
// ToArray converts current object values to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (p *Parser) ToArray() []interface{} {
|
||||
return p.json.ToArray()
|
||||
}
|
||||
|
||||
/* 以下为数据文件格式转换,支持类型:xml, json, yaml/yml, toml */
|
||||
|
||||
func (p *Parser) ToXml(rootTag...string) ([]byte, error) {
|
||||
return p.json.ToXml(rootTag...)
|
||||
}
|
||||
@ -227,12 +249,13 @@ func (p *Parser) ToToml() ([]byte, error) {
|
||||
return p.json.ToToml()
|
||||
}
|
||||
|
||||
// 打印Json对象
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (p *Parser) Dump() error {
|
||||
return p.json.Dump()
|
||||
}
|
||||
|
||||
// 将变量解析为对应的struct对象,注意传递的参数为struct对象指针
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
func (p *Parser) ToStruct(o interface{}) error {
|
||||
return p.json.ToStruct(o)
|
||||
}
|
||||
@ -261,7 +284,6 @@ func VarToToml(value interface{}) ([]byte, error) {
|
||||
return New(value).ToToml()
|
||||
}
|
||||
|
||||
// 将变量解析为对应的struct对象,注意传递的参数为struct对象指针
|
||||
func VarToStruct(value interface{}, obj interface{}) error {
|
||||
return New(value).ToStruct(obj)
|
||||
}
|
||||
|
||||
208
g/encoding/gparser/gparser_unit_basic_test.go
Normal file
208
g/encoding/gparser/gparser_unit_basic_test.go
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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]`))
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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"), "")
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
156
g/encoding/gparser/gparser_unit_load_test.go
Normal file
156
g/encoding/gparser/gparser_unit_load_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"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 := 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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_YAML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML1(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
@ -4,15 +4,12 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// 单元测试
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Set1(t *testing.T) {
|
||||
@ -23,7 +20,6 @@ func Test_Set1(t *testing.T) {
|
||||
})
|
||||
p.Set("k1.k11", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -37,7 +33,6 @@ func Test_Set2(t *testing.T) {
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("0.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -49,11 +44,10 @@ func Test_Set2(t *testing.T) {
|
||||
func Test_Set3(t *testing.T) {
|
||||
e := []byte(`{"kv":{"k1":"v1"}}`)
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("kv", map[string]string{
|
||||
p.Set("kv", map[string]string {
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -69,7 +63,6 @@ func Test_Set4(t *testing.T) {
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -83,7 +76,6 @@ func Test_Set5(t *testing.T) {
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -97,7 +89,6 @@ func Test_Set6(t *testing.T) {
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -114,7 +105,6 @@ func Test_Set7(t *testing.T) {
|
||||
})
|
||||
p.Set("0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -131,7 +121,6 @@ func Test_Set8(t *testing.T) {
|
||||
})
|
||||
p.Set("0.0.0.0.0.0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -148,7 +137,6 @@ func Test_Set9(t *testing.T) {
|
||||
})
|
||||
p.Set("k1.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -163,7 +151,6 @@ func Test_Set10(t *testing.T) {
|
||||
p := gparser.New(nil)
|
||||
p.Set("a.b.c", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -175,10 +162,9 @@ func Test_Set10(t *testing.T) {
|
||||
|
||||
func Test_Set11(t *testing.T) {
|
||||
e := []byte(`{"a":{"b":{}}}`)
|
||||
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`), "json")
|
||||
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
|
||||
p.Remove("a.b.c")
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -193,7 +179,6 @@ func Test_Set12(t *testing.T) {
|
||||
p.Set("0", 0)
|
||||
p.Set("1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -208,7 +193,6 @@ func Test_Set13(t *testing.T) {
|
||||
p.Set("array.0", 0)
|
||||
p.Set("array.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
fmt.Println(string(c))
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -217,7 +201,16 @@ func Test_Set13(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func Test_Set14(t *testing.T) {
|
||||
e := []byte(`{"f":{"a":1}}`)
|
||||
p := gparser.New(nil)
|
||||
p.Set("f", "m")
|
||||
p.Set("f.a", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
142
g/encoding/gtoml/gtoml_test.go
Normal file
142
g/encoding/gtoml/gtoml_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
package gtoml_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/encoding/gtoml"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tomlStr string = `
|
||||
# 模板引擎目录
|
||||
viewpath = "/home/www/templates/"
|
||||
# MySQL数据库配置
|
||||
[redis]
|
||||
disk = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
`
|
||||
|
||||
var tomlErr string = `
|
||||
# 模板引擎目录
|
||||
viewpath = "/home/www/templates/"
|
||||
# MySQL数据库配置
|
||||
[redis]
|
||||
dd = 11
|
||||
[redis]
|
||||
disk = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
`
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := make(map[string]string)
|
||||
m["toml"] = tomlStr
|
||||
res, err := gtoml.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := gparser.LoadContent(res)
|
||||
if err != nil {
|
||||
t.Errorf("parser failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
gtest.Assert(p.GetString("toml"), tomlStr)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
_, err := gtoml.Encode(tomlErr)
|
||||
if err == nil {
|
||||
t.Errorf("encode should be failed. %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := make(map[string]string)
|
||||
m["toml"] = tomlStr
|
||||
res, err := gtoml.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
decodeStr, err := gtoml.Decode(res)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
gtest.Assert(decodeStr.(map[string]interface{})["toml"], tomlStr)
|
||||
|
||||
decodeStr1 := make(map[string]interface{})
|
||||
err = gtoml.DecodeTo(res, &decodeStr1)
|
||||
if err != nil {
|
||||
t.Errorf("decodeTo failed. %v", err)
|
||||
return
|
||||
}
|
||||
gtest.Assert(decodeStr1["toml"], tomlStr)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
_, err := gtoml.Decode([]byte(tomlErr))
|
||||
if err == nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
decodeStr1 := make(map[string]interface{})
|
||||
err = gtoml.DecodeTo([]byte(tomlErr), &decodeStr1)
|
||||
if err == nil {
|
||||
t.Errorf("decodeTo failed. %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestToJson(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := make(map[string]string)
|
||||
m["toml"] = tomlStr
|
||||
res, err := gtoml.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonToml, err := gtoml.ToJson(res)
|
||||
if err != nil {
|
||||
t.Errorf("ToJson failed. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := gparser.LoadContent(res)
|
||||
if err != nil {
|
||||
t.Errorf("parser failed. %v", err)
|
||||
return
|
||||
}
|
||||
expectJson, err := p.ToJson()
|
||||
if err != nil {
|
||||
t.Errorf("parser ToJson failed. %v", err)
|
||||
return
|
||||
}
|
||||
gtest.Assert(jsonToml, expectJson)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
_, err := gtoml.ToJson([]byte(tomlErr))
|
||||
if err == nil {
|
||||
t.Errorf("ToJson failed. %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
92
g/encoding/gurl/url_test.go
Normal file
92
g/encoding/gurl/url_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
package gurl_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gurl"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var urlStr string = `https://golang.org/x/crypto?go-get=1 +`
|
||||
var urlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1+%2B`
|
||||
var rawUrlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1%20%2B`
|
||||
|
||||
func TestEncodeAndDecode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gurl.Encode(urlStr), urlEncode)
|
||||
|
||||
res, err := gurl.Decode(urlEncode)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
gtest.Assert(res, urlStr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRowEncodeAndDecode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gurl.RawEncode(urlStr), rawUrlEncode)
|
||||
|
||||
res, err := gurl.RawDecode(rawUrlEncode)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
gtest.Assert(res, urlStr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildQuery(t *testing.T) {
|
||||
src := url.Values{
|
||||
"a": {"a2", "a1"},
|
||||
"b": {"b2", "b1"},
|
||||
"c": {"c1", "c2"},
|
||||
}
|
||||
expect := "a=a2&a=a1&b=b2&b=b1&c=c1&c=c2"
|
||||
|
||||
gtest.Assert(gurl.BuildQuery(src), expect)
|
||||
}
|
||||
|
||||
func TestParseURL(t *testing.T) {
|
||||
src := `http://username:password@hostname:9090/path?arg=value#anchor`
|
||||
expect := map[string]string{
|
||||
"scheme": "http",
|
||||
"host": "hostname",
|
||||
"port": "9090",
|
||||
"user": "username",
|
||||
"pass": "password",
|
||||
"path": "/path",
|
||||
"query": "arg=value",
|
||||
"fragment": "anchor",
|
||||
}
|
||||
|
||||
gtest.Case(t, func() {
|
||||
component := 0
|
||||
for k, v := range []string{"all", "scheme", "host", "port", "user", "pass", "path", "query", "fragment"} {
|
||||
if v == "all" {
|
||||
component = -1
|
||||
} else {
|
||||
component = 1 << (uint(k - 1))
|
||||
}
|
||||
|
||||
res, err := gurl.ParseURL(src, component)
|
||||
if err != nil {
|
||||
t.Errorf("ParseURL failed. component:%v, err:%v", component, err)
|
||||
return
|
||||
}
|
||||
|
||||
if v == "all" {
|
||||
gtest.Assert(res, expect)
|
||||
} else {
|
||||
gtest.Assert(res[v], expect[v])
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -5,75 +5,80 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gxml provides accessing and converting for XML content.
|
||||
//
|
||||
// XML数据格式解析。
|
||||
package gxml
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/third/github.com/clbanning/mxj"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/third/github.com/axgle/mahonia"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/third/github.com/clbanning/mxj"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 将XML内容解析为map变量
|
||||
func Decode(xmlbyte []byte) (map[string]interface{}, error) {
|
||||
prepare(xmlbyte)
|
||||
return mxj.NewMapXml(xmlbyte)
|
||||
func Decode(content []byte) (map[string]interface{}, error) {
|
||||
res, err := convert(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mxj.NewMapXml(res)
|
||||
}
|
||||
|
||||
// 将map变量解析为XML格式内容
|
||||
func Encode(v map[string]interface{}, rootTag...string) ([]byte, error) {
|
||||
return mxj.Map(v).Xml(rootTag...)
|
||||
func Encode(v map[string]interface{}, rootTag ...string) ([]byte, error) {
|
||||
return mxj.Map(v).Xml(rootTag...)
|
||||
}
|
||||
|
||||
func EncodeWithIndent(v map[string]interface{}, rootTag...string) ([]byte, error) {
|
||||
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
|
||||
func EncodeWithIndent(v map[string]interface{}, rootTag ...string) ([]byte, error) {
|
||||
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
|
||||
}
|
||||
|
||||
// XML格式内容直接转换为JSON格式内容
|
||||
func ToJson(xmlbyte []byte) ([]byte, error) {
|
||||
prepare(xmlbyte)
|
||||
mv, err := mxj.NewMapXml(xmlbyte)
|
||||
func ToJson(content []byte) ([]byte, error) {
|
||||
res, err := convert(content)
|
||||
if err != nil {
|
||||
fmt.Println("convert error. ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mv, err := mxj.NewMapXml(res)
|
||||
if err == nil {
|
||||
return mv.Json()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
return mv.Json()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// XML字符集预处理
|
||||
// @author wenzi1
|
||||
// @date 20180604
|
||||
func prepare(xmlbyte []byte) error {
|
||||
// @date 20180604 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换
|
||||
func convert(xmlbyte []byte) (res []byte, err error) {
|
||||
patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>`
|
||||
charsetReader := func(charset string, input io.Reader) (io.Reader, error) {
|
||||
reader := mahonia.GetCharset(charset)
|
||||
if reader == nil {
|
||||
return nil, errors.New(fmt.Sprintf("not support charset:%s", charset))
|
||||
}
|
||||
return reader.NewDecoder().NewReader(input), nil
|
||||
}
|
||||
|
||||
matchStr, err := gregex.MatchString(patten, string(xmlbyte))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
xmlEncode := "UTF-8"
|
||||
if len(matchStr) == 2 {
|
||||
xmlEncode = matchStr[1]
|
||||
xmlEncode = matchStr[1]
|
||||
}
|
||||
|
||||
charset := mahonia.GetCharset(xmlEncode)
|
||||
if charset == nil {
|
||||
return errors.New(fmt.Sprintf("not support charset:%s", xmlEncode))
|
||||
s := mahonia.GetCharset(xmlEncode)
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("not support charset:%s\n", xmlEncode)
|
||||
}
|
||||
|
||||
if !strings.EqualFold(charset.Name, "UTF-8") {
|
||||
mxj.CustomDecoder = &xml.Decoder{Strict : false, CharsetReader : charsetReader}
|
||||
res, err = gregex.Replace(patten, []byte(""), []byte(xmlbyte))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.EqualFold(s.Name, "UTF-8") {
|
||||
res = []byte(s.NewDecoder().ConvertString(string(res)))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user