Compare commits

...

232 Commits

Author SHA1 Message Date
0e6c2e790d copy mysql driver to third folder 2019-05-04 23:02:07 +08:00
34c761e9db rename mysql driver from 'mysql' to 'gf-mysql' to avoid multiple imports error 2019-05-04 00:25:02 +08:00
87e3813636 remove go-sql-driver-mysql from third, add Register function to manually register 'mysql' driver 2019-05-04 00:10:02 +08:00
361ff0315c version updates 2019-05-03 17:04:42 +08:00
2bb227d058 update gtcp exxamples 2019-05-03 15:47:25 +08:00
99dc69e839 update gtcp exxamples 2019-05-03 13:28:27 +08:00
c9537af062 add package feature for gudp; gtcp updates 2019-04-29 23:54:47 +08:00
7ae03729f3 add package support for gtcp 2019-04-28 23:55:23 +08:00
ea7e2ec5ec remove go version limit in go.mod 2019-04-28 21:10:22 +08:00
237c58f2b0 Merge branch 'master' of https://github.com/gogf/gf 2019-04-26 13:46:47 +08:00
efa23e4a1d README updates 2019-04-26 13:46:42 +08:00
c109cee7ef Merge pull request #115 from touzijiao/master
Merge pull request #115 from touzijiao/master
2019-04-26 13:44:51 +08:00
e111d39c54 测试文件 2019-04-26 10:38:27 +08:00
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
630d8fdb43 add Instance function for gcfg 2019-04-03 09:59:15 +08:00
c4c7e6caf4 gofmt 2019-04-03 00:03:46 +08:00
8a8fea1257 v1.5.23 2019-04-02 23:40:20 +08:00
0e0f297a3f add consurrent safety parameter for gjson.Load/LoadContent; fulfil data type auto-check logics for gjson.LoadConetnt; update comment of gjson/gparser 2019-04-02 23:33:27 +08:00
fd2c0f2b24 Merge pull request #59 from wenzi1/master
修复gxml字符集转换的并发安全问题
2019-04-02 17:43:19 +08:00
ecc6e3888d up 2019-04-02 17:10:21 +08:00
47c073aaf3 add Clear function for gcfg; mark Reload function of gcfg as deprecated; update unit test for gins 2019-04-02 16:08:46 +08:00
07476a4349 add instance management feature for gdb/gredis; add customized configuration content management feature for gcfg; update gjson for data content type check 2019-04-02 14:37:46 +08:00
817148f3a1 增加单元测试 2019-04-01 18:32:37 +08:00
bd4271cd8c 增加gxml的单元测试 2019-03-31 23:36:11 +08:00
b1804fc346 TODO updates; version updates 2019-03-31 21:13:11 +08:00
a3886c2179 fix issue in RemoteAddr of gudp.Conn 2019-03-31 20:58:31 +08:00
f258b5bf1c change logging level from error to debug in route regitsry for struct for ghttp package; handle possible overrange usage of r.URL.Path in ghttp.Server 2019-03-31 20:52:30 +08:00
a05361011f 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换 2019-03-30 23:53:42 +08:00
6b34a77251 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换 2019-03-30 23:39:07 +08:00
afb1adee3d version updates 2019-03-28 17:58:33 +08:00
22fa7a37f3 add UseNumber support for gjson; add more unit test cases for gjson 2019-03-28 17:51:05 +08:00
6a58bfc574 add stdout printing support for ghttp.Server logging 2019-03-28 09:34:16 +08:00
64124c60fc add fatal error when no router set or statis feature enabled for ghttp.Server 2019-03-27 16:23:35 +08:00
9a0066de62 add file/folder search support for gcfg/gview in order of envpath/pwdpath/binpath/mainpath; add gfile.Search function 2019-03-27 11:48:53 +08:00
22c7c7403b fix issue in basic http auth check for server side 2019-03-27 09:16:23 +08:00
83db8e4b15 add wss example for websocket 2019-03-22 17:26:39 +08:00
25f2e121e7 version updates 2019-03-22 15:09:22 +08:00
1325a145d8 fix issue in config auto reload of gcfg 2019-03-22 15:08:43 +08:00
9bc49c0b29 add logging for error in ghttp.Request 2019-03-22 14:31:02 +08:00
8e84e5b0f3 update comment of gredis 2019-03-22 11:22:03 +08:00
a42e6b0c45 ignore private attribute of struct in gconv/gvalid 2019-03-22 10:12:43 +08:00
51bb7a9854 ignore private attribute of struct in gconv/gvalid 2019-03-22 10:12:15 +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
590 changed files with 18340 additions and 15022 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,39 @@ 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)
- [touzijiao](https://github.com/touzijiao)
- [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,33 @@ 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)
- [touzijiao](https://github.com/touzijiao)
- [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)
## 新特性

45
TODO.MD
View File

@ -1,23 +1,16 @@
# ON THE WAY
1. 增加图形验证码支持,至少支持数字和英文字母;
1. 增加热编译工具,提高开发环境的开发/测试效率媲美PHP开发效率
1. 增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射
1. ghttp.Response增加输出内容后自动退出当前请求机制不需要用户手动return参考beego如何实现
1. Cookie&Session数据池化处理
1. ghttp.Client增加proxy特性
1. gtime增加对时区转换的封装并简化失去转换时对类似+80500时区的支持
1. orm增加sqlite对Save方法的支持(去掉触发器语句);
1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps)
1. ghttp路由功能增加分组路由特性
1. ghttp增加返回数据压缩机制
1. gview中的template标签失效问题
1. gfile文件stat信息使用gfsnotify进行缓存更新改进
1. ghttp.Server增加proxy功能特性本地proxy和远程proxy本地即将路由规则映射远程即反向代理
1. gjson对大json数据的解析效率问题
1. ghttp增加route name特性并同时支持backend和template(提供内置函数)引用可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上
1. ghttp.Client自动Close机制
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则最好做成链式操作
1. 检查ghttp.Server超时问题
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节)
1. ghttp.Request增加对输入参数的自动HtmlEncode机制
1. 常量命名风格根据golint进行修改
@ -36,31 +29,22 @@
- https://github.com/Masterminds/sprig
1. gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题并完善文档参考https://www.cnblogs.com/kex1n/p/6502002.html
1. gfile对于文件的读写强行使用了gfpool在某些场景下不合适需要考虑剥离开并为开发者提供单独的指针池文件操作特性
1. 路由增加不区分大小写得匹配方式;
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
1. 改进WebServer获取POST参数处理逻辑当提交非form数据时例如json数据针对某些方法可以直接解析
1. WebServer增加可选择的路由覆盖配置默认情况下不覆盖
1. gkafka这个包比较重未来从框架中剥离出来
1. grpool性能压测结果变慢的问题
1. 增加jumplist的数据结构容器
1. DelayQueue/PriorityQueue
1. gconv针对struct的转换增加json tag支持gconv.Map默认也支持json tag, 完善开发文档;
1. 增加SO_REUSEPORT的支持
1. 权限管理模块;
1. 从ghttp中剥离SESSION功能构成单独的模块gsession
1. 改进gproc进程间通信处理逻辑提高稳定性以应对进程间大批量的数据发送/接收;
1. gdb的Data方法支持struct参数传入
1. gfcache依旧使用gcache作为缓存控制对象不要使用gmap
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
1. 更新跨域请求CORS相关功能文档
1. ghttp的热重启的本地进程端口监听在不使用该特性时默认关闭掉
1. gcfg包目前允许添加重复的目录路径需要在SetPath/AddPath时判断重复性不能添加重复的路径
1. gdb执行数据写入时如果参数为struct/[]struct自动映射与表字段对应关系不再使用gconv标签标识
1. gtcp增加对TLS加密通信的支持
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持
1. 添加sqlite数据库的单元测试用例
1. gredis增加cluster支持
1. gset.Add/Remove/Contains方法增加批量操作支持
1. gmlock增加手动清理机制当内存锁不再使用时由调用端决定是否清理内存锁
# DONE
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换
@ -119,4 +103,19 @@
1. gform对于MySQL字段类型为datetime类型的时区问题分析
1. 改进证书打开失败时的WebServer错误提示前置HOOK校验后关闭后续的HOOK逻辑执行
1. 目前WebServer的HOOK是按照优先级执行的需要增加覆盖特性
1. 更新跨域请求CORS相关功能文档
1. ghttp.Response增加输出内容后自动退出当前请求机制不需要用户手动return参考beego如何实现
1. gcfg包目前允许添加重复的目录路径需要在SetPath/AddPath时判断重复性不能添加重复的路径
1. gdb执行数据写入时如果参数为struct/[]struct自动映射与表字段对应关系不再使用gconv标签标识
1. gdb的Data方法支持struct参数传入
1. gfcache依旧使用gcache作为缓存控制对象不要使用gmap
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
1. gconv针对struct的转换增加json tag支持gconv.Map默认也支持json tag, 完善开发文档;
1. 增加SO_REUSEPORT的支持
1. gkafka这个包比较重未来从框架中剥离出来
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
1. gfile对于文件的读写强行使用了gfpool在某些场景下不合适需要考虑剥离开并为开发者提供单独的指针池文件操作特性
1. ghttp.Client自动Close机制
1. ghttp路由功能增加分组路由特性
1. 增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射

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

@ -0,0 +1,265 @@
package gvar_test
import (
"bytes"
"encoding/binary"
"testing"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/test/gtest"
)
func TestReadOnly(t *testing.T) {
gtest.Case(t, func() {
obj := gvar.New(nil, true)
var result string
switch obj.ReadOnly().(type) {
case gvar.VarRead:
result = "yes"
default:
result = "no"
}
gtest.Assert(result, "yes")
})
}
func TestSet(t *testing.T) {
gtest.Case(t, func() {
objOne := gvar.New("old", true)
objOneOld, _ := objOne.Set("new").(string)
gtest.Assert(objOneOld, "old")
objTwo := gvar.New("old", false)
objTwoOld, _ := objTwo.Set("new").(string)
gtest.Assert(objTwoOld, "old")
})
}
func TestVal(t *testing.T) {
gtest.Case(t, func() {
objOne := gvar.New(1, true)
objOneOld, _ := objOne.Val().(int)
gtest.Assert(objOneOld, 1)
objTwo := gvar.New(1, false)
objTwoOld, _ := objTwo.Val().(int)
gtest.Assert(objTwoOld, 1)
})
}
func TestInterface(t *testing.T) {
gtest.Case(t, func() {
objOne := gvar.New(1, true)
objOneOld, _ := objOne.Interface().(int)
gtest.Assert(objOneOld, 1)
objTwo := gvar.New(1, false)
objTwoOld, _ := objTwo.Interface().(int)
gtest.Assert(objTwoOld, 1)
})
}
func TestIsNil(t *testing.T) {
gtest.Case(t, func() {
objOne := gvar.New(nil, true)
gtest.Assert(objOne.IsNil(), true)
objTwo := gvar.New("noNil", false)
gtest.Assert(objTwo.IsNil(), false)
})
}
func TestBytes(t *testing.T) {
gtest.Case(t, func() {
x := int32(1)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
objOne := gvar.New(bytesBuffer.Bytes(), true)
bBuf := bytes.NewBuffer(objOne.Bytes())
var y int32
binary.Read(bBuf, binary.BigEndian, &y)
gtest.Assert(x, y)
})
}
func TestString(t *testing.T) {
gtest.Case(t, func() {
var str string = "hello"
objOne := gvar.New(str, true)
gtest.Assert(objOne.String(), str)
})
}
func TestBool(t *testing.T) {
gtest.Case(t, func() {
var ok bool = true
objOne := gvar.New(ok, true)
gtest.Assert(objOne.Bool(), ok)
ok = false
objTwo := gvar.New(ok, true)
gtest.Assert(objTwo.Bool(), ok)
})
}
func TestInt(t *testing.T) {
gtest.Case(t, func() {
var num int = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Int(), num)
})
}
func TestInt8(t *testing.T) {
gtest.Case(t, func() {
var num int8 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Int8(), num)
})
}
func TestInt16(t *testing.T) {
gtest.Case(t, func() {
var num int16 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Int16(), num)
})
}
func TestInt32(t *testing.T) {
gtest.Case(t, func() {
var num int32 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Int32(), num)
})
}
func TestInt64(t *testing.T) {
gtest.Case(t, func() {
var num int64 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Int64(), num)
})
}
func TestUint(t *testing.T) {
gtest.Case(t, func() {
var num uint = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Uint(), num)
})
}
func TestUint8(t *testing.T) {
gtest.Case(t, func() {
var num uint8 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Uint8(), num)
})
}
func TestUint16(t *testing.T) {
gtest.Case(t, func() {
var num uint16 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Uint16(), num)
})
}
func TestUint32(t *testing.T) {
gtest.Case(t, func() {
var num uint32 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Uint32(), num)
})
}
func TestUint64(t *testing.T) {
gtest.Case(t, func() {
var num uint64 = 1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Uint64(), num)
})
}
func TestFloat32(t *testing.T) {
gtest.Case(t, func() {
var num float32 = 1.1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Float32(), num)
})
}
func TestFloat64(t *testing.T) {
gtest.Case(t, func() {
var num float64 = 1.1
objOne := gvar.New(num, true)
gtest.Assert(objOne.Float64(), num)
})
}
func TestInts(t *testing.T) {
gtest.Case(t, func() {
var arr = []int{1, 2, 3, 4, 5}
objOne := gvar.New(arr, true)
gtest.Assert(objOne.Ints()[0], arr[0])
})
}
func TestFloats(t *testing.T) {
gtest.Case(t, func() {
var arr = []float64{1, 2, 3, 4, 5}
objOne := gvar.New(arr, true)
gtest.Assert(objOne.Floats()[0], arr[0])
})
}
func TestStrings(t *testing.T) {
gtest.Case(t, func() {
var arr = []string{"hello", "world"}
objOne := gvar.New(arr, true)
gtest.Assert(objOne.Strings()[0], arr[0])
})
}
func TestTime(t *testing.T) {
gtest.Case(t, func() {
var timeUnix int64 = 1556242660
objOne := gvar.New(timeUnix, true)
gtest.Assert(objOne.Time().Unix(), timeUnix)
})
}
type StTest struct {
Test int
}
func TestStruct(t *testing.T) {
gtest.Case(t, func() {
Kv := make(map[string]int, 1)
Kv["Test"] = 100
testObj := &StTest{}
objOne := gvar.New(Kv, true)
objOne.Struct(testObj)
gtest.Assert(testObj.Test, Kv["Test"])
})
}

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

@ -14,12 +14,12 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"time"
)
@ -147,30 +147,34 @@ type Map = map[string]interface{}
type List = []Map
const (
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
// 默认批量操作的数量值(Batch*操作)
gDEFAULT_BATCH_NUM = 10
// 默认的连接池连接存活时间(秒)
gDEFAULT_CONN_MAX_LIFE_TIME = 30
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
)
// 使用默认/指定分组配置进行连接数据库集群配置项default
func New(groupName ...string) (db DB, err error) {
group := config.d
if len(groupName) > 0 {
group = groupName[0]
}
config.RLock()
defer config.RUnlock()
var (
// Instance map.
instances = gmap.NewStringInterfaceMap()
)
if len(config.c) < 1 {
// New creates ORM DB object with global configurations.
// The param <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func New(name ...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
configs.RLock()
defer configs.RUnlock()
if len(configs.config) < 1 {
return nil, errors.New("empty database configuration")
}
if _, ok := config.c[group]; ok {
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase {
group : group,
@ -204,9 +208,27 @@ func New(groupName ...string) (db DB, err error) {
}
}
// Instance returns an instance for DB operations.
// The param <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func Instance(name ...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
}
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
if list, ok := config.c[group]; ok {
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
@ -319,17 +341,17 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
return
}
// 切换操作的数据库(注意该切换是全局的)
// 切换当前数据库对象操作的数据库。
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
}
// 创建底层数据库master链接对象
// 创建底层数据库master链接对象
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true)
}
// 创建底层数据库slave链接对象
// 创建底层数据库slave链接对象
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false)
}

View File

@ -17,14 +17,7 @@ const (
DEFAULT_GROUP_NAME = "default" // 默认配置名称
)
// 数据库配置包内对象
var config struct {
sync.RWMutex
c Config // 数据库配置
d string // 默认数据库分组名称
}
// 数据库配置
// 数据库分组配置
type Config map[string]ConfigGroup
// 数据库集群配置
@ -41,12 +34,19 @@ type ConfigNode struct {
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf8)编码,默认为 utf8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
Linkinfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
MaxIdleConnCount int // (可选)连接池最大限制的连接数
MaxOpenConnCount int // (可选)连接池最大打开的连接数
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
}
// 数据库配置包内对象
var configs struct {
sync.RWMutex // 并发安全互斥锁
config Config // 数据库分组配置
defaultGroup string // 默认数据库分组名称
}
// 数据库集群配置示例,支持主从处理,多数据库集群支持
/*
var DatabaseConfiguration = Config {
@ -80,29 +80,32 @@ var DatabaseConfiguration = Config {
// 包初始化
func init() {
config.c = make(Config)
config.d = DEFAULT_GROUP_NAME
configs.config = make(Config)
configs.defaultGroup = DEFAULT_GROUP_NAME
}
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
func SetConfig (c Config) {
config.Lock()
defer config.Unlock()
config.c = c
func SetConfig (config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config = config
}
// 添加数据库服务器集群配置
func AddConfigGroup (group string, nodes ConfigGroup) {
config.Lock()
config.c[group] = nodes
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = nodes
}
// 添加一台数据库服务器配置
func AddConfigNode (group string, node ConfigNode) {
config.Lock()
config.c[group] = append(config.c[group], node)
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
}
// 添加默认链接的一台数据库服务器配置
@ -117,16 +120,25 @@ func AddDefaultConfigGroup (nodes ConfigGroup) {
// 添加一台数据库服务器配置
func GetConfig (group string) ConfigGroup {
config.RLock()
defer config.RUnlock()
return config.c[group]
configs.RLock()
defer configs.RUnlock()
return configs.config[group]
}
// 设置默认链接的数据库链接配置项(默认是 default)
func SetDefaultGroup (groupName string) {
config.Lock()
config.d = groupName
config.Unlock()
func SetDefaultGroup (name string) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.defaultGroup = name
}
// 获取默认链接的数据库链接配置项(默认是 default)
func GetDefaultGroup() string {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
return configs.defaultGroup
}
// 设置数据库连接池中空闲链接的大小
@ -147,8 +159,8 @@ func (bs *dbBase) SetConnMaxLifetime(n int) {
// 节点配置转换为字符串
func (node *ConfigNode) String() string {
if node.Linkinfo != "" {
return node.Linkinfo
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset,

View File

@ -15,7 +15,6 @@ import (
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"reflect"
"strings"
)

View File

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

View File

@ -30,8 +30,8 @@ type dbMssql struct {
// 创建SQL操作对象
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
source := ""
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name)

View File

@ -8,8 +8,9 @@
package gdb
import (
"fmt"
"database/sql"
"database/sql"
"fmt"
_ "github.com/gogf/gf/third/github.com/gf-third/mysql"
)
// 数据库链接对象
@ -20,13 +21,13 @@ type dbMysql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
}
if db, err := sql.Open("mysql", source); err == nil {
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
} else {
return nil, err

View File

@ -30,8 +30,8 @@ type dbOracle struct {
// 创建SQL操作对象
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name)
}

View File

@ -26,8 +26,8 @@ type dbPgsql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbPgsql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
}

View File

@ -24,8 +24,8 @@ type dbSqlite struct {
func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = config.Name
}

View File

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

View File

@ -0,0 +1,28 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb_test
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
_, err := gdb.Instance("none")
gtest.AssertNE(err, nil)
db, err := gdb.Instance()
gtest.Assert(err, nil)
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
}

View File

@ -4,10 +4,11 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gredis provides client for redis server.
// Package gredis provides convenient client for redis server.
//
// Redis客户端.
// Redis中文手册文档请参考http://redisdoc.com/ , Redis官方命令请参考https://redis.io/commands
// Redis中文手册请参考http://redisdoc.com/
// Redis官方命令请参考https://redis.io/commands
package gredis
import (
@ -24,8 +25,9 @@ const (
// Redis客户端(管理连接池)
type Redis struct {
pool *redis.Pool
config Config
pool *redis.Pool // 底层连接池
group string // 配置分组
config Config // 配置对象
}
// Redis连接对象(连接池中的单个连接)
@ -48,13 +50,17 @@ type PoolStats struct {
redis.PoolStats
}
// 连接池map
var pools = gmap.NewStringInterfaceMap()
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
// 连接池Map
pools = gmap.NewStringInterfaceMap()
)
// New creates a redis client object with given configuration.
// Redis client maintains a connection pool automatically.
//
// 创建redis操作对象.
// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。
func New(config Config) *Redis {
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
@ -85,7 +91,8 @@ func New(config Config) *Redis {
}
return c, nil
},
// 用来测试连接是否可用
// 在被应用从连接池中获取出来之后,用以测试连接是否可用
// 如果返回error那么关闭该连接对象重新创建新的连接。
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
@ -95,11 +102,41 @@ func New(config Config) *Redis {
}
}
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// return redis instance with default group.
//
// 获取指定分组名称的Redis单例对象底层根据配置信息公用的连接池连接池单例
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It always not necessary to call Close manually.
//
// 关闭redis管理对象将会关闭底层的连接池。
// 往往没必要手动调用,跟随进程销毁即可。
func (r *Redis) Close() error {
if r.group != "" {
// 如果是单例对象那么需要从单例对象Map中删除
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
}

View File

@ -0,0 +1,61 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gredis
import "github.com/gogf/gf/g/container/gmap"
const (
// Default configuration group name.
DEFAULT_GROUP_NAME = "default"
)
var (
// Configuration groups.
configs = gmap.NewStringInterfaceMap()
)
// SetConfig sets the global configuration for specified group.
// If <name> is not passed, it sets configuration for the default group name.
func SetConfig(config Config, name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Set(group, config)
instances.Remove(group)
}
// GetConfig returns the global configuration with specified group name.
// If <name> is not passed, it returns configuration of the default group name.
func GetConfig(name...string) (config Config, ok bool) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
if v := configs.Get(group); v != nil {
return v.(Config), true
}
return Config{}, false
}
// RemoveConfig removes the global configuration with specified group.
// If <name> is not passed, it removes configuration of the default group name.
func RemoveConfig(name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Remove(group)
instances.Remove(group)
}
// ClearConfig removes all configurations and instances of redis.
func ClearConfig() {
configs.Clear()
instances.Clear()
}

View File

@ -99,6 +99,33 @@ func Test_Conn(t *testing.T) {
conn := redis.Conn()
defer conn.Close()
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
group := "my-test"
gredis.SetConfig(config, group)
defer gredis.RemoveConfig(group)
redis := gredis.Instance(group)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
_, err := conn.Do("SET", "k", "v")
gtest.Assert(err, nil)
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))

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

@ -5,13 +5,15 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gbinary provides useful API for handling binary/bytes data.
//
// 注意gbinary模块统一使用LittleEndian进行编码。
package gbinary
import (
"fmt"
"math"
"bytes"
"encoding/binary"
"fmt"
"math"
"bytes"
"encoding/binary"
)
// 二进制位(0|1)
@ -20,304 +22,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

@ -51,5 +51,5 @@ func ToUTF8(charset string, src string) (dst string, err error) {
// UTF8转指定字符集
func UTF8To(charset string, src string) (dst string, err error) {
return Convert(charset, "UTF-8", src)
return Convert(charset, "UTF-8", src)
}

View File

@ -1,10 +1,16 @@
package gcharset
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gcharset_test
import (
"github.com/gogf/gf/g/encoding/gcharset"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
@ -52,7 +58,7 @@ var testData = []struct {
func TestDecode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert("UTF-8", data.otherEncoding, data.other)
str, err := gcharset.Convert("UTF-8", data.otherEncoding, data.other)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -68,7 +74,7 @@ func TestDecode(t *testing.T) {
func TestEncode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert(data.otherEncoding, "UTF-8", data.utf8)
str, err := gcharset.Convert(data.otherEncoding, "UTF-8", data.utf8)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -86,7 +92,7 @@ func TestConvert(t *testing.T) {
dstCharset := "gbk"
dst := "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed"
str, err := Convert(dstCharset, srcCharset, src)
str, err := gcharset.Convert(dstCharset, srcCharset, src)
if err != nil {
t.Errorf("convert error. %v", err)
return

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

@ -4,18 +4,19 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gjson provides quite flexible and useful API for JSON/XML/YAML/TOML content handling.
// Package gjson provides convenient API for JSON/XML/YAML/TOML data handling.
package gjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
"github.com/gogf/gf/g/encoding/gyaml"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
@ -26,32 +27,38 @@ import (
)
const (
gDEFAULT_SPLIT_CHAR = '.' // 默认层级分隔符号
// Separator char for hierarchical data access.
gDEFAULT_SPLIT_CHAR = '.'
)
// json解析结果存放数组
// The customized JSON struct.
type Json struct {
mu *rwmutex.RWMutex
p *interface{} // 注意这是一个指针
c byte // 层级分隔符,默认为"."
vc bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
c byte // Char separator('.' in default).
vc bool // Violence Check(false in default), which is used to access data
// when the hierarchical data key contains separator char.
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者slice否者转换没有意义
func New(value interface{}, unsafe...bool) *Json {
// New creates a Json object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Json object
// in un-concurrent-safe context, which is false in default.
func New(data interface{}, unsafe...bool) *Json {
j := (*Json)(nil)
switch value.(type) {
switch data.(type) {
case map[string]interface{}, []interface{}, nil:
j = &Json {
p : &value,
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
case string, []byte:
j, _ = LoadContent(gconv.Bytes(value))
j, _ = LoadContent(gconv.Bytes(data))
default:
v := (interface{})(nil)
if m := gconv.Map(value); m != nil {
if m := gconv.Map(data); m != nil {
v = m
j = &Json {
p : &v,
@ -59,7 +66,7 @@ func New(value interface{}, unsafe...bool) *Json {
vc : false,
}
} else {
v = gconv.Interfaces(value)
v = gconv.Interfaces(data)
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
@ -71,114 +78,130 @@ func New(value interface{}, unsafe...bool) *Json {
return j
}
// 创建一个非并发安全的Json对象
func NewUnsafe(value...interface{}) *Json {
if len(value) > 0 {
return New(value[0], true)
// NewUnsafe creates a un-concurrent-safe Json object.
func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 {
return New(data[0], true)
}
return New(nil, true)
}
// 识别当前给定内容是否为JSON格式
func Valid (v interface{}) bool {
return json.Valid(gconv.Bytes(v))
// Valid checks whether <data> is a valid JSON data type.
func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data))
}
// 编码go变量为json字符串并返回json字符串指针
func Encode (v interface{}) ([]byte, error) {
return json.Marshal(v)
// Encode encodes <value> to JSON data type of bytes.
func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// 解码字符串为interface{}变量
func Decode (b []byte) (interface{}, error) {
var v interface{}
if err := DecodeTo(b, &v); err != nil {
// Decode decodes <data>(string/[]byte) to golang variable.
func Decode(data interface{}) (interface{}, error) {
var value interface{}
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
return nil, err
} else {
return v, nil
return value, nil
}
}
// 解析json字符串为go变量注意第二个参数为指针(任意结构的变量)
func DecodeTo (b []byte, v interface{}) error {
return json.Unmarshal(b, v)
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
// The <v> should be a pointer type.
func DecodeTo(data interface{}, v interface{}) error {
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
decoder.UseNumber()
return decoder.Decode(v)
}
// 解析json字符串为gjson.Json对象并返回操作对象指针
func DecodeToJson (b []byte) (*Json, error) {
if v, err := Decode(b); err != nil {
// DecodeToJson codes <data>(string/[]byte) to a Json object.
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
if v, err := Decode(gconv.Bytes(data)); err != nil {
return nil, err
} else {
return New(v), nil
return New(v, unsafe...), nil
}
}
// 支持多种配置文件类型转换为json格式内容并解析为gjson.Json对象
func Load (path string) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), gfile.Ext(path))
// Load loads content from specified file <path>,
// and creates a Json object from its content.
func Load(path string, unsafe...bool) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), unsafe...)
}
// 支持的配置文件格式xml, json, yaml/yml, toml,
// 默认为自动识别当无法检测成功时使用json解析。
func LoadContent(data []byte, dataType...string) (*Json, error) {
// LoadContent creates a Json object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
var err error
var result interface{}
b := gconv.Bytes(data)
t := "json"
if len(dataType) > 0 {
t = dataType[0]
// auto check data type
if json.Valid(b) {
t = "json"
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
t = "xml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
t = "yml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
t = "toml"
} else {
if gregex.IsMatch(`<.+>.*</.+>`, data) {
t = "xml"
} else if gregex.IsMatch(`\w+\s*:\s*\w+`, data) {
t = "yml"
} else if gregex.IsMatch(`\w+\s*=\s*\w+`, data) {
t = "toml"
}
return nil, errors.New("unsupported data type")
}
// convert to json type data
switch t {
case "xml", ".xml":
data, err = gxml.ToJson(data)
if err != nil {
return nil, err
}
case "json", ".json":
// ok
case "xml", ".xml":
// TODO UseNumber
b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml":
data, err = gyaml.ToJson(data)
if err != nil {
return nil, err
}
case "yml", "yaml", ".yml", ".yaml":
// TODO UseNumber
b, err = gyaml.ToJson(b)
case "toml", ".toml":
data, err = gtoml.ToJson(data)
if err != nil {
return nil, err
}
case "toml", ".toml":
// TODO UseNumber
b, err = gtoml.ToJson(b)
default:
err = errors.New("nonsupport type " + t)
}
if err != nil {
return nil, err
}
if result == nil {
if err := json.Unmarshal(data, &result); err != nil {
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
}
}
return New(result), nil
return New(result, unsafe...), nil
}
// 设置自定义的层级分隔符号
// SetSplitChar sets the separator char for hierarchical data access.
func (j *Json) SetSplitChar(char byte) {
j.mu.Lock()
j.c = char
j.mu.Unlock()
}
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
func (j *Json) SetViolenceCheck(check bool) {
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (j *Json) SetViolenceCheck(enabled bool) {
j.mu.Lock()
j.vc = check
j.vc = enabled
j.mu.Unlock()
}
// 将指定的json内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
// 注意第二个参数需要给的是**变量地址**
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <v> should be a pointer type.
func (j *Json) GetToVar(pattern string, v interface{}) error {
r := j.Get(pattern)
if r != nil {
@ -193,19 +216,18 @@ func (j *Json) GetToVar(pattern string, v interface{}) error {
return nil
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (j *Json) GetMap(pattern string) map[string]interface{} {
result := j.Get(pattern)
if result != nil {
if r, ok := result.(map[string]interface{}); ok {
return r
}
return gconv.Map(result)
}
return nil
}
// 将检索值转换为Json对象指针返回
// GetJson gets the value by specified <pattern>,
// and converts it to a Json object.
func (j *Json) GetJson(pattern string) *Json {
result := j.Get(pattern)
if result != nil {
@ -214,28 +236,39 @@ func (j *Json) GetJson(pattern string) *Json {
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (j *Json) GetArray(pattern string) []interface{} {
result := j.Get(pattern)
if result != nil {
if r, ok := result.([]interface{}); ok {
return r
// GetJsons gets the value by specified <pattern>,
// and converts it to a slice of Json object.
func (j *Json) GetJsons(pattern string) []*Json {
array := j.GetArray(pattern)
if len(array) > 0 {
jsons := make([]*Json, len(array))
for i := 0; i < len(array); i++ {
jsons[i] = New(array[i], !j.mu.IsSafe())
}
return jsons
}
return nil
}
// 返回指定json中的string
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (j *Json) GetArray(pattern string) []interface{} {
return gconv.Interfaces(j.Get(pattern))
}
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (j *Json) GetString(pattern string) string {
return gconv.String(j.Get(pattern))
}
// 返回指定json中的strings(转换为[]string数组)
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (j *Json) GetStrings(pattern string) []string {
return gconv.Strings(j.Get(pattern))
}
// See GetArray.
func (j *Json) GetInterfaces(pattern string) []interface{} {
return gconv.Interfaces(j.Get(pattern))
}
@ -248,7 +281,10 @@ func (j *Json) GetTimeDuration(pattern string) time.Duration {
return gconv.TimeDuration(j.Get(pattern))
}
// 返回指定json中的bool(false:"", 0, false, off)
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (j *Json) GetBool(pattern string) bool {
return gconv.Bool(j.Get(pattern))
}
@ -309,25 +345,29 @@ func (j *Json) GetFloats(pattern string) []float64 {
return gconv.Floats(j.Get(pattern))
}
// 将指定变量转换为struct对象(对象属性赋值)
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <objPointer>.
// The <objPointer> should be the pointer to an object.
func (j *Json) GetToStruct(pattern string, objPointer interface{}) error {
return gconv.Struct(j.Get(pattern), objPointer)
}
// 动态设置层级变量
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Set(pattern string, value interface{}) error {
return j.setValue(pattern, value, false)
}
// 动态删除层级变量
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Remove(pattern string) error {
return j.setValue(pattern, nil, true)
}
// 根据pattern查找并设置数据
// 注意:
// 1、写入的value为nil且removed为true时表示删除;
// 2、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值;
// Set <value> by <pattern>.
// Notice:
// 1. If value is nil and removed is true, means deleting this value;
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
array := strings.Split(pattern, string(j.c))
length := len(array)
@ -346,144 +386,144 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
defer j.mu.Unlock()
for i:= 0; i < length; i++ {
switch (*pointer).(type) {
case map[string]interface{}:
if i == length - 1 {
if removed && value == nil {
// 删除map元素
delete((*pointer).(map[string]interface{}), array[i])
} else {
(*pointer).(map[string]interface{})[array[i]] = value
}
} else {
// 当键名不存在的情况这里会进行处理
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
if removed && value == nil {
goto done
}
// 创建新节点
if gstr.IsNumeric(array[i + 1]) {
// 创建array节点
n, _ := strconv.Atoi(array[i + 1])
var v interface{} = make([]interface{}, n + 1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
} else {
// 创建map节点
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
pparent = pointer
pointer = &v
}
}
case []interface{}:
// 键名与当前指针类型不符合,需要执行**覆盖操作**
if !gstr.IsNumeric(array[i]) {
case map[string]interface{}:
if i == length - 1 {
*pointer = map[string]interface{}{ array[i] : value }
if removed && value == nil {
// 删除map元素
delete((*pointer).(map[string]interface{}), array[i])
} else {
(*pointer).(map[string]interface{})[array[i]] = value
}
} else {
var v interface{} = make(map[string]interface{})
*pointer = v
pparent = pointer
pointer = &v
// 当键名不存在的情况这里会进行处理
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
if removed && value == nil {
goto done
}
// 创建新节点
if gstr.IsNumeric(array[i + 1]) {
// 创建array节点
n, _ := strconv.Atoi(array[i + 1])
var v interface{} = make([]interface{}, n + 1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
} else {
// 创建map节点
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
pparent = pointer
pointer = &v
}
}
continue
}
valn, err := strconv.Atoi(array[i])
if err != nil {
return err
}
// 叶子节点
if i == length - 1 {
if len((*pointer).([]interface{})) > valn {
if removed && value == nil {
// 删除数据元素
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
case []interface{}:
// 键名与当前指针类型不符合,需要执行**覆盖操作**
if !gstr.IsNumeric(array[i]) {
if i == length - 1 {
*pointer = map[string]interface{}{ array[i] : value }
} else {
(*pointer).([]interface{})[valn] = value
}
} else {
if removed && value == nil {
goto done
}
if pparent == nil {
// 表示根节点
j.setPointerWithValue(pointer, array[i], value)
} else {
// 非根节点
s := make([]interface{}, valn + 1)
copy(s, (*pointer).([]interface{}))
s[valn] = value
j.setPointerWithValue(pparent, array[i - 1], s)
var v interface{} = make(map[string]interface{})
*pointer = v
pparent = pointer
pointer = &v
}
continue
}
} else {
if gstr.IsNumeric(array[i + 1]) {
n, _ := strconv.Atoi(array[i + 1])
valn, err := strconv.Atoi(array[i])
if err != nil {
return err
}
// 叶子节点
if i == length - 1 {
if len((*pointer).([]interface{})) > valn {
(*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
pparent = pointer
pointer = &(*pointer).([]interface{})[valn]
if removed && value == nil {
// 删除数据元素
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
} else {
(*pointer).([]interface{})[valn] = value
}
} else {
if removed && value == nil {
goto done
}
var v interface{} = make([]interface{}, n + 1)
if pparent == nil {
// 表示根节点
j.setPointerWithValue(pointer, array[i], value)
} else {
// 非根节点
s := make([]interface{}, valn + 1)
copy(s, (*pointer).([]interface{}))
s[valn] = value
j.setPointerWithValue(pparent, array[i - 1], s)
}
}
} else {
if gstr.IsNumeric(array[i + 1]) {
n, _ := strconv.Atoi(array[i + 1])
if len((*pointer).([]interface{})) > valn {
(*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
pparent = pointer
pointer = &(*pointer).([]interface{})[valn]
} else {
if removed && value == nil {
goto done
}
var v interface{} = make([]interface{}, n + 1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
}
// 如果当前指针指向的变量不是引用类型的,
// 那么修改变量必须通过父级进行修改,即 pparent
default:
if removed && value == nil {
goto done
}
if gstr.IsNumeric(array[i]) {
n, _ := strconv.Atoi(array[i])
s := make([]interface{}, n + 1)
if i == length - 1 {
s[n] = value
default:
if removed && value == nil {
goto done
}
if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i - 1], s)
} else {
*pointer = s
pparent = pointer
}
} else {
var v interface{} = make(map[string]interface{})
if i == length - 1 {
v = map[string]interface{}{
array[i] : value,
if gstr.IsNumeric(array[i]) {
n, _ := strconv.Atoi(array[i])
s := make([]interface{}, n + 1)
if i == length - 1 {
s[n] = value
}
if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i - 1], s)
} else {
*pointer = s
pparent = pointer
}
}
if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i - 1], v)
} else {
*pointer = v
pparent = pointer
var v interface{} = make(map[string]interface{})
if i == length - 1 {
v = map[string]interface{}{
array[i] : value,
}
}
if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i - 1], v)
} else {
*pointer = v
pparent = pointer
}
pointer = &v
}
pointer = &v
}
}
}
done:
return nil
}
// 数据结构转换map参数必须转换为map[string]interface{},数组参数必须转换为[]interface{}
// Convert <value> to map[string]interface{} or []interface{},
// which can be supported for hierarchical data access.
func (j *Json) convertValue(value interface{}) interface{} {
switch value.(type) {
case map[string]interface{}:
@ -503,7 +543,7 @@ func (j *Json) convertValue(value interface{}) interface{} {
case reflect.Map: return gconv.Map(value)
case reflect.Struct: return gconv.Map(value)
default:
// 最后使用JSON编解码
// Use json decode/encode at last.
b, _ := Encode(value)
v, _ := Decode(b)
return v
@ -511,8 +551,8 @@ func (j *Json) convertValue(value interface{}) interface{} {
}
}
// 用于Set方法中,对指针指向的内存地址进行赋值
// 返回修改后的父级指针
// Set <key>:<value> to <pointer>, the <key> may be a map key or slice index.
// It returns the pointer to the new value set.
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
switch (*pointer).(type) {
case map[string]interface{}:
@ -536,9 +576,12 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
return pointer
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"; 当pattern为空时表示获取所有数据;
// 返回的结果类型的interface{},因此需要自己做类型转换;
// 如果找不到对应节点的数据返回nil;
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
func (j *Json) Get(pattern...string) interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
@ -559,13 +602,14 @@ func (j *Json) Get(pattern...string) interface{} {
return nil
}
// 判断锁给定pattern是否数据存在
// Contains checks whether the value by specified <pattern> exist.
func (j *Json) Contains(pattern...string) bool {
return j.Get(pattern...) != nil
}
// 计算指定pattern的元素长度(pattern对应数据类型为map/slice时有效)。
// 当pattern对应的数据类型非map/slice时返回-1。
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (j *Json) Len(pattern string) int {
p := j.getPointerByPattern(pattern)
if p != nil {
@ -581,7 +625,8 @@ func (j *Json) Len(pattern string) int {
return -1
}
// 指定pattern追加元素
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (j *Json) Append(pattern string, value interface{}) error {
p := j.getPointerByPattern(pattern)
if p == nil {
@ -594,7 +639,7 @@ func (j *Json) Append(pattern string, value interface{}) error {
return fmt.Errorf("invalid variable type of %s", pattern)
}
// 根据pattern获取对应元素项的指针
// Get a pointer to the value by specified <pattern>.
func (j *Json) getPointerByPattern(pattern string) *interface{} {
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
@ -603,18 +648,7 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
}
}
// 根据pattern层级查找**变量指针**, 执行冲突检测。
// 检索方式:例如检索 a.a.a 值为1
// 1. 检索 a.a.a.a 是否存在对应map的键名
// 2. 检索 a.a.a 是否存在对应map的键名
// 3. 检索 a.a 是否存在对应map的键名
// 4. 检索 a 是否存在对应map的键名如果检索出这是一个map假如为变量m1
// 5. 在m1中检索 a.a.a 否存在对应map的键名
// 6. 在m1中检索 a.a 否存在对应map的键名
// 7. 在m1中检索 a 否存在对应map的键名如果检索出这是一个map假如为变量m2
// 8. 在m2中检索 a.a 否存在对应map的键名
// 9. 在m2中检索 a 否存在对应map的键名检索到有值值为1
// 这样检索的复杂度很高,主要是为了避免键名中存在分隔符号(默认为".")的情况,避免歧义。
// Get a pointer to the value of specified <pattern> with violence check.
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
if !j.vc {
return j.getPointerByPatternWithoutViolenceCheck(pattern)
@ -640,7 +674,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
pointer = r
}
} else {
// 查找下一个分割符号的索引位置
// Get the position for next separator char.
index = strings.LastIndexByte(pattern[start:index], j.c)
if index != -1 && length > 0 {
index += length + 1
@ -653,7 +687,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
return nil
}
// 层级检索,内部不执行分隔符冲突检查,检索效率会有所提高,但是冲突需要开发者自己根据自定义的分隔符来进行解决
// Get a pointer to the value of specified <pattern>, with no violence check.
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
@ -677,8 +711,8 @@ func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interfac
return nil
}
// 判断给定的key在当前的pointer下是否有值并返回对应的pointer,
// 注意这里返回的指针都是临时变量的内存地址
// Check whether there's value by <key> in specified <pointer>.
// It returns a pointer to the value.
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
switch (*pointer).(type) {
case map[string]interface{}:
@ -696,7 +730,8 @@ func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interfac
return nil
}
// 转换为map[string]interface{}类型,如果转换失败返回nil
// ToMap converts current Json object to map[string]interface{}.
// It returns nil if fails.
func (j *Json) ToMap() map[string]interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
@ -708,15 +743,16 @@ func (j *Json) ToMap() map[string]interface{} {
}
}
// 转换为[]interface{}类型,如果转换失败返回nil
// ToArray converts current Json object to []interface{}.
// It returns nil if fails.
func (j *Json) ToArray() []interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
switch (*(j.p)).(type) {
case []interface{}:
return (*(j.p)).([]interface{})
default:
return nil
case []interface{}:
return (*(j.p)).([]interface{})
default:
return nil
}
}
@ -724,42 +760,73 @@ func (j *Json) ToXml(rootTag...string) ([]byte, error) {
return gxml.Encode(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlString(rootTag...string) (string, error) {
b, e := j.ToXml(rootTag...)
return string(b), e
}
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
b, e := j.ToXmlIndent(rootTag...)
return string(b), e
}
func (j *Json) ToJson() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return Encode(*(j.p))
}
func (j *Json) ToJsonString() (string, error) {
b, e := j.ToJson()
return string(b), e
}
func (j *Json) ToJsonIndent() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return json.MarshalIndent(*(j.p), "", "\t")
}
func (j *Json) ToJsonIndentString() (string, error) {
b, e := j.ToJsonIndent()
return string(b), e
}
func (j *Json) ToYaml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gyaml.Encode(*(j.p))
}
func (j *Json) ToYamlString() (string, error) {
b, e := j.ToYaml()
return string(b), e
}
func (j *Json) ToToml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gtoml.Encode(*(j.p))
}
// 转换为指定的struct对象
func (j *Json) ToStruct(o interface{}) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Struct(*(j.p), o)
func (j *Json) ToTomlString() (string, error) {
b, e := j.ToToml()
return string(b), e
}
// 打印Json对象
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (j *Json) ToStruct(objPointer interface{}) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Struct(*(j.p), objPointer)
}
// Dump prints current Json object with more manually readable.
func (j *Json) Dump() error {
j.mu.RLock()
defer j.mu.RUnlock()

View File

@ -0,0 +1,253 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_New(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
}
func Test_NewUnsafe(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Valid(t *testing.T) {
data1 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
data2 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
gtest.Case(t, func() {
gtest.Assert(gjson.Valid(data1), true)
gtest.Assert(gjson.Valid(data2), false)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gjson.Encode(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
}
func Test_Decode(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
v, err := gjson.Decode(data)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
var v interface{}
err := gjson.DecodeTo(data, &v)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
j.SetSplitChar(byte('#'))
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
}
func Test_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
}
func Test_GetToVar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k" : "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
}
func Test_GetMap(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), nil)
})
}
func Test_GetJson(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j2 := j.GetJson("m")
gtest.AssertNE(j2, nil)
gtest.Assert(j2.Get("k"), "v")
gtest.Assert(j2.Get("a"), nil)
gtest.Assert(j2.Get("n"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
})
}
func Test_GetString(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
}
func Test_GetStrings(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}
func Test_GetInterfaces(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
})
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -0,0 +1,156 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Load_JSON(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
}
func Test_Load_YAML1(t *testing.T) {
data := []byte(`
a:
- 1
- 2
- 3
m:
k: v
"n": 123456789
`)
// YAML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_YAML2(t *testing.T) {
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_TOML1(t *testing.T) {
data := []byte(`
a = ["1", "2", "3"]
n = "123456789"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_TOML2(t *testing.T) {
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}

View File

@ -8,9 +8,7 @@ package gjson_test
import (
"bytes"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
@ -174,7 +172,7 @@ func Test_Set10(t *testing.T) {
func Test_Set11(t *testing.T) {
e := []byte(`{"a":{"b":{}}}`)
p, _ := gjson.LoadContent([]byte(`{"a":{"b":{"c":1}}}`), "json")
p, _ := gjson.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
p.Remove("a.b.c")
if c, err := p.ToJson(); err == nil {
@ -230,51 +228,3 @@ func Test_Set14(t *testing.T) {
t.Error(err)
}
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -4,7 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gp.
// Package gparser provides a flexible and easy way for accessing/converting variable and JSON/XML/YAML/TOML contents.
// Package gparser provides convenient API for accessing/converting variable and JSON/XML/YAML/TOML.
package gparser
import (
@ -16,13 +16,16 @@ type Parser struct {
json *gjson.Json
}
// 将变量转换为Parser对象进行处理该变量至少应当是一个map或者array否者转换没有意义
// value可以传递nil, 表示创建一个空的Parser对象
func New (value interface{}, unsafe...bool) *Parser {
// New creates a Parser object with any variable type of <data>,
// but <data> should be a map or slice for data access reason,
// or it will make no sense.
// The <unsafe> param specifies whether using this Parser object
// in un-concurrent-safe context, which is false in default.
func New(value interface{}, unsafe...bool) *Parser {
return &Parser{gjson.New(value, unsafe...)}
}
// 非并发安全Parser对象
// NewUnsafe creates a un-concurrent-safe Parser object.
func NewUnsafe (value...interface{}) *Parser {
if len(value) > 0 {
return &Parser{gjson.New(value[0], false)}
@ -30,57 +33,64 @@ func NewUnsafe (value...interface{}) *Parser {
return &Parser{gjson.New(nil, false)}
}
func Load (path string) (*Parser, error) {
if j, e := gjson.Load(path); e == nil {
// Load loads content from specified file <path>,
// and creates a Parser object from its content.
func Load (path string, unsafe...bool) (*Parser, error) {
if j, e := gjson.Load(path, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}
// 支持的数据内容格式json(默认), xml, yaml/yml, toml
func LoadContent (data []byte, dataType...string) (*Parser, error) {
if j, e := gjson.LoadContent(data, dataType...); e == nil {
// LoadContent creates a Parser object from given content,
// it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data.
func LoadContent (data []byte, unsafe...bool) (*Parser, error) {
if j, e := gjson.LoadContent(data, unsafe...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
}
}
// 设置自定义的层级分隔符号
// SetSplitChar sets the separator char for hierarchical data access.
func (p *Parser) SetSplitChar(char byte) {
p.json.SetSplitChar(char)
}
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
// SetViolenceCheck enables/disables violence check for hierarchical data access.
func (p *Parser) SetViolenceCheck(check bool) {
p.json.SetViolenceCheck(check)
}
// 将指定的json内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
// 注意第二个参数需要给的是变量地址
// GetToVar gets the value by specified <pattern>,
// and converts it to specified golang variable <v>.
// The <v> should be a pointer type.
func (p *Parser) GetToVar(pattern string, v interface{}) error {
return p.json.GetToVar(pattern, v)
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (p *Parser) GetMap(pattern string) map[string]interface{} {
return p.json.GetMap(pattern)
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
// GetArray gets the value by specified <pattern>,
// and converts it to a slice of []interface{}.
func (p *Parser) GetArray(pattern string) []interface{} {
return p.json.GetArray(pattern)
}
// 返回指定json中的string
// GetString gets the value by specified <pattern>,
// and converts it to string.
func (p *Parser) GetString(pattern string) string {
return p.json.GetString(pattern)
}
// GetStrings gets the value by specified <pattern>,
// and converts it to a slice of []string.
func (p *Parser) GetStrings(pattern string) []string {
return p.json.GetStrings(pattern)
}
@ -97,7 +107,10 @@ func (p *Parser) GetTimeDuration(pattern string) time.Duration {
return p.json.GetTimeDuration(pattern)
}
// 返回指定json中的bool(false:"", 0, false, off)
// GetBool gets the value by specified <pattern>,
// and converts it to bool.
// It returns false when value is: "", 0, false, off, nil;
// or returns true instead.
func (p *Parser) GetBool(pattern string) bool {
return p.json.GetBool(pattern)
}
@ -158,51 +171,60 @@ func (p *Parser) GetFloats(pattern string) []float64 {
return p.json.GetFloats(pattern)
}
// 将指定变量转换为struct对象(对象属性赋值)
// GetToStruct gets the value by specified <pattern>,
// and converts it to specified object <objPointer>.
// The <objPointer> should be the pointer to an object.
func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error {
return p.json.GetToStruct(pattern, objPointer)
}
// 根据pattern查找并设置数据
// 注意:写入的时候"."符号只能表示层级,不能使用带"."符号的键名
// Set sets value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Set(pattern string, value interface{}) error {
return p.json.Set(pattern, value)
}
// 计算指定pattern的元素长度(pattern对应数据类型为map[string]interface{}/[]interface{}时有效)
// Len returns the length/size of the value by specified <pattern>.
// The target value by <pattern> should be type of slice or map.
// It returns -1 if the target value is not found, or its type is invalid.
func (p *Parser) Len(pattern string) int {
return p.json.Len(pattern)
}
// 指定pattern追加元素
// Append appends value to the value by specified <pattern>.
// The target value by <pattern> should be type of slice.
func (p *Parser) Append(pattern string, value interface{}) error {
return p.json.Append(pattern, value)
}
// 动态删除变量节点
// Remove deletes value with specified <pattern>.
// It supports hierarchical data access by char separator, which is '.' in default.
func (p *Parser) Remove(pattern string) error {
return p.json.Remove(pattern)
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"; 当pattern为空时表示获取所有数据
// 返回的结果类型的interface{},因此需要自己做类型转换;
// 如果找不到对应节点的数据返回nil;
// Get returns value by specified <pattern>.
// It returns all values of current Json object, if <pattern> is empty or not specified.
// It returns nil if no value found by <pattern>.
//
// We can also access slice item by its index number in <pattern>,
// eg: "items.name.first", "list.10".
func (p *Parser) Get(pattern...string) interface{} {
return p.json.Get(pattern...)
}
// 转换为map[string]interface{}类型,如果转换失败返回nil
// ToMap converts current object values to map[string]interface{}.
// It returns nil if fails.
func (p *Parser) ToMap() map[string]interface{} {
return p.json.ToMap()
}
// 转换为[]interface{}类型,如果转换失败返回nil
// ToArray converts current object values to []interface{}.
// It returns nil if fails.
func (p *Parser) ToArray() []interface{} {
return p.json.ToArray()
}
/* 以下为数据文件格式转换支持类型xml, json, yaml/yml, toml */
func (p *Parser) ToXml(rootTag...string) ([]byte, error) {
return p.json.ToXml(rootTag...)
}
@ -227,12 +249,13 @@ func (p *Parser) ToToml() ([]byte, error) {
return p.json.ToToml()
}
// 打印Json对象
// Dump prints current Json object with more manually readable.
func (p *Parser) Dump() error {
return p.json.Dump()
}
// 将变量解析为对应的struct对象注意传递的参数为struct对象指针
// ToStruct converts current Json object to specified object.
// The <objPointer> should be a pointer type.
func (p *Parser) ToStruct(o interface{}) error {
return p.json.ToStruct(o)
}
@ -261,7 +284,6 @@ func VarToToml(value interface{}) ([]byte, error) {
return New(value).ToToml()
}
// 将变量解析为对应的struct对象注意传递的参数为struct对象指针
func VarToStruct(value interface{}, obj interface{}) error {
return New(value).ToStruct(obj)
}

View File

@ -0,0 +1,208 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gparser_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_New(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
}
func Test_NewUnsafe(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gparser.VarToJson(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
}
func Test_Decode(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
j.SetSplitChar(byte('#'))
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
}
func Test_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
}
func Test_GetToVar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j := gparser.New(data)
gtest.AssertNE(j, nil)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k" : "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
}
func Test_GetMap(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetMap("n"), nil)
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
})
}
func Test_GetString(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
}
func Test_GetStrings(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), nil)
})
}
func Test_GetInterfaces(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gparser.New(data)
gtest.AssertNE(j, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
})
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gparser.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -0,0 +1,156 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gparser_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Load_JSON(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
}
func Test_Load_YAML1(t *testing.T) {
data := []byte(`
a:
- 1
- 2
- 3
m:
k: v
"n": 123456789
`)
// YAML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_YAML2(t *testing.T) {
data := []byte("i : 123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}
func Test_Load_TOML1(t *testing.T) {
data := []byte(`
a = ["1", "2", "3"]
n = "123456789"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gparser.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_TOML2(t *testing.T) {
data := []byte("i=123456789")
gtest.Case(t, func() {
j, err := gparser.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}

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 gparser_test
import (
@ -22,7 +20,6 @@ func Test_Set1(t *testing.T) {
})
p.Set("k1.k11", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)) != 0 {
t.Error("expect:", string(e))
}
@ -36,7 +33,6 @@ func Test_Set2(t *testing.T) {
p := gparser.New([]string{"a"})
p.Set("0.1", 1)
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -48,11 +44,10 @@ func Test_Set2(t *testing.T) {
func Test_Set3(t *testing.T) {
e := []byte(`{"kv":{"k1":"v1"}}`)
p := gparser.New([]string{"a"})
p.Set("kv", map[string]string{
p.Set("kv", map[string]string {
"k1" : "v1",
})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -68,7 +63,6 @@ func Test_Set4(t *testing.T) {
"k1" : "v1",
})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -82,7 +76,6 @@ func Test_Set5(t *testing.T) {
p := gparser.New([]string{"a"})
p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -96,7 +89,6 @@ func Test_Set6(t *testing.T) {
p := gparser.New([]string{"a"})
p.Set("1", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -113,7 +105,6 @@ func Test_Set7(t *testing.T) {
})
p.Set("0.1", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -130,7 +121,6 @@ func Test_Set8(t *testing.T) {
})
p.Set("0.0.0.0.0.0.1", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -147,7 +137,6 @@ func Test_Set9(t *testing.T) {
})
p.Set("k1.1", []int{1,2,3})
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -162,7 +151,6 @@ func Test_Set10(t *testing.T) {
p := gparser.New(nil)
p.Set("a.b.c", 1)
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -174,10 +162,9 @@ func Test_Set10(t *testing.T) {
func Test_Set11(t *testing.T) {
e := []byte(`{"a":{"b":{}}}`)
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`), "json")
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
p.Remove("a.b.c")
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -192,7 +179,6 @@ func Test_Set12(t *testing.T) {
p.Set("0", 0)
p.Set("1", 1)
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -207,7 +193,6 @@ func Test_Set13(t *testing.T) {
p.Set("array.0", 0)
p.Set("array.1", 1)
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
@ -216,7 +201,16 @@ func Test_Set13(t *testing.T) {
}
}
func Test_Set14(t *testing.T) {
e := []byte(`{"f":{"a":1}}`)
p := gparser.New(nil)
p.Set("f", "m")
p.Set("f.a", 1)
if c, err := p.ToJson(); err == nil {
if bytes.Compare(c, e) != 0 {
t.Error("expect:", string(e))
}
} else {
t.Error(err)
}
}

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

@ -5,75 +5,80 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gxml provides accessing and converting for XML content.
//
// XML数据格式解析。
package gxml
import (
"github.com/gogf/gf/third/github.com/clbanning/mxj"
"encoding/xml"
"io"
"fmt"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/third/github.com/axgle/mahonia"
"errors"
"fmt"
"github.com/gogf/gf/third/github.com/clbanning/mxj"
"strings"
)
// 将XML内容解析为map变量
func Decode(xmlbyte []byte) (map[string]interface{}, error) {
prepare(xmlbyte)
return mxj.NewMapXml(xmlbyte)
func Decode(content []byte) (map[string]interface{}, error) {
res, err := convert(content)
if err != nil {
return nil, err
}
return mxj.NewMapXml(res)
}
// 将map变量解析为XML格式内容
func Encode(v map[string]interface{}, rootTag...string) ([]byte, error) {
return mxj.Map(v).Xml(rootTag...)
func Encode(v map[string]interface{}, rootTag ...string) ([]byte, error) {
return mxj.Map(v).Xml(rootTag...)
}
func EncodeWithIndent(v map[string]interface{}, rootTag...string) ([]byte, error) {
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
func EncodeWithIndent(v map[string]interface{}, rootTag ...string) ([]byte, error) {
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
}
// XML格式内容直接转换为JSON格式内容
func ToJson(xmlbyte []byte) ([]byte, error) {
prepare(xmlbyte)
mv, err := mxj.NewMapXml(xmlbyte)
func ToJson(content []byte) ([]byte, error) {
res, err := convert(content)
if err != nil {
fmt.Println("convert error. ", err)
return nil, err
}
mv, err := mxj.NewMapXml(res)
if err == nil {
return mv.Json()
} else {
return nil, err
}
return mv.Json()
} else {
return nil, err
}
}
// XML字符集预处理
// @author wenzi1
// @date 20180604
func prepare(xmlbyte []byte) error {
// @date 20180604 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换
func convert(xmlbyte []byte) (res []byte, err error) {
patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>`
charsetReader := func(charset string, input io.Reader) (io.Reader, error) {
reader := mahonia.GetCharset(charset)
if reader == nil {
return nil, errors.New(fmt.Sprintf("not support charset:%s", charset))
}
return reader.NewDecoder().NewReader(input), nil
}
matchStr, err := gregex.MatchString(patten, string(xmlbyte))
if err != nil {
return err
return nil, err
}
xmlEncode := "UTF-8"
if len(matchStr) == 2 {
xmlEncode = matchStr[1]
xmlEncode = matchStr[1]
}
charset := mahonia.GetCharset(xmlEncode)
if charset == nil {
return errors.New(fmt.Sprintf("not support charset:%s", xmlEncode))
s := mahonia.GetCharset(xmlEncode)
if s == nil {
return nil, fmt.Errorf("not support charset:%s\n", xmlEncode)
}
if !strings.EqualFold(charset.Name, "UTF-8") {
mxj.CustomDecoder = &xml.Decoder{Strict : false, CharsetReader : charsetReader}
res, err = gregex.Replace(patten, []byte(""), []byte(xmlbyte))
if err != nil {
return nil, err
}
return nil
}
if !strings.EqualFold(s.Name, "UTF-8") {
res = []byte(s.NewDecoder().ConvertString(string(res)))
}
return res, nil
}

View File

@ -0,0 +1,140 @@
// 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 gxml_test
import (
"bytes"
"github.com/gogf/gf/g/encoding/gcharset"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gxml"
"strings"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
}
func buildXml(charset string, str string) (string, string) {
head := `<?xml version="1.0" encoding="UTF-8"?>`
srcXml := strings.Replace(head, "UTF-8", charset, -1)
srcParser := gparser.New(nil)
srcParser.Set("name", str)
srcParser.Set("age", "12")
s, err := srcParser.ToXml()
if err != nil {
return "", ""
}
srcXml = srcXml + string(s)
srcXml, err = gcharset.UTF8To(charset, srcXml)
if err != nil {
return "", ""
}
dstXml := head + string(s)
return srcXml, dstXml
}
//测试XML中字符集的转换
func Test_XmlToJson(t *testing.T) {
for _, v := range testData {
srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
if len(srcXml) == 0 && len(dstXml) == 0 {
t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
}
srcJson, err := gxml.ToJson([]byte(srcXml))
if err != nil {
t.Errorf("gxml.ToJson error. %s", srcXml)
}
dstJson, err := gxml.ToJson([]byte(dstXml))
if err != nil {
t.Errorf("dstXml to json error. %s", dstXml)
}
if bytes.Compare(srcJson, dstJson) != 0 {
t.Errorf("convert to json error. srcJson:%s, dstJson:%s", string(srcJson), string(dstJson))
}
}
}
func Test_Decode(t *testing.T) {
for _, v := range testData {
srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
if len(srcXml) == 0 && len(dstXml) == 0 {
t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
}
srcMap, err := gxml.Decode([]byte(srcXml))
if err != nil {
t.Errorf("gxml.Decode error. %s", srcXml)
}
dstMap, err := gxml.Decode([]byte(dstXml))
if err != nil {
t.Errorf("gxml decode error. %s", dstXml)
}
s := srcMap["doc"].(map[string]interface{})
d := dstMap["doc"].(map[string]interface{})
for kk, vv := range s {
if vv.(string) != d[kk].(string) {
t.Errorf("convert to map error. src:%v, dst:%v", vv, d[kk])
}
}
}
}
func Test_Encode(t *testing.T) {
m := make(map[string]interface{})
v := map[string]interface{}{
"string": "hello world",
"int": 123,
"float": 100.92,
"bool": true,
}
m["root"] = interface{}(v)
xmlStr, err := gxml.Encode(m)
if err != nil {
t.Errorf("encode error.")
}
t.Logf("%s\n", string(xmlStr))
res := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string></root>`
if string(xmlStr) != res {
t.Errorf("encode error. result: [%s], expect:[%s]", string(xmlStr), res)
}
}
func Test_EncodeIndent(t *testing.T) {
m := make(map[string]interface{})
v := map[string]interface{}{
"string": "hello world",
"int": 123,
"float": 100.92,
"bool": true,
}
m["root"] = interface{}(v)
xmlStr, err := gxml.EncodeWithIndent(m, "xml")
if err != nil {
t.Errorf("encodeWithIndent error.")
}
t.Logf("%s\n", string(xmlStr))
}

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

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