Compare commits

..

191 Commits

Author SHA1 Message Date
66306464e1 add Pop/Pops functions for gset 2019-04-26 08:57:48 +08:00
dd34ac1722 add build-in function 'eq/ne/lt/le/gt/ge' for gview to replace the same functions in stdlib 2019-04-25 23:23:24 +08:00
34cb222b33 remove temprary function map parameter for gview when parsing template file and content 2019-04-25 22:14:20 +08:00
d39ef156de garray updates 2019-04-24 22:23:32 +08:00
f464dc7fb8 add NewFrom/NewIntSetFrom/NewStringSetFrom functions for gset; add more example for gset 2019-04-24 18:52:24 +08:00
d29b27a5df add Merge/Sum functions for gset 2019-04-24 18:15:50 +08:00
aadc6aa504 Merge pull request #114 from proptypes/master
fix: #112
2019-04-24 14:07:37 +08:00
e8c3dfa13e fix: #112
closes #112
2019-04-24 11:31:13 +08:00
9eea93cc6e change param type from string to interface{} for ghttp.ClientRequest 2019-04-23 20:12:44 +08:00
308cb55b6b version updates 2019-04-23 19:44:28 +08:00
75ada78f8f remove parameter bind from ghttp.RouterGroup.Bind 2019-04-23 19:39:35 +08:00
ecd86e3a12 add layout example for package gview 2019-04-23 19:11:38 +08:00
c1aa5eb717 Merge pull request #98 from qq976739120/gmap-test
新增测试方法
2019-04-23 18:56:58 +08:00
d2fed1198b Merge pull request #110 from jroam/master
增加gtime包下,format方法能直接格式化星期的数字型的值
2019-04-23 18:54:43 +08:00
a9f9261dbd add gregex.ReplaceFuncMatch/ReplaceStringFuncMatch functions for package gregex 2019-04-23 14:15:12 +08:00
161e0d6e97 edit var name to "weekMap" 2019-04-23 09:59:13 +08:00
3efe511f42 优化星期英文值和数字值的格式化功能 2019-04-22 23:13:35 +08:00
5d04c2e50a fix issue in gfile.MainPkgPath 2019-04-22 22:33:11 +08:00
7b26b7ea4c fix issue in gstr.Chr 2019-04-22 21:37:11 +08:00
9d1063c6b2 Merge branch 'master' of https://github.com/gogf/gf 2019-04-22 15:48:16 +08:00
5ff7632d32 add support of layout feature for gview; fix issue in gstr.Chr 2019-04-22 15:47:59 +08:00
e6fb41504c 简写"w"参数的注释,增加周六值测试 2019-04-22 14:10:46 +08:00
a800f731dd 优化格式化星期值性能 2019-04-22 13:55:53 +08:00
f1a9fbb74e Merge branch 'master' of https://github.com/jroam/gf 2019-04-22 10:51:40 +08:00
cf81a73526 增加gtime包下,format能直接格式化星期的数字型的值 2019-04-22 10:51:24 +08:00
65036fffe8 Merge pull request #13 from gogf/master
日常更新
2019-04-22 10:49:28 +08:00
a69934a7e3 添加星期值的int形式 2019-04-21 19:36:06 +08:00
9400457bf2 Merge pull request #107 from wenzi1/master
add encoding package unit test
2019-04-19 18:29:11 +08:00
5060329721 Merge branch 'master' of https://github.com/wenzi1/gf 2019-04-19 17:11:10 +08:00
07ab1d60e8 add encoding package unit test 2019-04-19 17:04:43 +08:00
7377a82e19 Merge pull request #8 from gogf/master
Merge pull request #80 from wenzi1/master
2019-04-19 12:06:00 +08:00
f82e3ac808 Merge pull request #80 from wenzi1/master
增加gbinary单元测试
2019-04-18 22:50:24 +08:00
1a6cd1de04 Merge pull request #7 from gogf/master
update latest code
2019-04-18 20:22:17 +08:00
90e6f685b7 Merge pull request #12 from gogf/master
日常更新
2019-04-18 17:55:19 +08:00
0fc825dac1 add gcompress packge unit test 2019-04-18 12:34:01 +08:00
dbb27efe3e rename io to writer for glog.Logger 2019-04-18 09:11:14 +08:00
2d3d2e783e add default writer for glog to be integrated with other package; comments update for glog 2019-04-17 23:50:37 +08:00
6ae1defa35 Merge pull request #6 from gogf/master
pull
2019-04-17 16:56:18 +08:00
d55e77fb90 README updates 2019-04-17 09:50:57 +08:00
7fb9ae71e7 Merge pull request #103 from jroam/master
增加gfile模块的测试代码
2019-04-17 09:47:51 +08:00
9419555149 add BindFuncMap funtion for gview; README updates 2019-04-17 09:46:31 +08:00
ab634f8beb del some test code 2019-04-16 23:15:22 +08:00
9503b80d57 edit tempdir 2019-04-16 23:07:53 +08:00
bc870226e5 Merge pull request #11 from gogf/master
日常更新
2019-04-16 22:52:03 +08:00
7e106ae011 Merge pull request #81 from pibigstar/master
add the crypto test
2019-04-16 22:38:26 +08:00
958e00e231 fmt test code file 2019-04-16 22:14:42 +08:00
ab187d225d edit yml 2019-04-16 22:12:29 +08:00
9f5711d41d edit testpackage's name 2019-04-16 22:11:44 +08:00
cdb2773127 Merge branch 'master' into test_gfile 2019-04-16 18:35:25 +08:00
d12532ccc1 set a definite value for an assertion #66 2019-04-16 15:49:42 +08:00
53e9f05a10 make the details perfect #66 2019-04-16 15:49:42 +08:00
60e7ab95bc increase coverage for crypto #66 2019-04-16 15:49:42 +08:00
ae552e2b46 increase coverage for crypto #66 2019-04-16 15:49:42 +08:00
145b52f343 increase coverage for crypto #66 2019-04-16 15:49:42 +08:00
57b8fac0d5 add the crypto test 2019-04-16 15:49:42 +08:00
Jay
16a4a5ba46 Gmap测试修改 2019-04-16 14:28:25 +08:00
3d37c83532 Update TODO.MD 2019-04-15 23:49:47 +08:00
b02fa701b8 add test_gfile test of ci 2019-04-15 23:31:17 +08:00
37b7c82c64 Merge branch 'master' into test_gfile 2019-04-15 23:30:21 +08:00
1deb27df06 Merge branch 'master' of https://github.com/gogf/gf into gogf-master 2019-04-15 23:24:38 +08:00
84ed3d5767 add move and rename testfun 2019-04-15 23:14:48 +08:00
fc909a3db2 travis ci updates 2019-04-15 23:06:42 +08:00
2f7d4cd80d fix issue in Add function for gtype.Float32/Float64 2019-04-15 22:55:12 +08:00
03ccbf3613 edit TestStat fun 2019-04-15 22:52:19 +08:00
1868465319 edit TestTruncate fun 2019-04-15 22:47:09 +08:00
ff473e2fcc edit ci yml 2019-04-15 22:17:58 +08:00
a3c38eec86 edit ci yml 2019-04-15 22:12:28 +08:00
2015c847e8 fmt test file 2019-04-15 18:14:38 +08:00
1b583ed984 pull ci yml 2019-04-15 15:41:51 +08:00
11191c746a edit test 2019-04-15 15:34:35 +08:00
abedd3c5bf edit test 2019-04-15 15:30:44 +08:00
501ba5135b edit test 2019-04-15 15:27:49 +08:00
a76c98c348 暂时去掉search测试 2019-04-15 14:01:09 +08:00
9dcdc1a339 edit ci yml 2019-04-15 13:47:25 +08:00
6d1f386203 edit travis.yml 2019-04-15 11:49:07 +08:00
ff9bbf0a49 edit test 2019-04-15 11:41:05 +08:00
66e24c8d40 Merge branch 'master' into test_gfile 2019-04-15 11:26:11 +08:00
34f117c631 Merge pull request #9 from gogf/master
日常更新
2019-04-15 11:22:16 +08:00
21f2f16889 go test 2019-04-14 23:21:03 +08:00
f6fa7c422d 测试小调整 2019-04-14 22:41:11 +08:00
6903b84bb6 调整测试文件生成目录 2019-04-13 23:00:57 +08:00
0978b8fb4f 初步完成测试代码重构 2019-04-13 18:27:01 +08:00
d014583e88 重构测试代码中 2019-04-13 13:14:30 +08:00
a747f51b9d 重构测试代码 2019-04-12 18:15:31 +08:00
8300885ab6 Merge pull request #5 from gogf/master
update gtest
2019-04-12 17:59:02 +08:00
Jay
b489eed4ef 新增测试方法 2019-04-12 10:59:05 +08:00
0b57771d76 用fmt格式化代码 2019-04-12 09:47:55 +08:00
8a32a8271c fix issue in empty router group error of ghttp.Server 2019-04-12 00:19:15 +08:00
c3e716dafd Merge branch 'master' of https://github.com/gogf/gf 2019-04-11 23:07:28 +08:00
119a11eb8d README updates 2019-04-11 23:07:22 +08:00
e464d14842 Merge pull request #85 from qq976739120/gmap-test
Gmap 测试添加
2019-04-11 21:59:00 +08:00
Jay
905f46359a Gmap string-string 类型测试提交 2019-04-11 17:50:26 +08:00
Jay
8e7bf1f908 bug 修复 2019-04-11 17:46:19 +08:00
Jay
3a1524ae6d Gmap 初测完成 2019-04-11 17:33:52 +08:00
5c04befea3 edit ci yml file 2019-04-11 16:39:20 +08:00
ae3584cdff edit ci 2019-04-11 16:27:47 +08:00
a60578c82c edit ci 2019-04-11 16:23:29 +08:00
74558a500c edit c1 2019-04-11 16:17:44 +08:00
d963d8c8c1 testci 2019-04-11 16:13:56 +08:00
853892c24f config ci 2019-04-11 16:05:33 +08:00
641f939e3d edit test 2019-04-11 15:57:48 +08:00
af77504eaf 修改测试 2019-04-11 15:49:00 +08:00
62649d6468 config ci 2019-04-11 15:22:29 +08:00
bb914e605e 配置ci 2019-04-11 15:08:17 +08:00
62ee88bbfa Update .travis.yml 2019-04-11 14:45:47 +08:00
99c964bb4a 添加ci配置文 2019-04-11 11:02:43 +08:00
09ceaef3e9 配置ci 2019-04-11 10:59:53 +08:00
a82900af55 对分支添加ci检查 2019-04-11 10:03:47 +08:00
5ef589f31a Merge pull request #8 from gogf/master
合并前更新。
2019-04-11 09:56:01 +08:00
cd719f134d 测试率达到82.9% 2019-04-11 09:47:51 +08:00
f69eb219b5 version updates 2019-04-11 09:43:17 +08:00
0ffe17ee3d revert SearchArray/InArray functions for gstr 2019-04-11 09:26:52 +08:00
4ac647a215 gcmd updates 2019-04-11 09:05:27 +08:00
872536c035 Merge branch 'master' of https://github.com/gogf/gf 2019-04-10 23:09:59 +08:00
c7a6a6fff0 improve grand.Intn; README updates 2019-04-10 23:09:38 +08:00
2534655bc8 测试覆盖率达到76.3% 2019-04-10 22:16:16 +08:00
0cb82d70fd Merge pull request #96 from aloncn/master
update unit test for gring
2019-04-10 21:42:25 +08:00
acac5a2ad6 version updates 2019-04-10 18:33:51 +08:00
9ec15ad2ca fix issue in repeated rand value of grand.Intn 2019-04-10 18:33:12 +08:00
a9b7d56d0b 测试覆盖率达到63.1% 2019-04-10 17:54:28 +08:00
e9ca1eb538 完成第一遍功能测试 2019-04-10 16:45:33 +08:00
0532800895 添加测试代码 2019-04-10 16:22:29 +08:00
ad50ca6e60 update unit test for gring 2019-04-10 14:26:21 +08:00
c1ad999c25 添加测试 2019-04-10 13:45:42 +08:00
4accd1264d 添加内容测试 2019-04-10 13:39:36 +08:00
e1f3da3aa8 去掉mod设置的版本信息 2019-04-10 11:31:24 +08:00
4bf9a7950b Merge branch 'master' into test_gfile 2019-04-10 11:28:57 +08:00
8285c31bf1 Update unit test for gring 2019-04-10 10:44:40 +08:00
230be66fa9 update unit test for gring 2019-04-10 10:13:56 +08:00
2c53f934c9 Merge branch 'master' of https://github.com/gogf/gf 2019-04-10 09:50:29 +08:00
08785cb272 update unit test cases of gjson 2019-04-10 08:54:49 +08:00
bd0207c938 update unit test for gring 2019-04-10 01:16:45 +08:00
6fad737617 update unit test cases of gjson/gparser 2019-04-10 01:00:51 +08:00
af4148d985 Merge branch 'master' of github.com:aloncn/gf 2019-04-10 00:37:22 +08:00
922eaf4d42 Update unit test for gring 2019-04-10 00:37:06 +08:00
4b5153950f Add unit test for gring 2019-04-10 00:37:06 +08:00
78010d2bd7 Update unit test for gring 2019-04-10 00:22:10 +08:00
6cc0017826 测试空的断言。 2019-04-09 23:25:20 +08:00
8a9131c3dc Merge pull request #7 from gogf/master
日常更新
2019-04-09 23:16:18 +08:00
85c2ed1bf2 improve nil checks for gtest.Assert* 2019-04-09 23:12:22 +08:00
aca1df634d 添加测试文件内容相关函数。 2019-04-09 23:11:04 +08:00
053a3c1a53 add unit test 2019-04-09 19:12:48 +08:00
429aa90e0d Merge branch 'master' of github.com:aloncn/gf 2019-04-09 18:42:16 +08:00
4f792b347d Add unit test for gring 2019-04-09 18:40:48 +08:00
468c315087 Merge pull request #4 from gogf/master
update 1.6
2019-04-09 17:36:40 +08:00
6d8ced21b9 Add unit test for gring 2019-04-09 17:27:58 +08:00
b3d5fc149e add unit test 2019-04-09 17:27:11 +08:00
Jay
277b7a4536 测试修改 2019-04-09 15:06:12 +08:00
43886511b9 add unit test 2019-04-09 12:28:21 +08:00
8b3eb5b02d version&release updates 2019-04-09 00:15:48 +08:00
054ef87886 继续添加文件测试. 2019-04-08 23:13:28 +08:00
Jay
39c65d9e9a 测试添加 2019-04-08 17:49:15 +08:00
Jay
b97bbbfa3d 测试添加 2019-04-08 17:32:07 +08:00
Jay
ace6ba8096 Gmap 测试添加 2019-04-08 17:02:57 +08:00
a2b87d84e9 添加gfile测试代码,10% 2019-04-07 22:46:14 +08:00
b90d61b27c README updates 2019-04-07 22:03:04 +08:00
85606e3e7e README updates 2019-04-07 21:59:28 +08:00
1fc85d49bd gtime updates; README updates 2019-04-07 21:49:24 +08:00
a5cfb4e638 Merge pull request #82 from hailaz/master
Add gtime unit test
2019-04-07 21:27:29 +08:00
38754bf062 Merge pull request #78 from youyixiao/youyixiao_gogf
gregex_unit_test
2019-04-07 21:23:03 +08:00
f1818ed2ff 补充gtime_format的覆盖测试。 2019-04-06 23:53:06 +08:00
352ad17715 避免pr时造成gtime文件的更改。 2019-04-06 22:56:47 +08:00
e50b8d9632 移除gtime unit test中注释掉的函数。 2019-04-06 22:50:37 +08:00
2107061a46 Add gtime unit test 2019-04-06 22:48:47 +08:00
61f57b4895 update comments of gstr 2019-04-06 21:31:01 +08:00
d34273abff 同步master 2019-04-05 23:53:43 +08:00
0aff0f0362 同步master 2019-04-05 23:49:20 +08:00
68949b69bc update example of gtime 2019-04-05 23:31:14 +08:00
c56c77d3a1 fix issue in G&j char format 2019-04-05 23:13:47 +08:00
dc82ce395a comment updates for gcfg 2019-04-05 00:23:59 +08:00
fd63a2209b 增加单元测试 2019-04-04 23:31:17 +08:00
2a29483456 comment fix 2019-04-04 23:24:27 +08:00
4f10562980 remove limit for gdb.Model.One 2019-04-04 23:22:09 +08:00
a26ec37f59 Merge pull request #3 from gogf/master
更新
2019-04-04 23:17:09 +08:00
779ad93bcb 参数为nil时的特殊处理 2019-04-04 23:02:00 +08:00
1ec0219473 add gbinary unit tests 2019-04-04 23:00:21 +08:00
6863928b06 !18 限制下查询单条记录查询条数
Merge pull request !18 from 一墨染尽青衣颜/master
2019-04-04 22:55:13 +08:00
aa4dca11f0 fix issue in gtime; update comment for g/gtest 2019-04-04 22:52:56 +08:00
dc6ab820ce 修复解析日期函数(parseDateStr)对"02.jan.2006"格式日期解析异常的问题。
更正函数(isNumeric)的注释。
2019-04-04 17:57:17 +08:00
be0fa4d60b gregex_unit_test 2019-04-04 17:45:04 +08:00
a86d2272af gregex_unit_test 2019-04-04 16:15:50 +08:00
61a67892ac Merge pull request #6 from gogf/master
同步最新代码
2019-04-04 11:37:58 +08:00
388d5954cb Merge pull request #2 from gogf/master
update unit test
2019-04-04 10:56:01 +08:00
08550d413e update unit test cases for gcfg 2019-04-04 09:18:43 +08:00
b89294561b add gmap.SetIfNotExistFunc/SetIfNotExistFuncLock for gmap; update comment of gmap; update instance feature of gcfg 2019-04-03 23:39:31 +08:00
20977558cc Merge pull request #1 from gogf/master
更新代码
2019-04-03 16:47:16 +08:00
f6aafc1d6b Merge pull request #5 from gogf/master
更新数据库操作
2019-03-18 10:00:23 +08:00
9014325a7c test 2019-03-12 10:54:38 +08:00
fc11856a28 Merge pull request #4 from gogf/master
日常更新
2019-03-11 17:43:06 +08:00
b0726b9733 Merge pull request #3 from gogf/master
日常更新
2019-03-10 22:38:15 +08:00
4e4ea25e7f Merge pull request #2 from gogf/master
第一次同步
2019-03-07 17:35:22 +08:00
193 changed files with 8722 additions and 8536 deletions

View File

@ -1,7 +1,6 @@
language: go
go:
- "1.10.x"
- "1.11.x"
- "1.12.x"
@ -11,29 +10,29 @@ branches:
- develop
env:
- GO111MODULE=on
- GO111MODULE=on
services:
- mysql
- redis-server
- 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)

View File

@ -5,9 +5,8 @@
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/gogf/gf)
[![Release](https://img.shields.io/github/release/gogf/gf.svg?style=flat)](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,30 +64,38 @@ func main() {
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Contributors
- [johng](https://gitee.com/johng)
- [zhaopengme](https://github.com/zhaopengme)
- [wenzi1](https://gitee.com/wenzi1)
- [zseeker](https://gitee.com/zseeker)
- [ymrjqyy](https://gitee.com/ymrjqyy)
- [aloncn](https://github.com/aloncn)
- [chenyang351](https://github.com/chenyang351)
- [wxkj](https://gitee.com/wxkj)
- [wxkj001](https://github.com/wxkj001)
- [zhangjinfu](https://gitee.com/zhangjinfu)
- [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
- [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)
- [hailaz](https://gitee.com/hailaz)
- [mg91](https://gitee.com/mg91)
- [wxkj](https://gitee.com/wxkj)
- [pibigstar](https://github.com/pibigstar)
- [flyke-xu](https://gitee.com/flyke-xu)

View File

@ -5,9 +5,8 @@
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/gogf/gf)
[![Release](https://img.shields.io/github/release/gogf/gf.svg?style=flat)](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
@ -87,25 +85,32 @@ func main() {
# 贡献者
- [johng](https://gitee.com/johng)
- [zhaopengme](https://github.com/zhaopengme)
- [wenzi1](https://gitee.com/wenzi1)
- [zseeker](https://gitee.com/zseeker)
- [ymrjqyy](https://gitee.com/ymrjqyy)
- [aloncn](https://github.com/aloncn)
- [chenyang351](https://github.com/chenyang351)
- [wxkj](https://gitee.com/wxkj)
- [wxkj001](https://github.com/wxkj001)
- [zhangjinfu](https://gitee.com/zhangjinfu)
- [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)
# 捐赠者
- [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)
- [hailaz](https://gitee.com/hailaz)
- [mg91](https://gitee.com/mg91)
- [wxkj](https://gitee.com/wxkj)
- [pibigstar](https://github.com/pibigstar)
- [flyke-xu](https://gitee.com/flyke-xu)

View File

@ -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)
## 新特性

View File

@ -40,8 +40,11 @@
1. 改进gproc进程间通信处理逻辑提高稳定性以应对进程间大批量的数据发送/接收;
1. ghttp的热重启的本地进程端口监听在不使用该特性时默认关闭掉
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类型的转换

View File

@ -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 container

View File

@ -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

View File

@ -8,7 +8,8 @@ package garray
import (
"bytes"
"github.com/gogf/gf/g/internal/rwmutex"
"fmt"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
@ -16,24 +17,20 @@ import (
)
type IntArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []int // 底层数组
mu *rwmutex.RWMutex
array []int
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// NewIntArray creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArray(unsafe...bool) *IntArray {
return NewIntArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// NewIntArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
@ -41,11 +38,9 @@ func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
}
}
// Create an array with given slice <array>.
// NewIntArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
@ -53,11 +48,9 @@ func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
}
}
// Create an array from a copy of given slice <array>.
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
newArray := make([]int, len(array))
copy(newArray, array)
@ -67,9 +60,8 @@ func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *IntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -77,9 +69,7 @@ func (a *IntArray) Get(index int) int {
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界。
// Set sets value to specified index.
func (a *IntArray) Set(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -87,9 +77,7 @@ func (a *IntArray) Set(index int, value int) *IntArray {
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *IntArray) SetArray(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -97,9 +85,7 @@ func (a *IntArray) SetArray(array []int) *IntArray {
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
// Replace replaces the array items by given <array> from the beginning of array.
func (a *IntArray) Replace(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -113,9 +99,7 @@ func (a *IntArray) Replace(array []int) *IntArray {
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和。
// Sum returns the sum of values in an array.
func (a *IntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -125,11 +109,9 @@ func (a *IntArray) Sum() (sum int) {
return
}
// Sort the array in increasing order.
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order
//
// 将数组排序(默认从低到高).
func (a *IntArray) Sort(reverse...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -146,9 +128,7 @@ func (a *IntArray) Sort(reverse...bool) *IntArray {
return a
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
// SortFunc sorts the array by custom function <less>.
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -158,9 +138,7 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertBefore inserts the <value> to the front of <index>.
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -170,9 +148,7 @@ func (a *IntArray) InsertBefore(index int, value int) *IntArray {
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertAfter inserts the <value> to the back of <index>.
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -182,13 +158,11 @@ func (a *IntArray) InsertAfter(index int, value int) *IntArray {
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *IntArray) Remove(index int) int {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -198,15 +172,15 @@ func (a *IntArray) Remove(index int) int {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PushLeft pushes one or multiple items to the beginning of array.
func (a *IntArray) PushLeft(value...int) *IntArray {
a.mu.Lock()
a.array = append(value, a.array...)
@ -214,9 +188,8 @@ func (a *IntArray) PushLeft(value...int) *IntArray {
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *IntArray) PushRight(value...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -224,9 +197,7 @@ func (a *IntArray) PushRight(value...int) *IntArray {
return a
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
// PopLeft pops and returns an item from the beginning of array.
func (a *IntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -235,9 +206,7 @@ func (a *IntArray) PopLeft() int {
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
// PopRight pops and returns an item from the end of array.
func (a *IntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -247,16 +216,12 @@ func (a *IntArray) PopRight() int {
return value
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *IntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *IntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -272,9 +237,7 @@ func (a *IntArray) PopRands(size int) []int {
return array
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项。
// PopLefts pops and returns <size> items from the beginning of array.
func (a *IntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -287,9 +250,7 @@ func (a *IntArray) PopLefts(size int) []int {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *IntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -302,11 +263,9 @@ func (a *IntArray) PopRights(size int) []int {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *IntArray) Range(start, end int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -333,8 +292,6 @@ func (a *IntArray) Range(start, end int) []int {
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *IntArray) Append(value...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -342,9 +299,7 @@ func (a *IntArray) Append(value...int) *IntArray {
return a
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *IntArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -352,11 +307,9 @@ func (a *IntArray) Len() int {
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *IntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
@ -370,9 +323,7 @@ func (a *IntArray) Slice() []int {
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *IntArray) Clone() (newArray *IntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
@ -381,9 +332,7 @@ func (a *IntArray) Clone() (newArray *IntArray) {
return NewIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
// Clear deletes all items of current array.
func (a *IntArray) Clear() *IntArray {
a.mu.Lock()
if len(a.array) > 0 {
@ -393,17 +342,13 @@ func (a *IntArray) Clear() *IntArray {
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
// Contains checks whether a value exists in the array.
func (a *IntArray) Contains(value int) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *IntArray) Search(value int) int {
if len(a.array) == 0 {
return -1
@ -421,9 +366,7 @@ func (a *IntArray) Search(value int) int {
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项。
// Unique uniques the array, clear repeated items.
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
@ -437,9 +380,7 @@ func (a *IntArray) Unique() *IntArray {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing by callback function <f>.
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -447,9 +388,7 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading by callback function <f>.
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
a.mu.RLock()
defer a.mu.RUnlock()
@ -457,11 +396,10 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// but Merge supports more parameter types.
func (a *IntArray) Merge(array interface{}) *IntArray {
switch v := array.(type) {
case *Array: a.Append(gconv.Ints(v.Slice())...)
@ -476,10 +414,8 @@ func (a *IntArray) Merge(array interface{}) *IntArray {
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the startIndex parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -496,10 +432,9 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *IntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
@ -520,14 +455,10 @@ func (a *IntArray) Chunk(size int) [][]int {
return n
}
// Pad array to the specified length with a value.
// Pad pads array to the specified length with <value>.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of size is less than or equal to the length of the array
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *IntArray) Pad(size int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -551,12 +482,9 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
return a
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *IntArray) SubSlice(offset, size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -575,18 +503,14 @@ func (a *IntArray) SubSlice(offset, size int) []int {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *IntArray) Rand() int {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *IntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -603,9 +527,7 @@ func (a *IntArray) Rands(size int) []int {
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
// Shuffle randomly shuffles the array.
func (a *IntArray) Shuffle() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -615,9 +537,7 @@ func (a *IntArray) Shuffle() *IntArray {
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
// Reverse makes array with elements in reverse order.
func (a *IntArray) Reverse() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -627,9 +547,7 @@ func (a *IntArray) Reverse() *IntArray {
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *IntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -641,4 +559,22 @@ func (a *IntArray) Join(glue string) string {
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *IntArray) CountValues() map[int]int {
m := make(map[int]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *IntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -17,15 +17,13 @@ import (
)
type Array struct {
mu *rwmutex.RWMutex // 互斥锁
array []interface{} // 底层数组
mu *rwmutex.RWMutex
array []interface{}
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// New creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
@ -35,11 +33,9 @@ func NewArray(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// NewArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewArraySize(size int, cap int, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
@ -57,11 +53,9 @@ func NewFromCopy(array []interface{}, unsafe...bool) *Array {
return NewArrayFromCopy(array, unsafe...)
}
// Create an array with given slice <array>.
// NewArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
@ -69,11 +63,9 @@ func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
}
}
// Create an array from a copy of given slice <array>.
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
newArray := make([]interface{}, len(array))
copy(newArray, array)
@ -83,9 +75,8 @@ func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *Array) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -93,9 +84,7 @@ func (a *Array) Get(index int) interface{} {
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界
// Set sets value to specified index.
func (a *Array) Set(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -103,9 +92,7 @@ func (a *Array) Set(index int, value interface{}) *Array {
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *Array) SetArray(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -113,9 +100,7 @@ func (a *Array) SetArray(array []interface{}) *Array {
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
// Replace replaces the array items by given <array> from the beginning of array.
func (a *Array) Replace(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -129,9 +114,7 @@ func (a *Array) Replace(array []interface{}) *Array {
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
// Sum returns the sum of values in an array.
func (a *Array) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -141,9 +124,7 @@ func (a *Array) Sum() (sum int) {
return
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
// SortFunc sorts the array by custom function <less>.
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -153,9 +134,7 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertBefore inserts the <value> to the front of <index>.
func (a *Array) InsertBefore(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -165,9 +144,7 @@ func (a *Array) InsertBefore(index int, value interface{}) *Array {
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertAfter inserts the <value> to the back of <index>.
func (a *Array) InsertAfter(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -177,13 +154,11 @@ func (a *Array) InsertAfter(index int, value interface{}) *Array {
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *Array) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency。
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -193,15 +168,15 @@ func (a *Array) Remove(index int) interface{} {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PushLeft pushes one or multiple items to the beginning of array.
func (a *Array) PushLeft(value...interface{}) *Array {
a.mu.Lock()
a.array = append(value, a.array...)
@ -209,9 +184,8 @@ func (a *Array) PushLeft(value...interface{}) *Array {
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *Array) PushRight(value...interface{}) *Array {
a.mu.Lock()
a.array = append(a.array, value...)
@ -219,16 +193,12 @@ func (a *Array) PushRight(value...interface{}) *Array {
return a
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *Array) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *Array) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -244,9 +214,7 @@ func (a *Array) PopRands(size int) []interface{} {
return array
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
// PopLeft pops and returns an item from the beginning of array.
func (a *Array) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -255,9 +223,7 @@ func (a *Array) PopLeft() interface{} {
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
// PopRight pops and returns an item from the end of array.
func (a *Array) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -267,9 +233,7 @@ func (a *Array) PopRight() interface{} {
return value
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
// PopLefts pops and returns <size> items from the beginning of array.
func (a *Array) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -282,9 +246,7 @@ func (a *Array) PopLefts(size int) []interface{} {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *Array) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -297,11 +259,9 @@ func (a *Array) PopRights(size int) []interface{} {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *Array) Range(start, end int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -328,16 +288,12 @@ func (a *Array) Range(start, end int) []interface{} {
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *Array) Append(value...interface{}) *Array {
a.PushRight(value...)
return a
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *Array) Len() int {
a.mu.RLock()
length := len(a.array)
@ -345,11 +301,9 @@ func (a *Array) Len() int {
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *Array) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
@ -363,9 +317,7 @@ func (a *Array) Slice() []interface{} {
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *Array) Clone() (newArray *Array) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
@ -374,9 +326,7 @@ func (a *Array) Clone() (newArray *Array) {
return NewArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组
// Clear deletes all items of current array.
func (a *Array) Clear() *Array {
a.mu.Lock()
if len(a.array) > 0 {
@ -386,16 +336,13 @@ func (a *Array) Clear() *Array {
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在
// Contains checks whether a value exists in the array.
func (a *Array) Contains(value interface{}) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) int {
if len(a.array) == 0 {
return -1
@ -413,9 +360,7 @@ func (a *Array) Search(value interface{}) int {
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项
// Unique uniques the array, clear repeated items.
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
@ -429,9 +374,7 @@ func (a *Array) Unique() *Array {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作
// LockFunc locks writing by callback function <f>.
func (a *Array) LockFunc(f func(array []interface{})) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -439,9 +382,7 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array {
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作
// RLockFunc locks reading by callback function <f>.
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
a.mu.RLock()
defer a.mu.RUnlock()
@ -449,11 +390,10 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// but Merge supports more parameter types.
func (a *Array) Merge(array interface{}) *Array {
switch v := array.(type) {
case *Array: a.Append(gconv.Interfaces(v.Slice())...)
@ -468,10 +408,8 @@ func (a *Array) Merge(array interface{}) *Array {
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the start_index parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -488,10 +426,9 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *Array) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
@ -512,15 +449,10 @@ func (a *Array) Chunk(size int) [][]interface{} {
return n
}
// Pad array to the specified length with a value.
// If size is positive then the array is padded on the right,
// if it's negative then on the left.
// If the absolute value of size is less than or equal to the length of the array
// Pad pads array to the specified length with <value>.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *Array) Pad(size int, val interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -544,10 +476,9 @@ func (a *Array) Pad(size int, val interface{}) *Array {
return a
}
// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *Array) SubSlice(offset, size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -566,18 +497,14 @@ func (a *Array) SubSlice(offset, size int) []interface{} {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *Array) Rand() interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *Array) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -594,9 +521,7 @@ func (a *Array) Rands(size int) []interface{} {
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
// Shuffle randomly shuffles the array.
func (a *Array) Shuffle() *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -606,9 +531,7 @@ func (a *Array) Shuffle() *Array {
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
// Reverse makes array with elements in reverse order.
func (a *Array) Reverse() *Array {
a.mu.Lock()
defer a.mu.Unlock()
@ -618,9 +541,7 @@ func (a *Array) Reverse() *Array {
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *Array) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -634,9 +555,7 @@ func (a *Array) Join(glue string) string {
return buffer.String()
}
// Counts all the values of an array.
//
// 统计数组中所有的值出现的次数.
// CountValues counts the number of occurrences of all values in the array.
func (a *Array) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
a.mu.RLock()
@ -648,8 +567,6 @@ func (a *Array) CountValues() map[interface{}]int {
}
// String returns current array as a string.
//
// 将当前数组转换为字符串返回。
func (a *Array) String() string {
a.mu.RLock()
defer a.mu.RUnlock()

View File

@ -8,7 +8,8 @@ package garray
import (
"bytes"
"github.com/gogf/gf/g/internal/rwmutex"
"fmt"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
@ -17,24 +18,20 @@ import (
)
type StringArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []string // 底层数组
mu *rwmutex.RWMutex
array []string
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// NewStringArray creates and returns an empty array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArray(unsafe...bool) *StringArray {
return NewStringArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// NewStringArraySize create and returns an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
return &StringArray{
mu : rwmutex.New(unsafe...),
@ -42,11 +39,9 @@ func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
}
}
// Create an array with given slice <array>.
// NewStringArrayFrom creates and returns an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
return &StringArray {
mu : rwmutex.New(unsafe...),
@ -54,11 +49,9 @@ func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
}
}
// Create an array from a copy of given slice <array>.
// NewStringArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
newArray := make([]string, len(array))
copy(newArray, array)
@ -68,9 +61,8 @@ func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *StringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -78,9 +70,7 @@ func (a *StringArray) Get(index int) string {
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界。
// Set sets value to specified index.
func (a *StringArray) Set(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -88,9 +78,7 @@ func (a *StringArray) Set(index int, value string) *StringArray {
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *StringArray) SetArray(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -98,9 +86,7 @@ func (a *StringArray) SetArray(array []string) *StringArray {
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
// Replace replaces the array items by given <array> from the beginning of array.
func (a *StringArray) Replace(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -114,9 +100,7 @@ func (a *StringArray) Replace(array []string) *StringArray {
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
// Sum returns the sum of values in an array.
func (a *StringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -126,11 +110,9 @@ func (a *StringArray) Sum() (sum int) {
return
}
// Sort the array in increasing order.
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order
//
// 将数组排序(默认从低到高).
func (a *StringArray) Sort(reverse...bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -147,9 +129,7 @@ func (a *StringArray) Sort(reverse...bool) *StringArray {
return a
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
// SortFunc sorts the array by custom function <less>.
func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -159,9 +139,7 @@ func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertBefore inserts the <value> to the front of <index>.
func (a *StringArray) InsertBefore(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -171,9 +149,7 @@ func (a *StringArray) InsertBefore(index int, value string) *StringArray {
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
// InsertAfter inserts the <value> to the back of <index>.
func (a *StringArray) InsertAfter(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -183,13 +159,11 @@ func (a *StringArray) InsertAfter(index int, value string) *StringArray {
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *StringArray) Remove(index int) string {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency。
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -199,15 +173,15 @@ func (a *StringArray) Remove(index int) string {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PushLeft pushes one or multiple items to the beginning of array.
func (a *StringArray) PushLeft(value...string) *StringArray {
a.mu.Lock()
a.array = append(value, a.array...)
@ -215,9 +189,8 @@ func (a *StringArray) PushLeft(value...string) *StringArray {
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *StringArray) PushRight(value...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -225,9 +198,7 @@ func (a *StringArray) PushRight(value...string) *StringArray {
return a
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
// PopLeft pops and returns an item from the beginning of array.
func (a *StringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -236,9 +207,7 @@ func (a *StringArray) PopLeft() string {
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
// PopRight pops and returns an item from the end of array.
func (a *StringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -248,16 +217,12 @@ func (a *StringArray) PopRight() string {
return value
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *StringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *StringArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -273,9 +238,7 @@ func (a *StringArray) PopRands(size int) []string {
return array
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
// PopLefts pops and returns <size> items from the beginning of array.
func (a *StringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -288,9 +251,7 @@ func (a *StringArray) PopLefts(size int) []string {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *StringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -303,11 +264,9 @@ func (a *StringArray) PopRights(size int) []string {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *StringArray) Range(start, end int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -334,8 +293,6 @@ func (a *StringArray) Range(start, end int) []string {
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *StringArray) Append(value...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -343,9 +300,7 @@ func (a *StringArray) Append(value...string) *StringArray {
return a
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *StringArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -353,11 +308,9 @@ func (a *StringArray) Len() int {
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *StringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
@ -371,9 +324,7 @@ func (a *StringArray) Slice() []string {
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *StringArray) Clone() (newArray *StringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
@ -382,9 +333,7 @@ func (a *StringArray) Clone() (newArray *StringArray) {
return NewStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
// Clear deletes all items of current array.
func (a *StringArray) Clear() *StringArray {
a.mu.Lock()
if len(a.array) > 0 {
@ -394,16 +343,13 @@ func (a *StringArray) Clear() *StringArray {
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
// Contains checks whether a value exists in the array.
func (a *StringArray) Contains(value string) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *StringArray) Search(value string) int {
if len(a.array) == 0 {
return -1
@ -420,9 +366,7 @@ func (a *StringArray) Search(value string) int {
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项。
// Unique uniques the array, clear repeated items.
func (a *StringArray) Unique() *StringArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
@ -436,9 +380,7 @@ func (a *StringArray) Unique() *StringArray {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing by callback function <f>.
func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -446,9 +388,7 @@ func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading by callback function <f>.
func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
a.mu.RLock()
defer a.mu.RUnlock()
@ -456,11 +396,10 @@ func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// but Merge supports more parameter types.
func (a *StringArray) Merge(array interface{}) *StringArray {
switch v := array.(type) {
case *Array: a.Append(gconv.Strings(v.Slice())...)
@ -475,10 +414,8 @@ func (a *StringArray) Merge(array interface{}) *StringArray {
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the start_index parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -495,10 +432,9 @@ func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *StringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
@ -519,15 +455,10 @@ func (a *StringArray) Chunk(size int) [][]string {
return n
}
// Pad array to the specified length with a value.
// If size is positive then the array is padded on the right,
// if it's negative then on the left.
// If the absolute value of size is less than or equal to the length of the array
// Pad pads array to the specified length with <value>.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *StringArray) Pad(size int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -551,12 +482,9 @@ func (a *StringArray) Pad(size int, value string) *StringArray {
return a
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *StringArray) SubSlice(offset, size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -575,18 +503,14 @@ func (a *StringArray) SubSlice(offset, size int) []string {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *StringArray) Rand() string {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *StringArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -603,9 +527,7 @@ func (a *StringArray) Rands(size int) []string {
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
// Shuffle randomly shuffles the array.
func (a *StringArray) Shuffle() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -615,9 +537,7 @@ func (a *StringArray) Shuffle() *StringArray {
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
// Reverse makes array with elements in reverse order.
func (a *StringArray) Reverse() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -627,9 +547,7 @@ func (a *StringArray) Reverse() *StringArray {
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *StringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -643,3 +561,20 @@ func (a *StringArray) Join(glue string) string {
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *StringArray) CountValues() map[string]int {
m := make(map[string]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *StringArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -8,7 +8,8 @@ package garray
import (
"bytes"
"github.com/gogf/gf/g/container/gtype"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
@ -16,28 +17,24 @@ import (
"sort"
)
// 默认按照从小到大进行排序
// It's using increasing order in default.
type SortedIntArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []int // 底层数组
unique *gtype.Bool // 是否要求不能重复(默认false)
compareFunc func(v1, v2 int) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
mu *rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// Create an empty sorted array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// NewSortedIntArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArray(unsafe...bool) *SortedIntArray {
return NewSortedIntArraySize(0, unsafe...)
}
// Create a sorted array with given size and cap.
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
return &SortedIntArray {
mu : rwmutex.New(unsafe...),
@ -55,11 +52,9 @@ func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
}
}
// Create an array with given slice <array>.
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, unsafe...)
a.array = array
@ -67,11 +62,9 @@ func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
return a
}
// Create an array from a copy of given slice <array>.
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
newArray := make([]int, len(array))
copy(newArray, array)
@ -81,9 +74,7 @@ func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
}
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -92,9 +83,9 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
return a
}
// Sort the array in increasing order.
//
// 将数组排序(默认从低到高).
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -102,9 +93,7 @@ func (a *SortedIntArray) Sort() *SortedIntArray {
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedIntArray) Add(values...int) *SortedIntArray {
if len(values) == 0 {
return a
@ -120,7 +109,6 @@ func (a *SortedIntArray) Add(values...int) *SortedIntArray {
a.array = append(a.array, value)
continue
}
// 加到指定索引后面
if cmp > 0 {
index++
}
@ -131,9 +119,8 @@ func (a *SortedIntArray) Add(values...int) *SortedIntArray {
return a
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedIntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -141,13 +128,11 @@ func (a *SortedIntArray) Get(index int) int {
return value
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *SortedIntArray) Remove(index int) int {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -157,15 +142,15 @@ func (a *SortedIntArray) Remove(index int) int {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedIntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -174,9 +159,7 @@ func (a *SortedIntArray) PopLeft() int {
return value
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
// PopRight pops and returns an item from the end of array.
func (a *SortedIntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -186,16 +169,12 @@ func (a *SortedIntArray) PopRight() int {
return value
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *SortedIntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedIntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -211,9 +190,7 @@ func (a *SortedIntArray) PopRands(size int) []int {
return array
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedIntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -226,9 +203,7 @@ func (a *SortedIntArray) PopLefts(size int) []int {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *SortedIntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
@ -241,11 +216,9 @@ func (a *SortedIntArray) PopRights(size int) []int {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedIntArray) Range(start, end int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -271,9 +244,7 @@ func (a *SortedIntArray) Range(start, end int) []int {
return array
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *SortedIntArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -281,9 +252,7 @@ func (a *SortedIntArray) Len() int {
return length
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和。
// Sum returns the sum of values in an array.
func (a *SortedIntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -293,11 +262,9 @@ func (a *SortedIntArray) Sum() (sum int) {
return
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedIntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
@ -311,24 +278,19 @@ func (a *SortedIntArray) Slice() []int {
return array
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
// Contains checks whether a value exists in the array.
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) == 0
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedIntArray) Search(value int) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -354,11 +316,9 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
return mid, cmp
}
// Set unique mark to the array,
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
//
// 设置是否允许数组唯一.
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
@ -368,9 +328,7 @@ func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
return a
}
// Do unique check, remove all repeated items.
//
// 清理数组中重复的元素项.
// Unique uniques the array, clear repeated items.
func (a *SortedIntArray) Unique() *SortedIntArray {
a.mu.Lock()
i := 0
@ -388,9 +346,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
return a
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
@ -399,9 +355,7 @@ func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
// Clear deletes all items of current array.
func (a *SortedIntArray) Clear() *SortedIntArray {
a.mu.Lock()
if len(a.array) > 0 {
@ -411,9 +365,7 @@ func (a *SortedIntArray) Clear() *SortedIntArray {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing by callback function <f>.
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -421,9 +373,7 @@ func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading by callback function <f>.
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
a.mu.RLock()
defer a.mu.RUnlock()
@ -431,11 +381,10 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// The difference between Merge and Add is Add supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Ints(v.Slice())...)
@ -450,10 +399,9 @@ func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedIntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
@ -474,12 +422,9 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *SortedIntArray) SubSlice(offset, size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -498,18 +443,14 @@ func (a *SortedIntArray) SubSlice(offset, size int) []int {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *SortedIntArray) Rand() int {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedIntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -526,9 +467,7 @@ func (a *SortedIntArray) Rands(size int) []int {
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *SortedIntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -540,4 +479,22 @@ func (a *SortedIntArray) Join(glue string) string {
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedIntArray) CountValues() map[int]int {
m := make(map[int]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *SortedIntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -8,7 +8,8 @@ package garray
import (
"bytes"
"github.com/gogf/gf/g/container/gtype"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
@ -16,36 +17,27 @@ import (
"sort"
)
// 默认按照从小到大进行排序
// It's using increasing order in default.
type SortedArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []interface{} // 底层数组
unique *gtype.Bool // 是否要求不能重复
compareFunc func(v1, v2 interface{}) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
mu *rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// Create an empty sorted array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
// NewSortedArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety, which is false in default.
// The param <compareFunc> used to compare values to sort in array,
// if it returns value < 0, means v1 < v2;
// if it returns value = 0, means v1 = v2;
// if it returns value > 0, means v1 > v2;
//
// 创建一个空的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// 参数compareFunc用于指定排序方法
// 如果返回值 < 0, 表示 v1 < v2;
// 如果返回值 = 0, 表示 v1 = v2;
// 如果返回值 > 0, 表示 v1 > v2;
func NewSortedArray(compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return NewSortedArraySize(0, compareFunc, unsafe...)
}
// Create a sorted array with given size and cap.
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return &SortedArray{
mu : rwmutex.New(unsafe...),
@ -55,11 +47,9 @@ func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsaf
}
}
// Create an array with given slice <array>.
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, compareFunc, unsafe...)
a.array = array
@ -69,11 +59,9 @@ func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}
return a
}
// Create an array from a copy of given slice <array>.
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
newArray := make([]interface{}, len(array))
copy(newArray, array)
@ -83,9 +71,7 @@ func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
}
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -96,9 +82,9 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
return a
}
// Sort the array by comparing function.
//
// 将数组按照比较方法进行排序.
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -108,9 +94,7 @@ func (a *SortedArray) Sort() *SortedArray {
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedArray) Add(values...interface{}) *SortedArray {
if len(values) == 0 {
return a
@ -126,7 +110,6 @@ func (a *SortedArray) Add(values...interface{}) *SortedArray {
a.array = append(a.array, value)
continue
}
// 加到指定索引后面
if cmp > 0 {
index++
}
@ -137,9 +120,8 @@ func (a *SortedArray) Add(values...interface{}) *SortedArray {
return a
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedArray) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -147,13 +129,11 @@ func (a *SortedArray) Get(index int) interface{} {
return value
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *SortedArray) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -163,15 +143,15 @@ func (a *SortedArray) Remove(index int) interface{} {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedArray) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -180,9 +160,7 @@ func (a *SortedArray) PopLeft() interface{} {
return value
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
// PopRight pops and returns an item from the end of array.
func (a *SortedArray) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -192,16 +170,12 @@ func (a *SortedArray) PopRight() interface{} {
return value
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *SortedArray) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedArray) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -217,9 +191,7 @@ func (a *SortedArray) PopRands(size int) []interface{} {
return array
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -232,9 +204,7 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -247,11 +217,9 @@ func (a *SortedArray) PopRights(size int) []interface{} {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedArray) Range(start, end int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -277,9 +245,7 @@ func (a *SortedArray) Range(start, end int) []interface{} {
return array
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
// Sum returns the sum of values in an array.
func (a *SortedArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -289,9 +255,7 @@ func (a *SortedArray) Sum() (sum int) {
return
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *SortedArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -299,11 +263,9 @@ func (a *SortedArray) Len() int {
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedArray) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
@ -317,25 +279,19 @@ func (a *SortedArray) Slice() []interface{} {
return array
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
// Contains checks whether a value exists in the array.
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) == 0
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedArray) Search(value interface{}) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找。查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果。
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -361,11 +317,9 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
return mid, cmp
}
// Set unique mark to the array,
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
//
// 设置是否允许数组唯一.
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
@ -375,9 +329,7 @@ func (a *SortedArray) SetUnique(unique bool) *SortedArray {
return a
}
// Do unique check, remove all repeated items.
//
// 清理数组中重复的元素项.
// Unique uniques the array, clear repeated items.
func (a *SortedArray) Unique() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -395,9 +347,7 @@ func (a *SortedArray) Unique() *SortedArray {
return a
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *SortedArray) Clone() (newArray *SortedArray) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
@ -406,9 +356,7 @@ func (a *SortedArray) Clone() (newArray *SortedArray) {
return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
// Clear deletes all items of current array.
func (a *SortedArray) Clear() *SortedArray {
a.mu.Lock()
if len(a.array) > 0 {
@ -418,9 +366,7 @@ func (a *SortedArray) Clear() *SortedArray {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing by callback function <f>.
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -428,9 +374,7 @@ func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading by callback function <f>.
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
a.mu.RLock()
defer a.mu.RUnlock()
@ -438,11 +382,10 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// The difference between Merge and Add is Add supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedArray) Merge(array interface{}) *SortedArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Interfaces(v.Slice())...)
@ -457,10 +400,9 @@ func (a *SortedArray) Merge(array interface{}) *SortedArray {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedArray) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
@ -481,12 +423,9 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *SortedArray) SubSlice(offset, size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -505,18 +444,14 @@ func (a *SortedArray) SubSlice(offset, size int) []interface{} {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *SortedArray) Rand() interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedArray) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -533,9 +468,7 @@ func (a *SortedArray) Rands(size int) []interface{} {
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *SortedArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -547,4 +480,22 @@ func (a *SortedArray) Join(glue string) string {
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedArray) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *SortedArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -8,7 +8,8 @@ package garray
import (
"bytes"
"github.com/gogf/gf/g/container/gtype"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
@ -17,28 +18,24 @@ import (
"strings"
)
// 默认按照从小到大进行排序
// It's using increasing order in default.
type SortedStringArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []string // 底层数组
unique *gtype.Bool // 是否要求不能重复
compareFunc func(v1, v2 string) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
mu *rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
compareFunc func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// Create an empty sorted array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// NewSortedStringArray creates and returns an empty sorted array.
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArray(unsafe...bool) *SortedStringArray {
return NewSortedStringArraySize(0, unsafe...)
}
// Create a sorted array with given size and cap.
// NewSortedStringArraySize create and returns an sorted array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
return &SortedStringArray {
mu : rwmutex.New(unsafe...),
@ -50,11 +47,9 @@ func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
}
}
// Create an array with given slice <array>.
// NewSortedStringArrayFrom creates and returns an sorted array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
a := NewSortedStringArraySize(0, unsafe...)
a.array = array
@ -62,11 +57,9 @@ func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray
return a
}
// Create an array from a copy of given slice <array>.
// NewSortedStringArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice拷贝创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// which is false in default.
func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
newArray := make([]string, len(array))
copy(newArray, array)
@ -76,9 +69,7 @@ func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringAr
}
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -87,9 +78,9 @@ func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
return a
}
// Sort the array in increasing order.
//
// 将数组排序(默认从低到高).
// Sort sorts the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedStringArray) Sort() *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -97,9 +88,7 @@ func (a *SortedStringArray) Sort() *SortedStringArray {
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedStringArray) Add(values...string) *SortedStringArray {
if len(values) == 0 {
return a
@ -115,7 +104,6 @@ func (a *SortedStringArray) Add(values...string) *SortedStringArray {
a.array = append(a.array, value)
continue
}
// 加到指定索引后面
if cmp > 0 {
index++
}
@ -126,9 +114,8 @@ func (a *SortedStringArray) Add(values...string) *SortedStringArray {
return a
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedStringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -136,13 +123,11 @@ func (a *SortedStringArray) Get(index int) string {
return value
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
// Remove removes an item by index.
func (a *SortedStringArray) Remove(index int) string {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
@ -152,15 +137,15 @@ func (a *SortedStringArray) Remove(index int) string {
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedStringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -169,9 +154,7 @@ func (a *SortedStringArray) PopLeft() string {
return value
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
// PopRight pops and returns an item from the end of array.
func (a *SortedStringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -181,16 +164,12 @@ func (a *SortedStringArray) PopRight() string {
return value
}
// PopRand picks an random item out of array.
//
// 随机将一个数据项移出数组,并返回该数据项。
// PopRand randomly pops and return an item out of array.
func (a *SortedStringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands picks <size> items out of array.
//
// 随机将size个数据项移出数组并返回该数据项。
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedStringArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -206,9 +185,7 @@ func (a *SortedStringArray) PopRands(size int) []string {
return array
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedStringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -221,9 +198,7 @@ func (a *SortedStringArray) PopLefts(size int) []string {
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
// PopRights pops and returns <size> items from the end of array.
func (a *SortedStringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
@ -236,11 +211,9 @@ func (a *SortedStringArray) PopRights(size int) []string {
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedStringArray) Range(start, end int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -266,9 +239,7 @@ func (a *SortedStringArray) Range(start, end int) []string {
return array
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
// Sum returns the sum of values in an array.
func (a *SortedStringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
@ -278,9 +249,7 @@ func (a *SortedStringArray) Sum() (sum int) {
return
}
// Get the length of array.
//
// 数组长度。
// Len returns the length of array.
func (a *SortedStringArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -288,11 +257,9 @@ func (a *SortedStringArray) Len() int {
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedStringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
@ -306,24 +273,19 @@ func (a *SortedStringArray) Slice() []string {
return array
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
// Contains checks whether a value exists in the array.
func (a *SortedStringArray) Contains(value string) bool {
return a.Search(value) == 0
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedStringArray) Search(value string) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找.
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -349,11 +311,9 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
return mid, cmp
}
// Set unique mark to the array,
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
//
// 设置是否允许数组唯一.
func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
@ -363,9 +323,7 @@ func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
return a
}
// Do unique check, remove all repeated items.
//
// 清理数组中重复的元素项.
// Unique uniques the array, clear repeated items.
func (a *SortedStringArray) Unique() *SortedStringArray {
a.mu.Lock()
i := 0
@ -383,9 +341,7 @@ func (a *SortedStringArray) Unique() *SortedStringArray {
return a
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
// Clone returns a new array, which is a copy of current array.
func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
@ -394,9 +350,7 @@ func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
// Clear deletes all items of current array.
func (a *SortedStringArray) Clear() *SortedStringArray {
a.mu.Lock()
if len(a.array) > 0 {
@ -406,9 +360,7 @@ func (a *SortedStringArray) Clear() *SortedStringArray {
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing by callback function <f>.
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -416,9 +368,7 @@ func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading by callback function <f>.
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
a.mu.RLock()
defer a.mu.RUnlock()
@ -426,11 +376,10 @@ func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray
return a
}
// Merge two arrays. The parameter <array> can be any garray type or slice type.
// The difference between Merge and Add is Add supports only specified slice type,
// but Merge supports more variable types.
//
// 合并两个数组, 支持任意的garray数组类型及slice类型.
// Merge merges <array> into current array.
// The parameter <array> can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Strings(v.Slice())...)
@ -445,10 +394,9 @@ func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
return a
}
// Chunks an array into arrays with size elements.
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedStringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
@ -469,12 +417,9 @@ func (a *SortedStringArray) Chunk(size int) [][]string {
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
func (a *SortedStringArray) SubSlice(offset, size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -493,18 +438,14 @@ func (a *SortedStringArray) SubSlice(offset, size int) []string {
}
}
// Rand gets one random entry from array.
//
// 从数组中随机获得1个元素项(不删除)。
// Rand randomly returns one item from array(no deleting).
func (a *SortedStringArray) Rand() string {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands gets one or more random entries from array(a copy).
//
// 从数组中随机拷贝size个元素项构成slice返回。
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedStringArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -521,9 +462,7 @@ func (a *SortedStringArray) Rands(size int) []string {
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
// Join joins array elements with a string <glue>.
func (a *SortedStringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -535,4 +474,22 @@ func (a *SortedStringArray) Join(glue string) string {
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedStringArray) CountValues() map[string]int {
m := make(map[string]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *SortedStringArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -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)
}

View File

@ -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 {
@ -291,6 +289,10 @@ func (l *List) RemoveAll() {
l.mu.Unlock()
}
func (l *List) Clear() {
l.RemoveAll()
}
// 读锁操作
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()

View File

@ -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()
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()
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
}
}

View File

@ -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()
defer gm.mu.Unlock()
f(gm.m)
}
// 并发安全锁操作,使用自定义方法执行加锁读取操作
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// 合并两个Map.
func (gm *IntBoolMap) Merge(m *IntBoolMap) {
// 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 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
}
}

View File

@ -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()
defer gm.mu.Unlock()
f(gm.m)
}
// 并发安全锁操作,使用自定义方法执行加锁读取操作
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// 交换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
}
}

View File

@ -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()
@ -115,7 +136,8 @@ func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interf
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)
@ -124,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())
@ -133,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)
@ -142,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)
@ -151,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 {
@ -160,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]
@ -171,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)
@ -182,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)
@ -193,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]
@ -201,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)
@ -209,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
@ -217,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()
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()
@ -249,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
}
}

View File

@ -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()
defer gm.mu.Unlock()
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()
defer gm.mu.RUnlock()
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
}
}

View File

@ -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()
defer gm.mu.Unlock()
f(gm.m)
}
// 并发安全锁操作,使用自定义方法执行加锁读取操作
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// 合并两个Map.
func (gm *StringBoolMap) Merge(m *StringBoolMap) {
// 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 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
}
}

View File

@ -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()
defer gm.mu.Unlock()
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()
defer gm.mu.RUnlock()
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
}
}

View File

@ -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,69 +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()
}
if value != nil {
gm.m[key] = value
}
return value
if v, ok := gm.m[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
value = f()
}
if value != nil {
gm.m[key] = value
}
return value
}
// 当键名存在时返回其键值,否则写入指定的键值
// GetOrSet returns the value by key,
// or set value with given <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]
@ -171,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)
@ -193,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]
@ -201,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)
@ -209,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
@ -217,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()
defer gm.mu.Unlock()
f(gm.m)
}
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// 交换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()
@ -249,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
}
}
}

View File

@ -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()
defer gm.mu.Unlock()
f(gm.m)
}
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) {
gm.mu.RLock()
defer gm.mu.RUnlock()
f(gm.m)
}
// 交换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
}
}

View File

@ -0,0 +1,123 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getBool() bool {
return true
}
func intBoolCallBack(int, bool) bool {
return true
}
func Test_IntBoolMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntBoolMap()
m.Set(1, true)
gtest.Assert(m.Get(1), true)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, false), false)
gtest.Assert(m.SetIfNotExist(2, false), false)
gtest.Assert(m.SetIfNotExist(3, false), true)
gtest.Assert(m.Remove(2), false)
gtest.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false})
m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false})
gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false})
})
}
func Test_IntBoolMap_Set_Fun(t *testing.T) {
m := gmap.NewIntBoolMap()
m.GetOrSetFunc(1, getBool)
m.GetOrSetFuncLock(2, getBool)
gtest.Assert(m.Get(1), true)
gtest.Assert(m.Get(2), true)
gtest.Assert(m.SetIfNotExistFunc(1, getBool), false)
gtest.Assert(m.SetIfNotExistFunc(4, getBool), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock(3, getBool), true)
}
func Test_IntBoolMap_Batch(t *testing.T) {
m := gmap.NewIntBoolMap()
m.BatchSet(map[int]bool{1: true, 2: false, 3: true})
gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]bool{3: true})
}
func Test_IntBoolMap_Iterator(t *testing.T){
expect := map[int]bool{1: true, 2: false}
m := gmap.NewIntBoolMapFrom(expect)
m.Iterator(func(k int, v bool) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v bool) bool {
i++
return true
})
m.Iterator(func(k int, v bool) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_IntBoolMap_Lock(t *testing.T){
expect := map[int]bool{1: true, 2: false}
m := gmap.NewIntBoolMapFrom(expect)
m.LockFunc(func(m map[int]bool) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[int]bool) {
gtest.Assert(m, expect)
})
}
func Test_IntBoolMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntBoolMap_Merge(t *testing.T) {
m1 := gmap.NewIntBoolMap()
m2 := gmap.NewIntBoolMap()
m1.Set(1, true)
m2.Set(2, false)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false})
}

View File

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

View File

@ -0,0 +1,128 @@
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.SetIfNotExistFunc(3, getInterface), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getInterface), true)
}
func Test_IntInterfaceMap_Batch(t *testing.T) {
m := gmap.NewIntInterfaceMap()
m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3})
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
}
func Test_IntInterfaceMap_Iterator(t *testing.T){
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntInterfaceMapFrom(expect)
m.Iterator(func(k int, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v interface{}) bool {
i++
return true
})
m.Iterator(func(k int, v interface{}) bool {
j++
return false
})
gtest.Assert(i, "2")
gtest.Assert(j, 1)
}
func Test_IntInterfaceMap_Lock(t *testing.T){
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntInterfaceMapFrom(expect)
m.LockFunc(func(m map[int]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[int]interface{}) {
gtest.Assert(m, expect)
})
}
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"})
}

View File

@ -0,0 +1,132 @@
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.SetIfNotExistFunc(3, getString), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getString), true)
}
func Test_IntStringMap_Batch(t *testing.T) {
m := gmap.NewIntStringMap()
m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"})
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
}
func Test_IntStringMap_Iterator(t *testing.T){
expect := map[int]string{1: "a", 2: "b"}
m := gmap.NewIntStringMapFrom(expect)
m.Iterator(func(k int, v string) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v string) bool {
i++
return true
})
m.Iterator(func(k int, v string) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_IntStringMap_Lock(t *testing.T){
expect := map[int]string{1: "a", 2: "b", 3: "c"}
m := gmap.NewIntStringMapFrom(expect)
m.LockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
})
}
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"})
}

View File

@ -0,0 +1,125 @@
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"})
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_Iterator(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_Map_Lock(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
})
}
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"})
}

View 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())
}

View File

@ -0,0 +1,122 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func StringBoolCallBack(string, bool) bool {
return true
}
func Test_StringBoolMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringBoolMap()
m.Set("a", true)
gtest.Assert(m.Get("a"), true)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("b", false), false)
gtest.Assert(m.SetIfNotExist("b", false), false)
gtest.Assert(m.SetIfNotExist("c", false), true)
gtest.Assert(m.Remove("b"), false)
gtest.Assert(m.Contains("b"), false)
gtest.AssertIN("c", m.Keys())
gtest.AssertIN("a", m.Keys())
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false})
m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false})
gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false})
})
}
func Test_StringBoolMap_Set_Fun(t *testing.T) {
m := gmap.NewStringBoolMap()
m.GetOrSetFunc("a", getBool)
m.GetOrSetFuncLock("b", getBool)
gtest.Assert(m.Get("a"), true)
gtest.Assert(m.Get("b"), true)
gtest.Assert(m.SetIfNotExistFunc("a", getBool), false)
gtest.Assert(m.SetIfNotExistFunc("c", getBool), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getBool), true)
}
func Test_StringBoolMap_Batch(t *testing.T) {
m := gmap.NewStringBoolMap()
m.BatchSet(map[string]bool{"a": true, "b": false, "c": true})
gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]bool{"c": true})
}
func Test_StringBoolMap_Iterator(t *testing.T) {
expect := map[string]bool{"a": true, "b": false}
m := gmap.NewStringBoolMapFrom(expect)
m.Iterator(func(k string, v bool) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k string, v bool) bool {
i++
return true
})
m.Iterator(func(k string, v bool) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_StringBoolMap_Lock(t *testing.T) {
expect := map[string]bool{"a": true, "b": false}
m := gmap.NewStringBoolMapFrom(expect)
m.LockFunc(func(m map[string]bool) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[string]bool) {
gtest.Assert(m, expect)
})
}
func Test_StringBoolMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
m_clone := m.Clone()
m.Remove("a")
//修改原 map,clone 后的 map 不影响
gtest.AssertIN("a", m_clone.Keys())
m_clone.Remove("b")
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringBoolMap_Merge(t *testing.T) {
m1 := gmap.NewStringBoolMap()
m2 := gmap.NewStringBoolMap()
m1.Set("a", true)
m2.Set("b", false)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false})
}

View File

@ -0,0 +1,128 @@
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.SetIfNotExistFunc("c", getInt), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getInt), true)
}
func Test_StringIntMap_Batch(t *testing.T) {
m := gmap.NewStringIntMap()
m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3})
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]int{"c": 3})
}
func Test_StringIntMap_Iterator(t *testing.T) {
expect := map[string]int{"a": 1, "b": 2}
m := gmap.NewStringIntMapFrom(expect)
m.Iterator(func(k string, v int) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k string, v int) bool {
i++
return true
})
m.Iterator(func(k string, v int) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_StringIntMap_Lock(t *testing.T) {
expect := map[string]int{"a": 1, "b": 2}
m := gmap.NewStringIntMapFrom(expect)
m.LockFunc(func(m map[string]int) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[string]int) {
gtest.Assert(m, expect)
})
}
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})
}

View File

@ -0,0 +1,125 @@
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.SetIfNotExistFunc("c", getInterface), true)
gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock("d", getInterface), true)
}
func Test_StringInterfaceMap_Batch(t *testing.T) {
m := gmap.NewStringInterfaceMap()
m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3})
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
}
func Test_StringInterfaceMap_Iterator(t *testing.T) {
expect := map[string]interface{}{"a": true, "b": false}
m := gmap.NewStringInterfaceMapFrom(expect)
m.Iterator(func(k string, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k string, v interface{}) bool {
i++
return true
})
m.Iterator(func(k string, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_StringInterfaceMap_Lock(t *testing.T) {
expect := map[string]interface{}{"a": true, "b": false}
m := gmap.NewStringInterfaceMapFrom(expect)
m.LockFunc(func(m map[string]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[string]interface{}) {
gtest.Assert(m, expect)
})
}
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"})
}

View File

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

View File

@ -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 (

View File

@ -4,14 +4,18 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gqueue provides a dynamic/static concurrent-safe(alternative) queue.
// Package gqueue provides a dynamic/static concurrent-safe queue.
//
// 并发安全动态队列.
// Features:
//
// 1. FIFO queue(data -> list -> chan);
//
// 2. Fast creation and initialization;
//
// 3. Support dynamic queue size(unlimited queue size);
//
// 4. Blocking when reading data from queue;
//
// 特点:
// 1. 动态队列初始化速度快;
// 2. 动态的队列大小(不限大小)
// 3. 取数据时如果队列为空那么会阻塞等待;
package gqueue
import (
@ -19,27 +23,21 @@ import (
"math"
)
// 1、这是一个先进先出的队列(chan <-- list)
//
// 2、当创建Queue对象时限定大小那么等同于一个同步的chan并发安全队列
//
// 3、不限制大小时list链表用以存储数据临时chan负责为客户端读取数据当从chan获取数据时list往chan中不停补充数据
//
// 4、由于功能主体是chan那么操作仍然像chan那样具有阻塞效果
type Queue struct {
limit int // 队列限制大小
list *glist.List // 底层数据链表
events chan struct{} // 写入事件通知
closed chan struct{} // 队列关闭通知
C chan interface{} // 队列数据读取
limit int // Limit for queue size.
list *glist.List // Underlying list structure for data maintaining.
events chan struct{} // Events for data writing.
closed chan struct{} // Events for queue closing.
C chan interface{} // Underlying channel for data reading.
}
const (
// 动态队列缓冲区大小
// Size for queue buffer.
gDEFAULT_QUEUE_SIZE = 10000
)
// 队列大小为非必须参数,默认不限制
// New returns a queue object.
// Param <limit> is optional and it is not limited by default.
func New(limit...int) *Queue {
q := &Queue {
closed : make(chan struct{}, 0),
@ -56,7 +54,8 @@ func New(limit...int) *Queue {
return q
}
// 异步list->chan同步队列
// startAsyncLoop starts an asynchronous goroutine,
// which handles the data synchronization from list <q.list> to channel <q.C>.
func (q *Queue) startAsyncLoop() {
for {
select {
@ -84,7 +83,8 @@ func (q *Queue) startAsyncLoop() {
}
}
// 将数据压入队列, 队尾
// Push pushes the data <v> into the queue.
// Note that it would panics if the Push method is called after the queue is closed.
func (q *Queue) Push(v interface{}) {
if q.limit > 0 {
q.C <- v
@ -94,19 +94,22 @@ func (q *Queue) Push(v interface{}) {
}
}
// 从队头先进先出地从队列取出一项数据
// Pop pops an item from the queue in FIFO way.
// Note that it would return nil immediately if the Pop method is called after the queue is closed.
func (q *Queue) Pop() interface{} {
return <- q.C
}
// 关闭队列(通知所有通过Pop*阻塞的协程退出)
// Close closes the queue.
// Notice: It would notify all goroutines exit immediately,
// which are blocked reading by Pop method).
func (q *Queue) Close() {
close(q.C)
close(q.events)
close(q.closed)
}
// 获取当前队列大小
// Size returns the length of the queue.
func (q *Queue) Size() int {
return len(q.C) + q.list.Len()
}

View File

@ -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 (

View 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
})
})
}

View File

@ -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 (
@ -20,18 +18,14 @@ type Set struct {
m map[interface{}]struct{}
}
// Create a set, which contains un-repeated items.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的集合对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Set {
return NewSet(unsafe...)
}
// See New.
//
// 同New.
func NewSet(unsafe...bool) *Set {
return &Set{
m : make(map[interface{}]struct{}),
@ -39,10 +33,21 @@ func NewSet(unsafe...bool) *Set {
}
}
// Iterate the set by given callback <f>,
// NewFrom returns a new set from <items>.
// Parameter <items> can be either a variable of any type, or a slice.
func NewFrom(items interface{}, unsafe...bool) *Set {
m := make(map[interface{}]struct{})
for _, v := range gconv.Interfaces(items) {
m[v] = struct{}{}
}
return &Set{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
//
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历。
func (set *Set) Iterator(f func (v interface{}) bool) *Set {
set.mu.RLock()
defer set.mu.RUnlock()
@ -54,9 +59,7 @@ func (set *Set) Iterator(f func (v interface{}) bool) *Set {
return set
}
// Add one or multiple items to the set.
//
// 添加元素项到集合中(支持多个).
// Add adds one or multiple items to the set.
func (set *Set) Add(item...interface{}) *Set {
set.mu.Lock()
for _, v := range item {
@ -66,9 +69,7 @@ func (set *Set) Add(item...interface{}) *Set {
return set
}
// Check whether the set contains <item>.
//
// 键是否存在.
// Contains checks whether the set contains <item>.
func (set *Set) Contains(item interface{}) bool {
set.mu.RLock()
_, exists := set.m[item]
@ -76,9 +77,7 @@ func (set *Set) Contains(item interface{}) bool {
return exists
}
// Remove <item> from set.
//
// 删除元素项。
// Remove deletes <item> from set.
func (set *Set) Remove(item interface{}) *Set {
set.mu.Lock()
delete(set.m, item)
@ -86,9 +85,7 @@ func (set *Set) Remove(item interface{}) *Set {
return set
}
// Get size of the set.
//
// 获得集合大小。
// Size returns the size of the set.
func (set *Set) Size() int {
set.mu.RLock()
l := len(set.m)
@ -96,9 +93,7 @@ func (set *Set) Size() int {
return l
}
// Clear the set.
//
// 清空集合。
// Clear deletes all items of the set.
func (set *Set) Clear() *Set {
set.mu.Lock()
set.m = make(map[interface{}]struct{})
@ -106,9 +101,7 @@ func (set *Set) Clear() *Set {
return set
}
// Get the copy of items from set as slice.
//
// 获得集合元素项列表.
// Slice returns the a of items of the set as slice.
func (set *Set) Slice() []interface{} {
set.mu.RLock()
i := 0
@ -121,23 +114,17 @@ func (set *Set) Slice() []interface{} {
return ret
}
// Join set items with a string.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// Join joins items with a string <glue>.
func (set *Set) Join(glue string) string {
return strings.Join(gconv.Strings(set.Slice()), ",")
}
// Return set items as a string, which are joined by char ','.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// String returns items as a string, which are joined by char ','.
func (set *Set) String() string {
return set.Join(",")
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing with callback function <f>.
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
set.mu.Lock()
defer set.mu.Unlock()
@ -145,9 +132,7 @@ func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
return set
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading with callback function <f>.
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
set.mu.RLock()
defer set.mu.RUnlock()
@ -155,9 +140,7 @@ func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
return set
}
// Check whether the two sets equal.
//
// 判断两个集合是否相等.
// Equal checks whether the two sets equal.
func (set *Set) Equal(other *Set) bool {
if set == other {
return true
@ -177,9 +160,7 @@ func (set *Set) Equal(other *Set) bool {
return true
}
// Check whether the current set is sub-set of <other>.
//
// 判断当前集合是否为other集合的子集.
// IsSubsetOf checks whether the current set is a sub-set of <other>.
func (set *Set) IsSubsetOf(other *Set) bool {
if set == other {
return true
@ -196,10 +177,8 @@ func (set *Set) IsSubsetOf(other *Set) bool {
return true
}
// Returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> is in <set> or in <other>.
//
// 并集, 返回新的集合属于set或属于others的元素为元素的集合.
// Union returns a new set which is the union of <set> and <others>.
// Which means, all the items in <newSet> are in <set> or in <others>.
func (set *Set) Union(others ... *Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
@ -224,10 +203,8 @@ func (set *Set) Union(others ... *Set) (newSet *Set) {
return
}
// Returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and not in <other>.
//
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
// Diff returns a new set which is the difference set from <set> to <others>.
// Which means, all the items in <newSet> are in <set> but not in <others>.
func (set *Set) Diff(others...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
@ -247,10 +224,8 @@ func (set *Set) Diff(others...*Set) (newSet *Set) {
return
}
// Returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and also in <other>.
//
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
// Intersect returns a new set which is the intersection from <set> to <others>.
// Which means, all the items in <newSet> are in <set> and also in <others>.
func (set *Set) Intersect(others...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
@ -271,11 +246,11 @@ func (set *Set) Intersect(others...*Set) (newSet *Set) {
return
}
// Returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> is in <full> and not in <set>.
// Complement returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> are in <full> and not in <set>.
//
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
// 如果给定的full集合不是set的全集时返回full与set的差集.
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *Set) Complement(full *Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
@ -290,4 +265,63 @@ func (set *Set) Complement(full *Set) (newSet *Set) {
}
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *Set) Merge(others ... *Set) *Set {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range other.m {
set.m[k] = v
}
if set != other {
other.mu.RUnlock()
}
}
return set
}
// Sum sums items.
// Note: The items should be converted to int type,
// or you'd get a result that you unexpected.
func (set *Set) Sum() (sum int) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
sum += gconv.Int(k)
}
return
}
// Pops randomly pops an item from set.
func (set *Set) Pop(size int) interface{} {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
return k
}
return nil
}
// Pops randomly pops <size> items from set.
func (set *Set) Pops(size int) []interface{} {
set.mu.RLock()
defer set.mu.RUnlock()
if size > len(set.m) {
size = len(set.m)
}
index := 0
array := make([]interface{}, size)
for k, _ := range set.m {
array[index] = k
index++
if index == size {
break
}
}
return array
}

View File

@ -18,11 +18,9 @@ type IntSet struct {
m map[int]struct{}
}
// Create a set, which contains un-repeated items.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的集合对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewIntSet(unsafe...bool) *IntSet {
return &IntSet{
m : make(map[int]struct{}),
@ -30,10 +28,20 @@ func NewIntSet(unsafe...bool) *IntSet {
}
}
// Iterate the set by given callback <f>,
// NewIntSetFrom returns a new set from <items>.
func NewIntSetFrom(items []int, unsafe...bool) *IntSet {
m := make(map[int]struct{})
for _, v := range items {
m[v] = struct{}{}
}
return &IntSet{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
//
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历。
func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
set.mu.RLock()
defer set.mu.RUnlock()
@ -45,9 +53,7 @@ func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
return set
}
// Add one or multiple items to the set.
//
// 添加元素项到集合中(支持多个).
// Add adds one or multiple items to the set.
func (set *IntSet) Add(item...int) *IntSet {
set.mu.Lock()
for _, v := range item {
@ -57,9 +63,7 @@ func (set *IntSet) Add(item...int) *IntSet {
return set
}
// Check whether the set contains <item>.
//
// 键是否存在.
// Contains checks whether the set contains <item>.
func (set *IntSet) Contains(item int) bool {
set.mu.RLock()
_, exists := set.m[item]
@ -67,9 +71,7 @@ func (set *IntSet) Contains(item int) bool {
return exists
}
// Remove <item> from set.
//
// 删除元素项。
// Remove deletes <item> from set.
func (set *IntSet) Remove(item int) *IntSet {
set.mu.Lock()
delete(set.m, item)
@ -77,9 +79,7 @@ func (set *IntSet) Remove(item int) *IntSet {
return set
}
// Get size of the set.
//
// 获得集合大小。
// Size returns the size of the set.
func (set *IntSet) Size() int {
set.mu.RLock()
l := len(set.m)
@ -87,9 +87,7 @@ func (set *IntSet) Size() int {
return l
}
// Clear the set.
//
// 清空集合。
// Clear deletes all items of the set.
func (set *IntSet) Clear() *IntSet {
set.mu.Lock()
set.m = make(map[int]struct{})
@ -97,9 +95,7 @@ func (set *IntSet) Clear() *IntSet {
return set
}
// Get the copy of items from set as slice.
//
// 获得集合元素项列表.
// Slice returns the a of items of the set as slice.
func (set *IntSet) Slice() []int {
set.mu.RLock()
ret := make([]int, len(set.m))
@ -112,23 +108,17 @@ func (set *IntSet) Slice() []int {
return ret
}
// Join set items with a string.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// Join joins items with a string <glue>.
func (set *IntSet) Join(glue string) string {
return strings.Join(gconv.Strings(set.Slice()), ",")
}
// Return set items as a string, which are joined by char ','.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// String returns items as a string, which are joined by char ','.
func (set *IntSet) String() string {
return set.Join(",")
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing with callback function <f>.
func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
set.mu.Lock()
defer set.mu.Unlock()
@ -136,9 +126,7 @@ func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
return set
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading with callback function <f>.
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
set.mu.RLock()
defer set.mu.RUnlock()
@ -146,9 +134,7 @@ func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
return set
}
// Check whether the two sets equal.
//
// 判断两个集合是否相等.
// Equal checks whether the two sets equal.
func (set *IntSet) Equal(other *IntSet) bool {
if set == other {
return true
@ -168,9 +154,7 @@ func (set *IntSet) Equal(other *IntSet) bool {
return true
}
// Check whether the current set is sub-set of <other>.
//
// 判断当前集合是否为other集合的子集.
// IsSubsetOf checks whether the current set is a sub-set of <other>.
func (set *IntSet) IsSubsetOf(other *IntSet) bool {
if set == other {
return true
@ -187,10 +171,8 @@ func (set *IntSet) IsSubsetOf(other *IntSet) bool {
return true
}
// Returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> is in <set> or in <other>.
//
// 并集, 返回新的集合属于set或属于others的元素为元素的集合.
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
@ -215,10 +197,8 @@ func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
return
}
// Returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and not in <other>.
//
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
@ -238,10 +218,8 @@ func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
return
}
// Returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and also in <other>.
//
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
@ -262,11 +240,11 @@ func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
return
}
// Returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> is in <full> and not in <set>.
// Complement returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> are in <full> and not in <set>.
//
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
// 如果给定的full集合不是set的全集时返回full与set的差集.
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
@ -282,3 +260,62 @@ func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *IntSet) Merge(others ... *IntSet) *IntSet {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range other.m {
set.m[k] = v
}
if set != other {
other.mu.RUnlock()
}
}
return set
}
// Sum sums items.
// Note: The items should be converted to int type,
// or you'd get a result that you unexpected.
func (set *IntSet) Sum() (sum int) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
sum += k
}
return
}
// Pops randomly pops an item from set.
func (set *IntSet) Pop(size int) int {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
return k
}
return 0
}
// Pops randomly pops <size> items from set.
func (set *IntSet) Pops(size int) []int {
set.mu.RLock()
defer set.mu.RUnlock()
if size > len(set.m) {
size = len(set.m)
}
index := 0
array := make([]int, size)
for k, _ := range set.m {
array[index] = k
index++
if index == size {
break
}
}
return array
}

View File

@ -9,7 +9,8 @@ package gset
import (
"github.com/gogf/gf/g/internal/rwmutex"
"strings"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
type StringSet struct {
@ -17,11 +18,9 @@ type StringSet struct {
m map[string]struct{}
}
// Create a set, which contains un-repeated items.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的集合对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
// New create and returns a new set, which contains un-repeated items.
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewStringSet(unsafe...bool) *StringSet {
return &StringSet {
m : make(map[string]struct{}),
@ -29,10 +28,20 @@ func NewStringSet(unsafe...bool) *StringSet {
}
}
// Iterate the set by given callback <f>,
// NewStringSetFrom returns a new set from <items>.
func NewStringSetFrom(items []string, unsafe...bool) *StringSet {
m := make(map[string]struct{})
for _, v := range items {
m[v] = struct{}{}
}
return &StringSet{
m : m,
mu : rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
//
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历。
func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
set.mu.RLock()
defer set.mu.RUnlock()
@ -44,9 +53,7 @@ func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
return set
}
// Add one or multiple items to the set.
//
// 添加元素项到集合中(支持多个).
// Add adds one or multiple items to the set.
func (set *StringSet) Add(item...string) *StringSet {
set.mu.Lock()
for _, v := range item {
@ -56,9 +63,7 @@ func (set *StringSet) Add(item...string) *StringSet {
return set
}
// Check whether the set contains <item>.
//
// 键是否存在.
// Contains checks whether the set contains <item>.
func (set *StringSet) Contains(item string) bool {
set.mu.RLock()
_, exists := set.m[item]
@ -66,9 +71,7 @@ func (set *StringSet) Contains(item string) bool {
return exists
}
// Remove <item> from set.
//
// 删除元素项。
// Remove deletes <item> from set.
func (set *StringSet) Remove(item string) *StringSet {
set.mu.Lock()
delete(set.m, item)
@ -76,9 +79,7 @@ func (set *StringSet) Remove(item string) *StringSet {
return set
}
// Get size of the set.
//
// 获得集合大小。
// Size returns the size of the set.
func (set *StringSet) Size() int {
set.mu.RLock()
l := len(set.m)
@ -86,9 +87,7 @@ func (set *StringSet) Size() int {
return l
}
// Clear the set.
//
// 清空集合。
// Clear deletes all items of the set.
func (set *StringSet) Clear() *StringSet {
set.mu.Lock()
set.m = make(map[string]struct{})
@ -96,9 +95,7 @@ func (set *StringSet) Clear() *StringSet {
return set
}
// Get the copy of items from set as slice.
//
// 获得集合元素项列表.
// Slice returns the a of items of the set as slice.
func (set *StringSet) Slice() []string {
set.mu.RLock()
ret := make([]string, len(set.m))
@ -112,23 +109,17 @@ func (set *StringSet) Slice() []string {
return ret
}
// Join set items with a string.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// Join joins items with a string <glue>.
func (set *StringSet) Join(glue string) string {
return strings.Join(set.Slice(), ",")
}
// Return set items as a string, which are joined by char ','.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
// String returns items as a string, which are joined by char ','.
func (set *StringSet) String() string {
return set.Join(",")
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
// LockFunc locks writing with callback function <f>.
func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
set.mu.Lock()
defer set.mu.Unlock()
@ -136,9 +127,7 @@ func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
return set
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
// RLockFunc locks reading with callback function <f>.
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
set.mu.RLock()
defer set.mu.RUnlock()
@ -146,9 +135,7 @@ func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
return set
}
// Check whether the two sets equal.
//
// 判断两个集合是否相等.
// Equal checks whether the two sets equal.
func (set *StringSet) Equal(other *StringSet) bool {
if set == other {
return true
@ -168,9 +155,7 @@ func (set *StringSet) Equal(other *StringSet) bool {
return true
}
// Check whether the current set is sub-set of <other>.
//
// 判断当前集合是否为other集合的子集.
// IsSubsetOf checks whether the current set is a sub-set of <other>.
func (set *StringSet) IsSubsetOf(other *StringSet) bool {
if set == other {
return true
@ -187,10 +172,8 @@ func (set *StringSet) IsSubsetOf(other *StringSet) bool {
return true
}
// Returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> is in <set> or in <other>.
//
// 并集, 返回新的集合属于set或属于others的元素为元素的集合.
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
@ -215,10 +198,8 @@ func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
return
}
// Returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and not in <other>.
//
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
@ -238,10 +219,8 @@ func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
return
}
// Returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> is in <set> and also in <other>.
//
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
@ -262,11 +241,11 @@ func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
return
}
// Returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> is in <full> and not in <set>.
// Complement returns a new set which is the complement from <set> to <full>.
// Which means, all the items in <newSet> are in <full> and not in <set>.
//
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
// 如果给定的full集合不是set的全集时返回full与set的差集.
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
@ -282,3 +261,62 @@ func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *StringSet) Merge(others ... *StringSet) *StringSet {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range other.m {
set.m[k] = v
}
if set != other {
other.mu.RUnlock()
}
}
return set
}
// Sum sums items.
// Note: The items should be converted to int type,
// or you'd get a result that you unexpected.
func (set *StringSet) Sum() (sum int) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
sum += gconv.Int(k)
}
return
}
// Pops randomly pops an item from set.
func (set *StringSet) Pop(size int) string {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
return k
}
return ""
}
// Pops randomly pops <size> items from set.
func (set *StringSet) Pops(size int) []string {
set.mu.RLock()
defer set.mu.RUnlock()
if size > len(set.m) {
size = len(set.m)
}
index := 0
array := make([]string, size)
for k, _ := range set.m {
array[index] = k
index++
if index == size {
break
}
}
return array
}

View File

@ -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
}

View File

@ -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)))
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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...)
}

View File

@ -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)))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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)))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 (

View File

@ -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

View File

@ -61,7 +61,10 @@ func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) {
blockModel := cipher.NewCBCDecrypter(block, ivValue)
plainText := make([]byte, len(cipherText))
blockModel.CryptBlocks(plainText, cipherText)
plainText = PKCS5UnPadding(plainText)
plainText, e := PKCS5UnPadding(plainText, blockSize)
if e != nil {
return nil, e
}
return plainText, nil
}
@ -72,8 +75,27 @@ func PKCS5Padding(src []byte, blockSize int) []byte {
return append(src, padtext...)
}
func PKCS5UnPadding(src []byte) []byte {
func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if blockSize <= 0 {
return nil, errors.New("invalid blocklen")
}
if length%blockSize != 0 || length == 0 {
return nil, errors.New("invalid data len")
}
unpadding := int(src[length - 1])
return src[:(length - unpadding)]
}
if unpadding > blockSize || unpadding == 0 {
return nil, errors.New("invalid padding")
}
padding := src[length - unpadding:]
for i := 0; i < unpadding; i++ {
if padding[i] != byte(unpadding) {
return nil, errors.New("invalid padding")
}
}
return src[:(length - unpadding)], nil
}

View 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 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")
keys = []byte("12345678912345678912345678912346")
)
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))
encrypt, err = gaes.Encrypt(content, key_32, iv)
decrypt, err = gaes.Decrypt(encrypt, keys, iv)
gtest.Assert(err, "invalid padding")
})
}

View 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)
})
}

View File

@ -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))
}
}
}

View 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, "")
})
}

View 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,"")
})
}

View File

@ -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

View File

@ -148,23 +148,23 @@ 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.
)
var (
// 单例对象Map
// Instance map.
instances = gmap.NewStringInterfaceMap()
)
// 使用默认/指定分组配置进行连接数据库集群配置项default
func New(name...string) (db DB, err error) {
// 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]
@ -209,8 +209,10 @@ func New(name...string) (db DB, err error) {
}
}
// 获得数据库操作对象单例
func Instance(name...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]

View File

@ -107,7 +107,7 @@ func New(config Config) *Redis {
// return redis instance with default group.
//
// 获取指定分组名称的Redis单例对象底层根据配置信息公用的连接池连接池单例
func Instance(name...string) *Redis {
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]

View File

@ -9,18 +9,16 @@ 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.
//
// 设置全局分组配置name为非必需参数默认为默认分组名称。
func SetConfig(config Config, name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
@ -30,10 +28,8 @@ func SetConfig(config Config, name...string) {
instances.Remove(group)
}
// GetConfig returns the global configuration with specified group.
// If <group> is not passed, it returns configuration of the default group name.
//
// 获取指定全局分组配置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 {
@ -47,8 +43,6 @@ func GetConfig(name...string) (config Config, ok bool) {
// RemoveConfig removes the global configuration with specified group.
// If <name> is not passed, it removes configuration of the default group name.
//
// 删除指定全局分组配置name为非必需参数默认为默认分组名称。
func RemoveConfig(name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
@ -59,8 +53,6 @@ func RemoveConfig(name...string) {
}
// ClearConfig removes all configurations and instances of redis.
//
// 清除所有的配置内容。
func ClearConfig() {
configs.Clear()
instances.Clear()

View File

@ -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

View 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)
}
}

View File

@ -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
}

View 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)
}
}

View 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))
}

View 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 &#39;quote&#39; &#34;is&#34; &lt;b&gt;bold&lt;/b&gt;`
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 &#39;quote&#39; &#34;is&#34; &lt;b&gt;bold&lt;/b&gt;`
gtest.Assert(ghtml.SpecialChars(src), dst)
gtest.Assert(ghtml.SpecialCharsDecode(dst), src)
}

View File

@ -139,9 +139,9 @@ func Test_GetMap(t *testing.T) {
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetMap("n"), g.Map{})
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), g.Map{})
gtest.Assert(j.GetMap("a"), nil)
})
}
@ -189,7 +189,7 @@ func Test_GetStrings(t *testing.T) {
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}

View File

@ -107,9 +107,9 @@ func Test_GetMap(t *testing.T) {
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetMap("n"), g.Map{})
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), g.Map{})
gtest.Assert(j.GetMap("a"), nil)
})
}
@ -144,7 +144,7 @@ func Test_GetStrings(t *testing.T) {
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}

View 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
}
})
}

View 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])
}
}
})
}

View File

@ -0,0 +1,143 @@
// 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 gyaml_test
import (
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gyaml"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
var yamlStr string = `
#即表示url属性值
url: http://www.wolfcode.cn
#即表示server.host属性的值
server:
host: http://www.wolfcode.cn
#数组即表示server为[a,b,c]
server:
- 120.168.117.21
- 120.168.117.22
- 120.168.117.23
#常量
pi: 3.14 #定义一个数值3.14
hasChild: true #定义一个boolean值
name: '你好YAML' #定义一个字符串
`
var yamlErr 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["yaml"] = yamlStr
res, err := gyaml.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("yaml"), yamlStr)
})
}
func TestDecode(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["yaml"] = yamlStr
res, err := gyaml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
decodeStr, err := gyaml.Decode(res)
if err != nil {
t.Errorf("decode failed. %v", err)
return
}
gtest.Assert(decodeStr.(map[string]interface{})["yaml"], yamlStr)
decodeStr1 := make(map[string]interface{})
err = gyaml.DecodeTo(res, &decodeStr1)
if err != nil {
t.Errorf("decodeTo failed. %v", err)
return
}
gtest.Assert(decodeStr1["yaml"], yamlStr)
})
gtest.Case(t, func() {
_, err := gyaml.Decode([]byte(yamlErr))
if err == nil {
t.Errorf("decode failed. %v", err)
return
}
decodeStr1 := make(map[string]interface{})
err = gyaml.DecodeTo([]byte(yamlErr), &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["yaml"] = yamlStr
res, err := gyaml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
jsonyaml, err := gyaml.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(jsonyaml, expectJson)
})
gtest.Case(t, func() {
_, err := gyaml.ToJson([]byte(yamlErr))
if err == nil {
t.Errorf("ToJson failed. %v", err)
return
}
})
}

View File

@ -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 frame

View File

@ -8,25 +8,21 @@
package gins
import (
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/database/gredis"
"github.com/gogf/gf/g/internal/cmdenv"
"github.com/gogf/gf/g/os/gcfg"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gview"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"time"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/database/gredis"
"github.com/gogf/gf/g/os/gcfg"
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gview"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"time"
)
const (
gFRAME_CORE_COMPONENT_NAME_VIEW = "gf.core.component.view"
gFRAME_CORE_COMPONENT_NAME_CONFIG = "gf.core.component.config"
gFRAME_CORE_COMPONENT_NAME_REDIS = "gf.core.component.redis"
gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database"
)
@ -64,48 +60,20 @@ func SetIfNotExist(key string, value interface{}) bool {
return instances.SetIfNotExist(key, value)
}
// 核心对象View
func View(name...string) *gview.View {
group := "default"
if len(name) > 0 {
group = name[0]
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_VIEW, group)
return instances.GetOrSetFuncLock(key, func() interface{} {
view := gview.New(gfile.Pwd())
// 自定义的环境变量/启动参数路径,优先级最高,覆盖默认的工作目录
if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" && gfile.Exists(envPath) {
view.SetPath(envPath)
}
// 二进制文件执行目录
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
view.AddPath(selfPath)
}
// 开发环境源码main包目录
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
view.AddPath(mainPath)
}
// 框架内置函数
view.BindFunc("config", funcConfig)
return view
}).(*gview.View)
// View returns an instance of View with default settings.
// The param <name> is the name for the instance.
func View(name ...string) *gview.View {
return gview.Instance(name ...)
}
// 核心对象:Config
// 配置文件目录查找依次为启动参数cfgpath、当前程序运行目录
func Config(file...string) *gcfg.Config {
configFile := gcfg.DEFAULT_CONFIG_FILE
if len(file) > 0 {
configFile = file[0]
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile)
return instances.GetOrSetFuncLock(key, func() interface{} {
return gcfg.New(configFile)
}).(*gcfg.Config)
// Config returns an instance of View with default settings.
// The param <name> is the name for the instance.
func Config(name ...string) *gcfg.Config {
return gcfg.Instance(name ...)
}
// 数据库操作对象,使用了连接池
func Database(name...string) gdb.DB {
func Database(name ...string) gdb.DB {
config := Config()
group := gdb.DEFAULT_GROUP_NAME
if len(name) > 0 {

View File

@ -7,13 +7,13 @@
package gins_test
import (
"fmt"
"github.com/gogf/gf/g/frame/gins"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
"fmt"
"github.com/gogf/gf/g/frame/gins"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
func Test_Config(t *testing.T) {
@ -51,6 +51,7 @@ test = "v=1"
gtest.Case(t, func() {
gtest.AssertNE(gins.Config(), nil)
})
// relative path
gtest.Case(t, func() {
path := "config.toml"
@ -84,10 +85,11 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Clear()
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
defer gins.Config("test").Clear()
gins.Config("test").SetFileName("test.toml")
gtest.Assert(gins.Config("test").Get("test"), "v=1")
gtest.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
})
// for gfsnotify callbacks to refresh cache of config file
time.Sleep(500*time.Millisecond)
@ -97,10 +99,11 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Clear()
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
defer gins.Config("test").Clear()
gins.Config("test").SetFileName("test.toml")
gtest.Assert(gins.Config("test").Get("test"), "v=1")
gtest.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
})
// for gfsnotify callbacks to refresh cache of config file
time.Sleep(500*time.Millisecond)
@ -141,12 +144,12 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config("test.toml").Clear()
gtest.Assert(gins.Config("test.toml").AddPath(path), nil)
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
defer gins.Config("test").Clear()
gins.Config("test").SetFileName("test.toml")
gtest.Assert(gins.Config("test").AddPath(path), nil)
gtest.Assert(gins.Config("test").Get("test"), "v=1")
gtest.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
})
time.Sleep(500*time.Millisecond)
@ -156,11 +159,11 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config("test.toml").Clear()
gtest.Assert(gins.Config("test.toml").AddPath(path), nil)
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
defer gins.Config().Clear()
gins.Config("test").SetFileName("test.toml")
gtest.Assert(gins.Config("test").AddPath(path), nil)
gtest.Assert(gins.Config("test").Get("test"), "v=1")
gtest.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
})
}

View File

@ -18,7 +18,6 @@ type View struct {
mu sync.RWMutex // 并发互斥锁
view *gview.View // 底层视图对象
data gview.Params // 视图数据/模板变量
fmap gview.FuncMap // 绑定的模板函数
response *ghttp.Response // 数据返回对象
}
@ -27,7 +26,6 @@ func NewView(w *ghttp.Response) *View {
return &View {
view : gins.View(),
data : make(gview.Params),
fmap : make(gview.FuncMap),
response : w,
}
}
@ -48,26 +46,19 @@ func (view *View) Assign(key string, value interface{}) {
view.mu.Unlock()
}
// 绑定自定义模板函数
func (view *View) BindFunc(name string, function interface{}){
view.mu.Lock()
view.fmap[name] = function
view.mu.Unlock()
}
// 解析模板,并返回解析后的内容
func (view *View) Parse(file string) ([]byte, error) {
func (view *View) Parse(file string) (string, error) {
view.mu.RLock()
defer view.mu.RUnlock()
buffer, err := view.response.ParseTpl(file, view.data, view.fmap)
buffer, err := view.response.ParseTpl(file, view.data)
return buffer, err
}
// 直接解析模板内容,并返回解析后的内容
func (view *View) ParseContent(content string) ([]byte, error) {
func (view *View) ParseContent(content string) (string, error) {
view.mu.RLock()
defer view.mu.RUnlock()
buffer, err := view.response.ParseTplContent(content, view.data, view.fmap)
buffer, err := view.response.ParseTplContent(content, view.data)
return buffer, err
}

8
g/g.go
View File

@ -9,13 +9,9 @@ package g
import "github.com/gogf/gf/g/container/gvar"
// Universal variable type, like generics.
//
// 动态变量类型可以用该类型替代interface{}类型
type Var = gvar.Var
// Frequently-used map type alias.
//
// 常用map数据结构(使用别名)
type Map = map[string]interface{}
type MapAnyAny = map[interface{}]interface{}
type MapAnyStr = map[interface{}]string
@ -28,8 +24,6 @@ type MapIntStr = map[int]string
type MapIntInt = map[int]int
// Frequently-used slice type alias.
//
// 常用list数据结构(使用别名)
type List = []Map
type ListAnyStr = []map[interface{}]string
type ListAnyInt = []map[interface{}]int
@ -41,8 +35,6 @@ type ListIntStr = []map[int]string
type ListIntInt = []map[int]int
// Frequently-used slice type alias.
//
// 常用slice数据结构(使用别名)
type Slice = []interface{}
type SliceAny = []interface{}
type SliceStr = []string

View File

@ -13,38 +13,28 @@ import (
"github.com/gogf/gf/g/util/gutil"
)
// NewVar creates a *Var.
//
// 动态变量
// NewVar returns a *gvar.Var.
func NewVar(i interface{}, unsafe...bool) *Var {
return gvar.New(i, unsafe...)
}
// Wait blocks until all the web servers shutdown.
//
// 阻塞等待HTTPServer执行完成(同一进程多HTTPServer情况下)
func Wait() {
ghttp.Wait()
}
// Dump dumps a variable to stdout with more manually readable.
//
// 格式化打印变量.
func Dump(i...interface{}) {
gutil.Dump(i...)
}
// Export exports a variable to string with more manually readable.
//
// 格式化导出变量.
func Export(i...interface{}) string {
return gutil.Export(i...)
}
// Throw throws a exception, which can be caught by Catch function.
// Throw throws a exception, which can be caught by TryCatch function.
// It always be used in TryCatch function.
//
// 抛出一个异常
func Throw(exception interface{}) {
gutil.Throw(exception)
}
@ -55,12 +45,8 @@ func TryCatch(try func(), catch ... func(exception interface{})) {
}
// IsEmpty checks given value empty or not.
// false: integer(0), bool(false), slice/map(len=0), nil;
// true : other.
//
// 判断给定的变量是否为空。
// 整型为0, 布尔为false, slice/map长度为0, 其他为nil的情况都为空。
// 为空时返回true否则返回false。
// It returns false if value is: integer(0), bool(false), slice/map(len=0), nil;
// or else true.
func IsEmpty(value interface{}) bool {
return empty.IsEmpty(value)
}

View File

@ -10,23 +10,17 @@ import (
"github.com/gogf/gf/g/os/glog"
)
// Disable/Enabled debug of logging globally.
//
// 是否显示调试信息
// SetDebug disables/enables debug level for logging globally.
func SetDebug(debug bool) {
glog.SetDebug(debug)
}
// Set the logging level globally.
//
// 设置日志的显示等级
// SetLogLevel sets the logging level globally.
func SetLogLevel(level int) {
glog.SetLevel(level)
}
// Get the global logging level.
//
// 获取设置的日志显示等级
// GetLogLevel returns the global logging level.
func GetLogLevel() int {
return glog.GetLevel()
}

View File

@ -17,59 +17,42 @@ import (
"github.com/gogf/gf/g/os/gcfg"
)
// Get an instance of http server with specified name.
//
// HTTPServer单例对象
// Server returns an instance of http server with specified name.
func Server(name...interface{}) *ghttp.Server {
return ghttp.GetServer(name...)
}
// Get an instance of tcp server with specified name.
//
// TCPServer单例对象
// TCPServer returns an instance of tcp server with specified name.
func TCPServer(name...interface{}) *gtcp.Server {
return gtcp.GetServer(name...)
}
// Get an instance of udp server with specified name.
//
// UDPServer单例对象
// UDPServer returns an instance of udp server with specified name.
func UDPServer(name...interface{}) *gudp.Server {
return gudp.GetServer(name...)
}
// Get an instance of template engine object with specified name.
//
// 核心对象View
// View returns an instance of template engine object with specified name.
func View(name...string) *gview.View {
return gins.View(name...)
}
// Get an instance of config object with specified default config file name.
//
// Config配置管理对象,
// 配置文件目录查找依次为启动参数cfgpath、当前程序运行目录
// Config returns an instance of config object with specified name.
func Config(file...string) *gcfg.Config {
return gins.Config(file...)
}
// Get an instance of database ORM object with specified configuration group name.
//
// 数据库操作对象,使用了连接池
// Database returns an instance of database ORM object with specified configuration group name.
func Database(name...string) gdb.DB {
return gins.Database(name...)
}
// Alias of Database.
//
// (别名)Database
// Alias of Database. See Database.
func DB(name...string) gdb.DB {
return gins.Database(name...)
}
// Get an instance of redis client with specified configuration group name.
//
// Redis操作对象使用了连接池
// Redis returns an instance of redis client with specified configuration group name.
func Redis(name...string) *gredis.Redis {
return gins.Redis(name...)
}

View File

@ -8,9 +8,8 @@ package g
import "github.com/gogf/gf/g/net/ghttp"
// SetServerGraceful enables/disables graceful reload feature of ghttp Web Server.
//
// 是否开启WebServer的平滑重启特性。
// SetServerGraceful enables/disables graceful/hot reload feature of http Web Server.
// This feature is disabled in default.
func SetServerGraceful(enabled bool) {
ghttp.SetGraceful(enabled)
}

View File

@ -7,12 +7,27 @@
package cmdenv
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcmd"
"github.com/gogf/gf/g/os/genv"
"strings"
"github.com/gogf/gf/g/container/gvar"
"os"
"regexp"
"strings"
)
var (
// Console options.
cmdOptions = make(map[string]string)
)
func init() {
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
if len(result) > 1 {
cmdOptions[result[1]] = result[2]
}
}
}
// 获取指定名称的命令行参数,当不存在时获取环境变量参数,皆不存在时,返回给定的默认值。
// 规则:
// 1、命令行参数以小写字母格式使用: gf.包名.变量名 传递;
@ -22,11 +37,11 @@ func Get(key string, def...interface{}) gvar.VarRead {
if len(def) > 0 {
value = def[0]
}
if v := gcmd.Option.Get(key); v != "" {
if v, ok := cmdOptions[key]; ok {
value = v
} else {
key = strings.ToUpper(strings.Replace(key, ".", "_", -1))
if v := genv.Get(key); v != "" {
if v := os.Getenv(key); v != "" {
value = v
}
}

View File

@ -12,85 +12,85 @@ func Get(url string) (*ClientResponse, error) {
return DoRequest("GET", url)
}
func Put(url string, data...string) (*ClientResponse, error) {
func Put(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("PUT", url, data...)
}
func Post(url string, data...string) (*ClientResponse, error) {
func Post(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("POST", url, data...)
}
func Delete(url string, data...string) (*ClientResponse, error) {
func Delete(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("DELETE", url, data...)
}
func Head(url string, data...string) (*ClientResponse, error) {
func Head(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("HEAD", url, data...)
}
func Patch(url string, data...string) (*ClientResponse, error) {
func Patch(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("PATCH", url, data...)
}
func Connect(url string, data...string) (*ClientResponse, error) {
func Connect(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("CONNECT", url, data...)
}
func Options(url string, data...string) (*ClientResponse, error) {
func Options(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("OPTIONS", url, data...)
}
func Trace(url string, data...string) (*ClientResponse, error) {
func Trace(url string, data...interface{}) (*ClientResponse, error) {
return DoRequest("TRACE", url, data...)
}
// 该方法支持二进制提交数据
func DoRequest(method, url string, data...string) (*ClientResponse, error) {
func DoRequest(method, url string, data...interface{}) (*ClientResponse, error) {
return NewClient().DoRequest(method, url, data...)
}
// GET请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func GetContent(url string, data...string) string {
func GetContent(url string, data...interface{}) string {
return RequestContent("GET", url, data...)
}
// PUT请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func PutContent(url string, data...string) string {
func PutContent(url string, data...interface{}) string {
return RequestContent("PUT", url, data...)
}
// POST请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func PostContent(url string, data...string) string {
func PostContent(url string, data...interface{}) string {
return RequestContent("POST", url, data...)
}
// DELETE请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func DeleteContent(url string, data...string) string {
func DeleteContent(url string, data...interface{}) string {
return RequestContent("DELETE", url, data...)
}
func HeadContent(url string, data...string) string {
func HeadContent(url string, data...interface{}) string {
return RequestContent("HEAD", url, data...)
}
func PatchContent(url string, data...string) string {
func PatchContent(url string, data...interface{}) string {
return RequestContent("PATCH", url, data...)
}
func ConnectContent(url string, data...string) string {
func ConnectContent(url string, data...interface{}) string {
return RequestContent("CONNECT", url, data...)
}
func OptionsContent(url string, data...string) string {
func OptionsContent(url string, data...interface{}) string {
return RequestContent("OPTIONS", url, data...)
}
func TraceContent(url string, data...string) string {
func TraceContent(url string, data...interface{}) string {
return RequestContent("TRACE", url, data...)
}
// 请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func RequestContent(method string, url string, data...string) string {
func RequestContent(method string, url string, data...interface{}) string {
return NewClient().DoRequestContent(method, url, data...)
}

View File

@ -69,20 +69,20 @@ func (c *Client) Get(url string) (*ClientResponse, error) {
}
// PUT请求
func (c *Client) Put(url string, data...string) (*ClientResponse, error) {
func (c *Client) Put(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("PUT", url, data...)
}
// POST请求提交数据默认使用表单方式提交数据(绝大部分场景下也是如此)。
// 如果服务端对Content-Type有要求可使用Client对象进行请求单独设置相关属性。
// 支持文件上传需要字段格式为FieldName=@file:
func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
func (c *Client) Post(url string, data...interface{}) (*ClientResponse, error) {
if len(c.prefix) > 0 {
url = c.prefix + url
}
param := ""
if len(data) > 0 {
param = data[0]
param = BuildParams(data[0])
}
req := (*http.Request)(nil)
if strings.Contains(param, "@file:") {
@ -178,72 +178,72 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
}
// DELETE请求
func (c *Client) Delete(url string, data...string) (*ClientResponse, error) {
func (c *Client) Delete(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("DELETE", url, data...)
}
func (c *Client) Head(url string, data...string) (*ClientResponse, error) {
func (c *Client) Head(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("HEAD", url, data...)
}
func (c *Client) Patch(url string, data...string) (*ClientResponse, error) {
func (c *Client) Patch(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("PATCH", url, data...)
}
func (c *Client) Connect(url string, data...string) (*ClientResponse, error) {
func (c *Client) Connect(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("CONNECT", url, data...)
}
func (c *Client) Options(url string, data...string) (*ClientResponse, error) {
func (c *Client) Options(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("OPTIONS", url, data...)
}
func (c *Client) Trace(url string, data...string) (*ClientResponse, error) {
func (c *Client) Trace(url string, data...interface{}) (*ClientResponse, error) {
return c.DoRequest("TRACE", url, data...)
}
// GET请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) GetContent(url string, data...string) string {
func (c *Client) GetContent(url string, data...interface{}) string {
return c.DoRequestContent("GET", url, data...)
}
// PUT请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) PutContent(url string, data...string) string {
func (c *Client) PutContent(url string, data...interface{}) string {
return c.DoRequestContent("PUT", url, data...)
}
// POST请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) PostContent(url string, data...string) string {
func (c *Client) PostContent(url string, data...interface{}) string {
return c.DoRequestContent("POST", url, data...)
}
// DELETE请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) DeleteContent(url string, data...string) string {
func (c *Client) DeleteContent(url string, data...interface{}) string {
return c.DoRequestContent("DELETE", url, data...)
}
func (c *Client) HeadContent(url string, data...string) string {
func (c *Client) HeadContent(url string, data...interface{}) string {
return c.DoRequestContent("HEAD", url, data...)
}
func (c *Client) PatchContent(url string, data...string) string {
func (c *Client) PatchContent(url string, data...interface{}) string {
return c.DoRequestContent("PATCH", url, data...)
}
func (c *Client) ConnectContent(url string, data...string) string {
func (c *Client) ConnectContent(url string, data...interface{}) string {
return c.DoRequestContent("CONNECT", url, data...)
}
func (c *Client) OptionsContent(url string, data...string) string {
func (c *Client) OptionsContent(url string, data...interface{}) string {
return c.DoRequestContent("OPTIONS", url, data...)
}
func (c *Client) TraceContent(url string, data...string) string {
func (c *Client) TraceContent(url string, data...interface{}) string {
return c.DoRequestContent("TRACE", url, data...)
}
// 请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) DoRequestContent(method string, url string, data...string) string {
func (c *Client) DoRequestContent(method string, url string, data...interface{}) string {
response, err := c.DoRequest(method, url, data...)
if err != nil {
return ""
@ -253,7 +253,7 @@ func (c *Client) DoRequestContent(method string, url string, data...string) stri
}
// 请求并返回response对象该方法支持二进制提交数据
func (c *Client) DoRequest(method, url string, data...string) (*ClientResponse, error) {
func (c *Client) DoRequest(method, url string, data...interface{}) (*ClientResponse, error) {
if strings.EqualFold("POST", method) {
return c.Post(url, data...)
}
@ -262,7 +262,7 @@ func (c *Client) DoRequest(method, url string, data...string) (*ClientResponse,
}
param := ""
if len(data) > 0 {
param = data[0]
param = BuildParams(data[0])
}
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader([]byte(param)))
if err != nil {

View File

@ -8,21 +8,28 @@ package ghttp
import (
"github.com/gogf/gf/g/encoding/gurl"
"strings"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
// 构建请求参数,将参数进行urlencode编码
func BuildParams(params map[string]string) string {
var s string
for k, v := range params {
if len(s) > 0 {
s += "&"
// 构建请求参数,参数支持任意数据类型常见参数类型为string/map。
// 如果参数为map类型参数值将会进行urlencode编码。
func BuildParams(params interface{}) (encodedParamStr string) {
m := gconv.Map(params)
if len(m) == 0 {
return gconv.String(params)
}
s := ""
for k, v := range m {
if len(encodedParamStr) > 0 {
encodedParamStr += "&"
}
if len(v) > 6 && strings.Compare(v[0 : 6], "@file:") == 0 {
s += k + "=" + v
s = gconv.String(v)
if len(s) > 6 && strings.Compare(s[0 : 6], "@file:") == 0 {
encodedParamStr += k + "=" + s
} else {
s += k + "=" + gurl.Encode(v)
encodedParamStr += k + "=" + gurl.Encode(s)
}
}
return s
return
}

View File

@ -13,13 +13,9 @@ import (
)
// 展示模板,可以给定模板参数,及临时的自定义模板函数
func (r *Response) WriteTpl(tpl string, params map[string]interface{}, funcMap...map[string]interface{}) error {
fmap := make(gview.FuncMap)
if len(funcMap) > 0 {
fmap = funcMap[0]
}
if b, err := r.ParseTpl(tpl, params, fmap); err != nil {
r.Write("Tpl Parsing Error: " + err.Error())
func (r *Response) WriteTpl(tpl string, params...gview.Params) error {
if b, err := r.ParseTpl(tpl, params...); err != nil {
r.Write("Template Parsing Error: " + err.Error())
return err
} else {
r.Write(b)
@ -28,13 +24,9 @@ func (r *Response) WriteTpl(tpl string, params map[string]interface{}, funcMap..
}
// 展示模板内容,可以给定模板参数,及临时的自定义模板函数
func (r *Response) WriteTplContent(content string, params map[string]interface{}, funcMap...map[string]interface{}) error {
fmap := make(gview.FuncMap)
if len(funcMap) > 0 {
fmap = funcMap[0]
}
if b, err := r.ParseTplContent(content, params, fmap); err != nil {
r.Write("Tpl Parsing Error: " + err.Error())
func (r *Response) WriteTplContent(content string, params...gview.Params) error {
if b, err := r.ParseTplContent(content, params...); err != nil {
r.Write("Template Parsing Error: " + err.Error())
return err
} else {
r.Write(b)
@ -43,61 +35,27 @@ func (r *Response) WriteTplContent(content string, params map[string]interface{}
}
// 解析模板文件,并返回模板内容
func (r *Response) ParseTpl(tpl string, params gview.Params, funcMap...map[string]interface{}) ([]byte, error) {
fmap := make(gview.FuncMap)
if len(funcMap) > 0 {
fmap = funcMap[0]
}
return gins.View().Parse(tpl, r.buildInVars(params), r.buildInFuncs(fmap))
func (r *Response) ParseTpl(tpl string, params...gview.Params) (string, error) {
return gins.View().Parse(tpl, r.buildInVars(params...))
}
// 解析并返回模板内容
func (r *Response) ParseTplContent(content string, params gview.Params, funcMap...map[string]interface{}) ([]byte, error) {
fmap := make(gview.FuncMap)
if len(funcMap) > 0 {
fmap = funcMap[0]
}
return gins.View().ParseContent(content, r.buildInVars(params), r.buildInFuncs(fmap))
func (r *Response) ParseTplContent(content string, params...gview.Params) (string, error) {
return gins.View().ParseContent(content, r.buildInVars(params...))
}
// 内置变量
func (r *Response) buildInVars(params map[string]interface{}) map[string]interface{} {
if params == nil {
params = make(map[string]interface{})
}
c := gins.Config()
if c.GetFilePath() != "" {
params["Config"] = c.GetMap("")
// 内置变量/对象
func (r *Response) buildInVars(params...map[string]interface{}) map[string]interface{} {
vars := map[string]interface{}(nil)
if len(params) > 0 {
vars = params[0]
} else {
params["Config"] = nil
vars = make(map[string]interface{})
}
params["Cookie"] = r.request.Cookie.Map()
params["Session"] = r.request.Session.Data()
return params
}
// 内置函数
func (r *Response) buildInFuncs(funcMap map[string]interface{}) map[string]interface{} {
if funcMap == nil {
funcMap = make(map[string]interface{})
}
funcMap["get"] = r.funcGet
funcMap["post"] = r.funcPost
funcMap["request"] = r.funcRequest
return funcMap
}
// 模板内置函数: get
func (r *Response) funcGet(key string, def...string) string {
return r.request.GetQueryString(key, def...)
}
// 模板内置函数: post
func (r *Response) funcPost(key string, def...string) string {
return r.request.GetPostString(key, def...)
}
// 模板内置函数: request
func (r *Response) funcRequest(key string, def...string) string {
return r.request.Get(key, def...)
vars["Config"] = gins.Config().GetMap("")
vars["Cookie"] = r.request.Cookie.Map()
vars["Session"] = r.request.Session.Map()
vars["Get"] = r.request.GetQueryMap()
vars["Post"] = r.request.GetPostMap()
return vars
}

View File

@ -15,7 +15,8 @@ import (
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/genv"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gproc"
"github.com/gogf/gf/g/os/gtimer"
"github.com/gogf/gf/g/text/gregex"
@ -108,9 +109,9 @@ const (
HOOK_BEFORE_OUTPUT = "BeforeOutput"
HOOK_AFTER_OUTPUT = "AfterOutput"
// deprecated.
// Deprecated.
HOOK_BEFORE_CLOSE = "BeforeClose"
// deprecated.
// Deprecated.
HOOK_AFTER_CLOSE = "AfterClose"
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
@ -188,10 +189,10 @@ func serverProcessInit() {
go handleProcessMessage()
}
// 是否处于开发环境
//if gfile.MainPkgPath() != "" {
// glog.Debug("GF notices that you're in develop environment, so error logs are auto enabled to stdout.")
//}
// 是否处于开发环境这里调用该方法初始化main包路径值
// 防止异步服务goroutine获取main包路径失败
// 该方法只有在main协程中才会执行。
gfile.MainPkgPath()
}
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)

View File

@ -26,13 +26,13 @@ type GroupItem = []interface{}
// 获取分组路由对象
func (s *Server) Group(prefix...string) *RouterGroup {
group := &RouterGroup{
server : s,
}
if len(prefix) > 0 {
return &RouterGroup{
server : s,
prefix : prefix[0],
}
group.prefix = prefix[0]
}
return &RouterGroup{}
return group
}
// 获取分组路由对象
@ -47,7 +47,7 @@ func (d *Domain) Group(prefix...string) *RouterGroup {
}
// 执行分组路由批量绑定
func (g *RouterGroup) Bind(group string, items []GroupItem) {
func (g *RouterGroup) Bind(items []GroupItem) {
for _, item := range items {
if len(item) < 3 {
glog.Fatalfln("invalid router item: %s", item)

View File

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

View File

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

View File

@ -69,7 +69,7 @@ func (s *Session) Id() string {
}
// 获取当前session所有数据
func (s *Session) Data() map[string]interface{} {
func (s *Session) Map() map[string]interface{} {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
return s.data.Map()

View File

@ -123,7 +123,7 @@ func Test_Router_Basic2(t *testing.T) {
obj := new(GroupObject)
ctl := new(GroupController)
// 分组路由批量注册
s.Group("/api").Bind("/api", []ghttp.GroupItem{
s.Group("/api").Bind([]ghttp.GroupItem{
{"ALL", "/handler", Handler},
{"ALL", "/ctl", ctl},
{"GET", "/ctl/my-show", ctl, "Show"},

View File

@ -1 +0,0 @@
package net

View File

@ -38,7 +38,8 @@ type Config struct {
}
// New returns a new configuration management object.
func New(file...string) *Config {
// The param <file> specifies the default configuration file name for reading.
func New(file ...string) *Config {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
@ -49,19 +50,24 @@ func New(file...string) *Config {
jsons : gmap.NewStringInterfaceMap(),
vc : gtype.NewBool(),
}
// Dir path of working dir.
c.SetPath(gfile.Pwd())
// Dir path from env/cmd, most high priority, will overwrite previous dir path setting.
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" && gfile.Exists(envPath) {
c.SetPath(envPath)
}
// Dir path of binary.
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
c.AddPath(selfPath)
}
// Dir path of main package.
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
c.AddPath(mainPath)
// Customized dir path from env/cmd.
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" {
if gfile.Exists(envPath) {
c.SetPath(envPath)
} else {
glog.Errorfln("Configuration directory path does not exist: %s", envPath)
}
} else {
// Dir path of working dir.
c.SetPath(gfile.Pwd())
// Dir path of binary.
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
c.AddPath(selfPath)
}
// Dir path of main package.
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
c.AddPath(mainPath)
}
}
return c
}
@ -78,8 +84,12 @@ func (c *Config) filePath(file...string) (path string) {
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
index := 1
for _, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
index++
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v + gfile.Separator + "config"))
index++
}
})
} else {
@ -90,12 +100,13 @@ func (c *Config) filePath(file...string) (path string) {
return path
}
// 设置配置管理器的配置文件存放目录绝对路径
// SetPath sets the configuration directory path for file search.
// The param <path> can be absolute or relative path, but absolute path is suggested.
func (c *Config) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
// Absolute path.
realPath := gfile.RealPath(path)
if realPath == "" {
// 判断相对路径
// Relative path.
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
@ -105,7 +116,7 @@ func (c *Config) SetPath(path string) error {
}
})
}
// 目录不存在错误处理
// Path not exist.
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
@ -122,13 +133,13 @@ func (c *Config) SetPath(path string) error {
glog.Error(err)
return err
}
// 路径必须为目录类型
// Should be a directory.
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
// Repeated path check.
if c.paths.Search(realPath) != -1 {
return nil
}
@ -139,19 +150,23 @@ func (c *Config) SetPath(path string) error {
return nil
}
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
// SetViolenceCheck sets whether to perform level conflict check.
// This feature needs to be enabled when there is a level symbol in the key name.
// The default is off.
// Turning on this feature is quite expensive,
// and it is not recommended to allow separators in the key names.
// It is best to avoid this on the application side.
func (c *Config) SetViolenceCheck(check bool) {
c.vc.Set(check)
c.Clear()
}
// 添加配置管理器的配置文件搜索路径
// AddPath adds a absolute or relative path to the search paths.
func (c *Config) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
// Absolute path.
realPath := gfile.RealPath(path)
if realPath == "" {
// 判断相对路径
// Relative path.
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
@ -161,14 +176,13 @@ func (c *Config) AddPath(path string) error {
}
})
}
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
buffer.WriteString(fmt.Sprintf("\n%d. %s", k + 1, v))
}
})
} else {
@ -178,13 +192,12 @@ func (c *Config) AddPath(path string) error {
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
// Repeated path check.
if c.paths.Search(realPath) != -1 {
return nil
}
@ -193,8 +206,10 @@ func (c *Config) AddPath(path string) error {
return nil
}
// 查找配置文件,获取指定配置文件的绝对路径,默认获取默认的配置文件路径;
// 当指定的配置文件不存在时,返回空字符串,并且不会报错。
// GetFilePath returns the absolute path of the specified configuration file.
// If <file> is not passed, it returns the configuration file path of the default name.
// If the specified configuration file does not exist,
// an empty string is returned.
func (c *Config) GetFilePath(file...string) (path string) {
name := c.name.Val()
if len(file) > 0 {
@ -202,12 +217,10 @@ func (c *Config) GetFilePath(file...string) (path string) {
}
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
// 查找当前目录
if path, _ = gspath.Search(v, name); path != "" {
break
}
// 查找当前目录下的config子目录
if path, _ = gspath.Search(v, "config" + gfile.Separator + name); path != "" {
if path, _ = gspath.Search(v + gfile.Separator + "config", name); path != "" {
break
}
}
@ -215,19 +228,20 @@ func (c *Config) GetFilePath(file...string) (path string) {
return
}
// 设置配置管理对象的默认文件名称
// SetFileName sets the default configuration file name.
func (c *Config) SetFileName(name string) {
//glog.Debug("[gcfg] SetFileName:", name)
c.name.Set(name)
}
// 获取配置管理对象的默认文件名称
// GetFileName returns the default configuration file name.
func (c *Config) GetFileName() string {
return c.name.Val()
}
// 添加配置文件到配置管理器中,第二个参数为非必须,如果不输入表示添加进入默认的配置名称中
// 内部带缓存控制功能。
// getJson returns a gjson.Json object for the specified <file> content.
// It would print error if file reading fails.
// If any error occurs, it return nil.
func (c *Config) getJson(file...string) *gjson.Json {
name := c.name.Val()
if len(file) > 0 {
@ -245,7 +259,8 @@ func (c *Config) getJson(file...string) *gjson.Json {
}
if j, err := gjson.LoadContent(content); err == nil {
j.SetViolenceCheck(c.vc.Val())
// 添加配置文件监听,如果有任何变化,删除文件内容缓存,下一次查询会自动更新
// Add monitor for this configuration file,
// any changes of this file will refresh its cache in Config object.
if filePath != "" {
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
@ -267,7 +282,6 @@ func (c *Config) getJson(file...string) *gjson.Json {
return nil
}
// 获取配置项当不存在时返回nil
func (c *Config) Get(pattern string, file...string) interface{} {
if j := c.getJson(file...); j != nil {
return j.Get(pattern)
@ -275,7 +289,6 @@ func (c *Config) Get(pattern string, file...string) interface{} {
return nil
}
// 获得配置项,返回动态变量
func (c *Config) GetVar(pattern string, file...string) gvar.VarRead {
if j := c.getJson(file...); j != nil {
return gvar.New(j.Get(pattern), true)
@ -283,7 +296,6 @@ func (c *Config) GetVar(pattern string, file...string) gvar.VarRead {
return gvar.New(nil, true)
}
// 判断指定的配置项是否存在
func (c *Config) Contains(pattern string, file...string) bool {
if j := c.getJson(file...); j != nil {
return j.Contains(pattern)
@ -291,8 +303,6 @@ func (c *Config) Contains(pattern string, file...string) bool {
return false
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (c *Config) GetMap(pattern string, file...string) map[string]interface{} {
if j := c.getJson(file...); j != nil {
return j.GetMap(pattern)
@ -300,8 +310,6 @@ func (c *Config) GetMap(pattern string, file...string) map[string]interface{} {
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (c *Config) GetArray(pattern string, file...string) []interface{} {
if j := c.getJson(file...); j != nil {
return j.GetArray(pattern)
@ -309,7 +317,6 @@ func (c *Config) GetArray(pattern string, file...string) []interface{} {
return nil
}
// 返回指定json中的string
func (c *Config) GetString(pattern string, file...string) string {
if j := c.getJson(file...); j != nil {
return j.GetString(pattern)
@ -331,7 +338,6 @@ func (c *Config) GetInterfaces(pattern string, file...string) []interface{} {
return nil
}
// 返回指定json中的bool
func (c *Config) GetBool(pattern string, file...string) bool {
if j := c.getJson(file...); j != nil {
return j.GetBool(pattern)
@ -339,7 +345,6 @@ func (c *Config) GetBool(pattern string, file...string) bool {
return false
}
// 返回指定json中的float32
func (c *Config) GetFloat32(pattern string, file...string) float32 {
if j := c.getJson(file...); j != nil {
return j.GetFloat32(pattern)
@ -347,7 +352,6 @@ func (c *Config) GetFloat32(pattern string, file...string) float32 {
return 0
}
// 返回指定json中的float64
func (c *Config) GetFloat64(pattern string, file...string) float64 {
if j := c.getJson(file...); j != nil {
return j.GetFloat64(pattern)
@ -362,7 +366,6 @@ func (c *Config) GetFloats(pattern string, file...string) []float64 {
return nil
}
// 返回指定json中的float64->int
func (c *Config) GetInt(pattern string, file...string) int {
if j := c.getJson(file...); j != nil {
return j.GetInt(pattern)
@ -406,7 +409,6 @@ func (c *Config) GetInts(pattern string, file...string) []int {
return nil
}
// 返回指定json中的float64->uint
func (c *Config) GetUint(pattern string, file...string) uint {
if j := c.getJson(file...); j != nil {
return j.GetUint(pattern)

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