Compare commits

...

159 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
2534655bc8 测试覆盖率达到76.3% 2019-04-10 22:16:16 +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
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
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
aca1df634d 添加测试文件内容相关函数。 2019-04-09 23:11:04 +08:00
053a3c1a53 add unit test 2019-04-09 19:12:48 +08:00
468c315087 Merge pull request #4 from gogf/master
update 1.6
2019-04-09 17:36:40 +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
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
fd63a2209b 增加单元测试 2019-04-04 23:31:17 +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
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
20977558cc Merge pull request #1 from gogf/master
更新代码
2019-04-03 16:47:16 +08:00
f6aafc1d6b Merge pull request #5 from gogf/master
更新数据库操作
2019-03-18 10:00:23 +08:00
9014325a7c test 2019-03-12 10:54:38 +08:00
fc11856a28 Merge pull request #4 from gogf/master
日常更新
2019-03-11 17:43:06 +08:00
b0726b9733 Merge pull request #3 from gogf/master
日常更新
2019-03-10 22:38:15 +08:00
4e4ea25e7f Merge pull request #2 from gogf/master
第一次同步
2019-03-07 17:35:22 +08:00
209 changed files with 8474 additions and 8010 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

@ -64,15 +64,19 @@ func main() {
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Contributors
- [aloncn](https://github.com/aloncn)
- [chenyang351](https://github.com/chenyang351)
- [garfieldkwong](https://gitee.com/garfieldkwong)
- [hailaz](https://gitee.com/hailaz)
- [johng](https://gitee.com/johng)
- [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)
@ -85,6 +89,7 @@ func main() {
- [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)
@ -97,4 +102,3 @@ func main() {

View File

@ -30,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
@ -90,9 +89,12 @@ func main() {
- [chenyang351](https://github.com/chenyang351)
- [garfieldkwong](https://gitee.com/garfieldkwong)
- [hailaz](https://gitee.com/hailaz)
- [johng](https://gitee.com/johng)
- [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)
@ -105,6 +107,7 @@ func main() {
- [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)

View File

@ -43,6 +43,8 @@
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持
1. 添加sqlite数据库的单元测试用例
1. gredis增加cluster支持
1. gset.Add/Remove/Contains方法增加批量操作支持
1. gmlock增加手动清理机制当内存锁不再使用时由调用端决定是否清理内存锁
# DONE
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换

View File

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

@ -289,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,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 {
@ -20,21 +19,21 @@ type IntStringMap struct {
// 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 {
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...),
}
}
// 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 NewIntStringMapFrom(m map[int]string, unsafe ...bool) *IntStringMap {
return &IntStringMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// NewIntStringMapFromArray returns an IntStringMap object from given array.
@ -43,37 +42,37 @@ func NewIntStringMapFrom(m map[int]string, unsafe...bool) *IntStringMap {
//
// 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...),
}
func NewIntStringMapFromArray(keys []int, values []string, unsafe ...bool) *IntStringMap {
m := make(map[int]string)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = ""
}
}
return &IntStringMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *IntStringMap) Iterator(f func (k int, v string) bool) {
gm.mu.RLock()
defer gm.mu.RUnlock()
for k, v := range gm.m {
if !f(k, v) {
break
}
}
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 returns a copy of the data of the hash map.
@ -83,7 +82,7 @@ func (gm *IntStringMap) Map() map[int]string {
for k, v := range gm.m {
m[k] = v
}
gm.mu.RUnlock()
gm.mu.RUnlock()
return m
}
@ -117,40 +116,40 @@ func (gm *IntStringMap) Get(key int) string {
//
// 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
}
}
// GetOrSetFuncLock returns the value by key,
@ -159,31 +158,31 @@ func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string {
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
gm.mu.RLock()
val, ok := gm.m[key]
gm.mu.RUnlock()
if !ok {
gm.mu.Lock()
defer gm.mu.Unlock()
if v, ok := gm.m[key]; ok {
return v
}
val = f()
gm.m[key] = val
return val
} else {
return val
}
gm.mu.RLock()
val, ok := gm.m[key]
gm.mu.RUnlock()
if !ok {
gm.mu.Lock()
defer gm.mu.Unlock()
if v, ok := gm.m[key]; ok {
return v
}
val = f()
gm.m[key] = val
return val
} else {
return val
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *IntStringMap) SetIfNotExist(key int, value string) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
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.
@ -213,117 +212,116 @@ func (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool {
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)
}
// 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
}
// 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
}
}
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

@ -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 {
@ -20,21 +20,21 @@ type StringIntMap struct {
// 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 {
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...),
}
}
// 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 NewStringIntMapFrom(m map[string]int, unsafe ...bool) *StringIntMap {
return &StringIntMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// NewStringIntMapFromArray returns an StringIntMap object from given array.
@ -43,48 +43,48 @@ func NewStringIntMapFrom(m map[string]int, unsafe...bool) *StringIntMap {
//
// 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...),
}
func NewStringIntMapFromArray(keys []string, values []int, unsafe ...bool) *StringIntMap {
m := make(map[string]int)
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = 0
}
}
return &StringIntMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *StringIntMap) Iterator(f func (k string, v int) bool) {
gm.mu.RLock()
defer gm.mu.RUnlock()
for k, v := range gm.m {
if !f(k, v) {
break
}
}
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 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.
@ -107,7 +107,7 @@ func (gm *StringIntMap) BatchSet(m map[string]int) {
func (gm *StringIntMap) Get(key string) int {
gm.mu.RLock()
val, _ := gm.m[key]
gm.mu.RUnlock()
gm.mu.RUnlock()
return val
}
@ -117,41 +117,41 @@ func (gm *StringIntMap) Get(key string) int {
//
// 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
}
}
// GetOrSetFuncLock returns the value by key,
@ -161,31 +161,31 @@ func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int {
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
gm.mu.RLock()
val, ok := gm.m[key]
gm.mu.RUnlock()
if !ok {
gm.mu.Lock()
defer gm.mu.Unlock()
if v, ok := gm.m[key]; ok {
return v
}
val = f()
gm.m[key] = val
return val
} else {
return val
}
gm.mu.RLock()
val, ok := gm.m[key]
gm.mu.RUnlock()
if !ok {
gm.mu.Lock()
defer gm.mu.Unlock()
if v, ok := gm.m[key]; ok {
return v
}
val = f()
gm.m[key] = val
return val
} else {
return val
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (gm *StringIntMap) SetIfNotExist(key string, value int) bool {
if !gm.Contains(key) {
gm.doSetWithLockCheck(key, value)
return true
}
return false
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.
@ -217,114 +217,114 @@ func (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
// 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)
}
// 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
}
// 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
}
}
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

@ -20,21 +20,21 @@ type StringInterfaceMap struct {
// 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 {
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...),
}
}
// 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 NewStringInterfaceMapFrom(m map[string]interface{}, unsafe ...bool) *StringInterfaceMap {
return &StringInterfaceMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array.
@ -43,25 +43,25 @@ func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe...bool) *StringI
//
// 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...),
}
func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe ...bool) *StringInterfaceMap {
m := make(map[string]interface{})
l := len(values)
for i, k := range keys {
if i < l {
m[k] = values[i]
} else {
m[k] = interface{}(nil)
}
}
return &StringInterfaceMap{
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If f returns true, then continue iterating; or false to stop.
func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) {
func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) bool) {
gm.mu.RLock()
defer gm.mu.RUnlock()
for k, v := range gm.m {
@ -73,18 +73,18 @@ 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 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.
@ -96,11 +96,11 @@ func (gm *StringInterfaceMap) Set(key string, val interface{}) {
// 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>.
@ -123,37 +123,37 @@ func (gm *StringInterfaceMap) Get(key string) interface{} {
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
}
}
// GetOrSetFuncLock returns the value by key,
@ -163,21 +163,21 @@ func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) int
// 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
}
}
// 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.
@ -205,11 +205,11 @@ func (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interfa
// 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.
@ -230,7 +230,7 @@ func (gm *StringInterfaceMap) Keys() []string {
for key, _ := range gm.m {
keys = append(keys, key)
}
gm.mu.RUnlock()
gm.mu.RUnlock()
return keys
}
@ -273,9 +273,9 @@ func (gm *StringInterfaceMap) IsEmpty() bool {
// 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.
@ -315,4 +315,4 @@ func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) {
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

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

@ -18,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{}),
@ -37,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()
@ -52,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 {
@ -64,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]
@ -74,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)
@ -84,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)
@ -94,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{})
@ -104,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
@ -119,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()
@ -143,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()
@ -153,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
@ -175,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
@ -194,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()
@ -222,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()
@ -245,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()
@ -269,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()
@ -288,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,11 +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/unsafe 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

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

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

@ -20,7 +20,6 @@ import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"time"
)

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

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

View File

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

View File

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

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

@ -0,0 +1,142 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gtoml_test
import (
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
var tomlStr string = `
# 模板引擎目录
viewpath = "/home/www/templates/"
# MySQL数据库配置
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
`
var tomlErr string = `
# 模板引擎目录
viewpath = "/home/www/templates/"
# MySQL数据库配置
[redis]
dd = 11
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
`
func TestEncode(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["toml"] = tomlStr
res, err := gtoml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
p, err := gparser.LoadContent(res)
if err != nil {
t.Errorf("parser failed. %v", err)
return
}
gtest.Assert(p.GetString("toml"), tomlStr)
})
gtest.Case(t, func() {
_, err := gtoml.Encode(tomlErr)
if err == nil {
t.Errorf("encode should be failed. %v", err)
return
}
})
}
func TestDecode(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["toml"] = tomlStr
res, err := gtoml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
decodeStr, err := gtoml.Decode(res)
if err != nil {
t.Errorf("decode failed. %v", err)
return
}
gtest.Assert(decodeStr.(map[string]interface{})["toml"], tomlStr)
decodeStr1 := make(map[string]interface{})
err = gtoml.DecodeTo(res, &decodeStr1)
if err != nil {
t.Errorf("decodeTo failed. %v", err)
return
}
gtest.Assert(decodeStr1["toml"], tomlStr)
})
gtest.Case(t, func() {
_, err := gtoml.Decode([]byte(tomlErr))
if err == nil {
t.Errorf("decode failed. %v", err)
return
}
decodeStr1 := make(map[string]interface{})
err = gtoml.DecodeTo([]byte(tomlErr), &decodeStr1)
if err == nil {
t.Errorf("decodeTo failed. %v", err)
return
}
})
}
func TestToJson(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["toml"] = tomlStr
res, err := gtoml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
jsonToml, err := gtoml.ToJson(res)
if err != nil {
t.Errorf("ToJson failed. %v", err)
return
}
p, err := gparser.LoadContent(res)
if err != nil {
t.Errorf("parser failed. %v", err)
return
}
expectJson, err := p.ToJson()
if err != nil {
t.Errorf("parser ToJson failed. %v", err)
return
}
gtest.Assert(jsonToml, expectJson)
})
gtest.Case(t, func() {
_, err := gtoml.ToJson([]byte(tomlErr))
if err == nil {
t.Errorf("ToJson failed. %v", err)
return
}
})
}

View File

@ -0,0 +1,92 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gurl_test
import (
"github.com/gogf/gf/g/encoding/gurl"
"github.com/gogf/gf/g/test/gtest"
"net/url"
"testing"
)
var urlStr string = `https://golang.org/x/crypto?go-get=1 +`
var urlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1+%2B`
var rawUrlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1%20%2B`
func TestEncodeAndDecode(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gurl.Encode(urlStr), urlEncode)
res, err := gurl.Decode(urlEncode)
if err != nil {
t.Errorf("decode failed. %v", err)
return
}
gtest.Assert(res, urlStr)
})
}
func TestRowEncodeAndDecode(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gurl.RawEncode(urlStr), rawUrlEncode)
res, err := gurl.RawDecode(rawUrlEncode)
if err != nil {
t.Errorf("decode failed. %v", err)
return
}
gtest.Assert(res, urlStr)
})
}
func TestBuildQuery(t *testing.T) {
src := url.Values{
"a": {"a2", "a1"},
"b": {"b2", "b1"},
"c": {"c1", "c2"},
}
expect := "a=a2&a=a1&b=b2&b=b1&c=c1&c=c2"
gtest.Assert(gurl.BuildQuery(src), expect)
}
func TestParseURL(t *testing.T) {
src := `http://username:password@hostname:9090/path?arg=value#anchor`
expect := map[string]string{
"scheme": "http",
"host": "hostname",
"port": "9090",
"user": "username",
"pass": "password",
"path": "/path",
"query": "arg=value",
"fragment": "anchor",
}
gtest.Case(t, func() {
component := 0
for k, v := range []string{"all", "scheme", "host", "port", "user", "pass", "path", "query", "fragment"} {
if v == "all" {
component = -1
} else {
component = 1 << (uint(k - 1))
}
res, err := gurl.ParseURL(src, component)
if err != nil {
t.Errorf("ParseURL failed. component:%v, err:%v", component, err)
return
}
if v == "all" {
gtest.Assert(res, expect)
} else {
gtest.Assert(res[v], expect[v])
}
}
})
}

View File

@ -0,0 +1,143 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gyaml_test
import (
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gyaml"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
var yamlStr string = `
#即表示url属性值
url: http://www.wolfcode.cn
#即表示server.host属性的值
server:
host: http://www.wolfcode.cn
#数组即表示server为[a,b,c]
server:
- 120.168.117.21
- 120.168.117.22
- 120.168.117.23
#常量
pi: 3.14 #定义一个数值3.14
hasChild: true #定义一个boolean值
name: '你好YAML' #定义一个字符串
`
var yamlErr string = `
# 模板引擎目录
viewpath = "/home/www/templates/"
# MySQL数据库配置
[redis]
dd = 11
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
`
func TestEncode(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["yaml"] = yamlStr
res, err := gyaml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
p, err := gparser.LoadContent(res)
if err != nil {
t.Errorf("parser failed. %v", err)
return
}
gtest.Assert(p.GetString("yaml"), yamlStr)
})
}
func TestDecode(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["yaml"] = yamlStr
res, err := gyaml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
decodeStr, err := gyaml.Decode(res)
if err != nil {
t.Errorf("decode failed. %v", err)
return
}
gtest.Assert(decodeStr.(map[string]interface{})["yaml"], yamlStr)
decodeStr1 := make(map[string]interface{})
err = gyaml.DecodeTo(res, &decodeStr1)
if err != nil {
t.Errorf("decodeTo failed. %v", err)
return
}
gtest.Assert(decodeStr1["yaml"], yamlStr)
})
gtest.Case(t, func() {
_, err := gyaml.Decode([]byte(yamlErr))
if err == nil {
t.Errorf("decode failed. %v", err)
return
}
decodeStr1 := make(map[string]interface{})
err = gyaml.DecodeTo([]byte(yamlErr), &decodeStr1)
if err == nil {
t.Errorf("decodeTo failed. %v", err)
return
}
})
}
func TestToJson(t *testing.T) {
gtest.Case(t, func() {
m := make(map[string]string)
m["yaml"] = yamlStr
res, err := gyaml.Encode(m)
if err != nil {
t.Errorf("encode failed. %v", err)
return
}
jsonyaml, err := gyaml.ToJson(res)
if err != nil {
t.Errorf("ToJson failed. %v", err)
return
}
p, err := gparser.LoadContent(res)
if err != nil {
t.Errorf("parser failed. %v", err)
return
}
expectJson, err := p.ToJson()
if err != nil {
t.Errorf("parser ToJson failed. %v", err)
return
}
gtest.Assert(jsonyaml, expectJson)
})
gtest.Case(t, func() {
_, err := gyaml.ToJson([]byte(yamlErr))
if err == nil {
t.Errorf("ToJson failed. %v", err)
return
}
})
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,13 +18,15 @@ import (
type Conn struct {
conn net.Conn // 底层tcp对象
reader *bufio.Reader // 当前链接的缓冲读取对象
buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理)
recvDeadline time.Time // 读取超时时间
sendDeadline time.Time // 写入超时时间
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔
}
const (
gRECV_ALL_WAIT_TIMEOUT = time.Millisecond // 读取全部缓冲数据时,没有缓冲数据时的等待间隔
// 读取全部缓冲数据时,没有缓冲数据时的等待间隔
gRECV_ALL_WAIT_TIMEOUT = time.Millisecond
)
// 创建TCP链接
@ -103,7 +105,7 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
// 缓冲区数据写入等待处理。
// 如果已经读取到数据(这点很关键,表明缓冲区已经有数据,剩下的操作就是将所有数据读取完毕)
// 那么可以设置读取全部缓冲数据的超时时间;如果没有接收到任何数据,那么将会进入读取阻塞(或者自定义的超时阻塞);
// 仅对读取全部缓冲数据操作有效
// 仅对读取全部缓冲数据操作有效
if length <= 0 && index > 0 {
bufferWait = true
c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait))
@ -117,9 +119,14 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
break
}
} else {
// 如果长度超过了自定义的读取缓冲区,那么自动增长
if index >= gDEFAULT_READ_BUFFER_SIZE {
// 如果长度超过了自定义的读取缓冲区,那么自动增长
buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...)
} else {
// 如果第一次读取的数据并未达到缓冲变量长度,那么直接返回
if !bufferWait {
break
}
}
}
}
@ -234,8 +241,8 @@ func (c *Conn) SetSendDeadline(t time.Time) error {
// 读取全部缓冲区数据时,读取完毕后的写入等待间隔,如果超过该等待时间后仍无可读数据,那么读取操作返回。
// 该时间间隔不能设置得太大会影响Recv读取时长(默认为1毫秒)。
func (c *Conn) SetRecvBufferWait(d time.Duration) {
c.recvBufferWait = d
func (c *Conn) SetRecvBufferWait(bufferWaitDuration time.Duration) {
c.recvBufferWait = bufferWaitDuration
}
func (c *Conn) LocalAddr() net.Addr {

115
g/net/gtcp/gtcp_conn_pkg.go Normal file
View File

@ -0,0 +1,115 @@
// 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 gtcp
import (
"encoding/binary"
"errors"
"fmt"
"time"
)
const (
// 允许最大的简单协议包大小(byte), 15MB
PKG_MAX_SIZE = 0xFFFFFF
// 消息包头大小: "总长度"3字节+"校验码"4字节
PKG_HEADER_SIZE = 7
)
// 根据简单协议发送数据包。
// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。
// 注意:
// 1. "总长度"包含自身3字节及"校验码"4字节。
// 2. 由于"总长度"为3字节并且使用的BigEndian字节序因此最后返回的buffer使用了buffer[1:]。
func (c *Conn) SendPkg(data []byte, retry...Retry) error {
length := uint32(len(data))
if length > PKG_MAX_SIZE - PKG_HEADER_SIZE {
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE))
}
buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data))
copy(buffer[PKG_HEADER_SIZE + 1 : ], data)
binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length)
binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data))
//fmt.Println("SendPkg:", buffer[1:])
return c.Send(buffer[1:], retry...)
}
// 简单协议: 带超时时间的数据发送
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
}
// 简单协议: 发送数据并等待接收返回数据
func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
} else {
return nil, err
}
}
// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
} else {
return nil, err
}
}
// 简单协议: 获取一个数据包。
func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
var temp []byte
var length uint32
for {
// 先根据对象的缓冲区数据进行计算
for {
if len(c.buffer) >= PKG_HEADER_SIZE {
// 注意"总长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]})
// 解析的大小是否符合规范
if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE {
c.buffer = c.buffer[1:]
continue
}
// 不满足包大小,需要继续读取
if uint32(len(c.buffer)) < length {
break
}
// 数据校验
if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) {
c.buffer = c.buffer[1:]
continue
}
result = c.buffer[PKG_HEADER_SIZE : length]
c.buffer = c.buffer[length: ]
return
} else {
break
}
}
// 读取系统socket缓冲区的完整数据
temp, err = c.Recv(-1, retry...)
if err != nil {
break
}
if len(temp) > 0 {
c.buffer = append(c.buffer, temp...)
}
//fmt.Println("RecvPkg:", c.buffer)
}
return
}
// 简单协议: 带超时时间的消息包获取
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
}

View File

@ -51,12 +51,12 @@ func Send(addr string, data []byte, retry...Retry) error {
// (面向短链接)发送数据并等待接收返回数据
func SendRecv(addr string, data []byte, receive int, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecv(data, receive, retry...)
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecv(data, receive, retry...)
}
// (面向短链接)带超时时间的数据发送

View File

@ -0,0 +1,49 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gtcp
import "time"
// 简单协议: (面向短链接)发送消息包
func SendPkg(addr string, data []byte, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkg(data, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据
func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkg(data, retry...)
}
// 简单协议: (面向短链接)带超时时间的数据发送
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkgWithTimeout(data, timeout, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间)
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkgWithTimeout(data, timeout, retry...)
}

View File

@ -0,0 +1,72 @@
// 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 gtcp
import (
"time"
)
// 简单协议: (方法覆盖)发送数据
func (c *PoolConn) SendPkg(data []byte, retry...Retry) (err error) {
if err = c.Conn.SendPkg(data, retry...); err != nil && c.status == gCONN_STATUS_UNKNOWN {
if v, e := c.pool.NewFunc(); e == nil {
c.Conn = v.(*PoolConn).Conn
err = c.Conn.SendPkg(data, retry...)
} else {
err = e
}
}
if err != nil {
c.status = gCONN_STATUS_ERROR
} else {
c.status = gCONN_STATUS_ACTIVE
}
return err
}
// 简单协议: (方法覆盖)接收数据
func (c *PoolConn) RecvPkg(retry...Retry) ([]byte, error) {
data, err := c.Conn.RecvPkg(retry...)
if err != nil {
c.status = gCONN_STATUS_ERROR
} else {
c.status = gCONN_STATUS_ACTIVE
}
return data, err
}
// 简单协议: (方法覆盖)带超时时间的数据获取
func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
}
// 简单协议: (方法覆盖)带超时时间的数据发送
func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
}
// 简单协议: (方法覆盖)发送数据并等待接收返回数据
func (c *PoolConn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
} else {
return nil, err
}
}
// 简单协议: (方法覆盖)发送数据并等待接收返回数据(带返回超时等待时间)
func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
} else {
return nil, err
}
}

View File

@ -14,11 +14,12 @@ import (
// 封装的链接对象
type Conn struct {
conn *net.UDPConn // 底层链接对象
raddr *net.UDPAddr // 远程地址
recvDeadline time.Time // 读取超时时间
sendDeadline time.Time // 写入超时时间
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔
conn *net.UDPConn // 底层链接对象
raddr *net.UDPAddr // 远程地址
buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理)
recvDeadline time.Time // 读取超时时间
sendDeadline time.Time // 写入超时时间
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔
}
const (
@ -119,9 +120,14 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
break
}
} else {
// 如果长度超过了自定义的读取缓冲区,那么自动增长
if index >= gDEFAULT_READ_BUFFER_SIZE {
// 如果长度超过了自定义的读取缓冲区,那么自动增长
buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...)
} else {
// 如果第一次读取的数据并未达到缓冲变量长度,那么直接返回
if !bufferWait {
break
}
}
}
}

115
g/net/gudp/gudp_conn_pkg.go Normal file
View File

@ -0,0 +1,115 @@
// 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 gudp
import (
"encoding/binary"
"errors"
"fmt"
"time"
)
const (
// 允许最大的简单协议包大小(byte), 15MB
PKG_MAX_SIZE = 0xFFFFFF
// 消息包头大小: "总长度"3字节+"校验码"4字节
PKG_HEADER_SIZE = 7
)
// 根据简单协议发送数据包。
// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。
// 注意:
// 1. "总长度"包含自身3字节及"校验码"4字节。
// 2. 由于"总长度"为3字节并且使用的BigEndian字节序因此最后返回的buffer使用了buffer[1:]。
func (c *Conn) SendPkg(data []byte, retry...Retry) error {
length := uint32(len(data))
if length > PKG_MAX_SIZE - PKG_HEADER_SIZE {
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE))
}
buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data))
copy(buffer[PKG_HEADER_SIZE + 1 : ], data)
binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length)
binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data))
//fmt.Println("SendPkg:", buffer[1:])
return c.Send(buffer[1:], retry...)
}
// 简单协议: 带超时时间的数据发送
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
}
// 简单协议: 发送数据并等待接收返回数据
func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
} else {
return nil, err
}
}
// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
} else {
return nil, err
}
}
// 简单协议: 获取一个数据包。
func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
var temp []byte
var length uint32
for {
// 先根据对象的缓冲区数据进行计算
for {
if len(c.buffer) >= PKG_HEADER_SIZE {
// 注意"总长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]})
// 解析的大小是否符合规范
if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE {
c.buffer = c.buffer[1:]
continue
}
// 不满足包大小,需要继续读取
if uint32(len(c.buffer)) < length {
break
}
// 数据校验
if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) {
c.buffer = c.buffer[1:]
continue
}
result = c.buffer[PKG_HEADER_SIZE : length]
c.buffer = c.buffer[length: ]
return
} else {
break
}
}
// 读取系统socket缓冲区的完整数据
temp, err = c.Recv(-1, retry...)
if err != nil {
break
}
if len(temp) > 0 {
c.buffer = append(c.buffer, temp...)
}
//fmt.Println("RecvPkg:", c.buffer)
}
return
}
// 简单协议: 带超时时间的消息包获取
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
}

View File

@ -10,6 +10,15 @@ import (
"net"
)
// 常见的二进制数据校验方式,生成校验结果
func Checksum(buffer []byte) uint32 {
var checksum uint32
for _, b := range buffer {
checksum += uint32(b)
}
return checksum
}
// 创建标准库UDP链接操作对象
func NewNetConn(raddr string, laddr...string) (*net.UDPConn, error) {
var err error

View File

@ -0,0 +1,49 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gudp
import "time"
// 简单协议: (面向短链接)发送消息包
func SendPkg(addr string, data []byte, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkg(data, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据
func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkg(data, retry...)
}
// 简单协议: (面向短链接)带超时时间的数据发送
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkgWithTimeout(data, timeout, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间)
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkgWithTimeout(data, timeout, retry...)
}

View File

@ -88,7 +88,7 @@ func (c *Config) filePath(file...string) (path string) {
for _, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
index++
buffer.WriteString(fmt.Sprintf("\n%d. %s%s%s", index, v, gfile.Separator, "config"))
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v + gfile.Separator + "config"))
index++
}
})
@ -217,12 +217,10 @@ func (c *Config) GetFilePath(file...string) (path string) {
}
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
// 查找当前目录
if path, _ = gspath.Search(v, name); path != "" {
break
}
// 查找当前目录下的config子目录
if path, _ = gspath.Search(v, "config" + gfile.Separator + name); path != "" {
if path, _ = gspath.Search(v + gfile.Separator + "config", name); path != "" {
break
}
}

View File

@ -6,107 +6,69 @@
//
// Package gcmd provides console operations, like options/values reading and command running.
//
// 命令行管理.
package gcmd
import (
"github.com/gogf/gf/g/os/glog"
"os"
"errors"
"regexp"
"regexp"
)
// 命令行参数列表
type gCmdValue struct {
values []string
// Console values.
type gCmdValue struct {
values []string
}
// 命令行选项列表
// Console options.
type gCmdOption struct {
options map[string]string
options map[string]string
}
// 终端管理对象(全局)
var Value = &gCmdValue{} // 终端参数-命令参数列表
var Option = &gCmdOption{} // 终端参数-选项参数列表
var cmdFuncMap = make(map[string]func()) // 终端命令及函数地址对应表
var Value = &gCmdValue{} // Console values.
var Option = &gCmdOption{} // Console options.
var cmdFuncMap = make(map[string]func()) // Registered callback functions.
// 检查并初始化console参数在包加载的时候触发
// 初始化时执行,不影响运行时性能
func init() {
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
Option.options = make(map[string]string)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
if len(result) > 1 {
Option.options[result[1]] = result[2]
} else {
Value.values = append(Value.values, os.Args[i])
}
}
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
Option.options = make(map[string]string)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
if len(result) > 1 {
Option.options[result[1]] = result[2]
} else {
Value.values = append(Value.values, os.Args[i])
}
}
}
// 返回所有的命令行参数values
func (c *gCmdValue) GetAll() []string {
return c.values
// BindHandle registers callback function <f> with <cmd>.
func BindHandle(cmd string, f func()) {
if _, ok := cmdFuncMap[cmd]; ok {
glog.Fatal("duplicated handle for command:" + cmd)
} else {
cmdFuncMap[cmd] = f
}
}
// 返回所有的命令行参数options
func (c *gCmdOption) GetAll() map[string]string {
return c.options
// RunHandle executes the callback function registered by <cmd>.
func RunHandle(cmd string) {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
} else {
glog.Fatal("no handle found for command:" + cmd)
}
}
// 获得一条指定索引位置的value参数
func (c *gCmdValue) Get(index uint8, def...string) string {
if index < uint8(len(c.values)) {
return c.values[index]
} else if len(def) > 0 {
return def[0]
}
return ""
}
// 获得一条指定索引位置的option参数;
func (c *gCmdOption) Get(key string, def...string) string {
if option, ok := c.options[key]; ok {
return option
} else if len(def) > 0 {
return def[0]
}
return ""
}
// 绑定命令行参数及对应的命令函数,注意命令函数参数是函数的内存地址
// 如果操作失败返回错误信息
func BindHandle (cmd string, f func()) error {
if _, ok := cmdFuncMap[cmd]; ok {
return errors.New("duplicated handle for command:" + cmd)
} else {
cmdFuncMap[cmd] = f
return nil
}
}
// 执行命令对应的函数
func RunHandle (cmd string) error {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
}
}
// 自动识别命令参数并执行命令参数对应的函数
func AutoRun () error {
if cmd := Value.Get(1); cmd != "" {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
}
} else {
return errors.New("no command found")
}
// AutoRun automatically recognizes and executes the callback function
// by value of index 0 (the first console parameter).
func AutoRun() {
if cmd := Value.Get(1); cmd != "" {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
} else {
glog.Fatal("no handle found for command:" + cmd)
}
} else {
glog.Fatal("no command found")
}
}

32
g/os/gcmd/gcmd_option.go Normal file
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 gcmd
import "github.com/gogf/gf/g/container/gvar"
// GetAll returns all option values as map[string]string.
func (c *gCmdOption) GetAll() map[string]string {
return c.options
}
// Get returns the option value string specified by <key>,
// if value dose not exist, then returns <def>.
func (c *gCmdOption) Get(key string, def...string) string {
if option, ok := c.options[key]; ok {
return option
} else if len(def) > 0 {
return def[0]
}
return ""
}
// GetVar returns the option value as gvar.VarRead object specified by <key>,
// if value does not exist, then returns <def> as its default value.
func (c *gCmdOption) GetVar(key string, def...string) gvar.VarRead {
return gvar.NewRead(c.Get(key, def...), true)
}

32
g/os/gcmd/gcmd_value.go Normal file
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 gcmd
import "github.com/gogf/gf/g/container/gvar"
// GetAll returns all values as a slice of string.
func (c *gCmdValue) GetAll() []string {
return c.values
}
// Get returns value by index <index> as string,
// if value does not exist, then returns <def>.
func (c *gCmdValue) Get(index int, def...string) string {
if index < len(c.values) {
return c.values[index]
} else if len(def) > 0 {
return def[0]
}
return ""
}
// GetVar returns value by index <index> as gvar.VarRead object,
// if value does not exist, then returns <def> as its default value.
func (c *gCmdValue) GetVar(index int, def...string) gvar.VarRead {
return gvar.NewRead(c.Get(index, def...), true)
}

View File

@ -15,18 +15,22 @@ import (
"time"
)
// 定时任务项
// Timed task entry.
type Entry struct {
cron *Cron // 所属定时任务
entry *gtimer.Entry // 定时器任务对象
schedule *cronSchedule // 定时任务配置对象
jobName string // 任务注册方法名称
Name string // 定时任务名称
Job func() // 注册定时任务方法
Time time.Time // 注册时间
cron *Cron // Cron object belonged to.
entry *gtimer.Entry // Associated gtimer.Entry.
schedule *cronSchedule // Timed schedule object.
jobName string // Callback function name(address info).
Name string // Entry name.
Job func() `json:"-"` // Callback function.
Time time.Time // Registered time.
}
// 创建定时任务
// addEntry creates and returns a new Entry object.
// Param <job> is the callback function for timed task execution.
// Param <singleton> specifies whether timed task executing in singleton mode.
// Param <times> limits the times for timed task executing.
// Param <name> names this entry for manual control.
func (c *Cron) addEntry(pattern string, job func(), singleton bool, times int, name ... string) (*Entry, error) {
schedule, err := newSchedule(pattern)
if err != nil {

View File

@ -10,22 +10,21 @@
package gfile
import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"io"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"sort"
"strings"
"time"
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"io"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"sort"
"strings"
"time"
)
const (
@ -441,48 +440,29 @@ func homeWindows() (string, error) {
// Available in develop environment.
//
// 获取入口函数文件所在目录(main包文件目录),
// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**
// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**
// 注意该方法被第一次调用时如果是在异步的goroutine中该方法可能无法获取到main包路径。
func MainPkgPath() string {
path := mainPkgPath.Val()
if path != "" {
if path == "-" {
return ""
}
return path
}
f := ""
goroot := runtime.GOROOT()
// runtime.GOROOT() 在windows下有可能是以'\'符号分隔,
// 而 runtime.Caller(i) 获取到的文件路径却是以'/'符号分隔,
// 因此这里统一转换为'/'符号再进行比较
goroot = gstr.Replace(goroot, "\\", "/")
for i := 1; i < 10000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
// 不包含go源码路径
if file != "" && goroot != "" &&
!gregex.IsMatchString("^" + goroot, file) &&
!strings.EqualFold("<autogenerated>", file) {
f = file
}
if gregex.IsMatchString(`package\s+main`, GetContents(file)) {
path = Dir(file)
mainPkgPath.Set(path)
return path
}
} else {
break
}
}
if f != "" {
for {
p := Dir(f)
if p == f {
break
}
// 会自动扫描源码寻找main包
if paths, err := ScanDir(p, "*.go"); err == nil && len(paths) > 0 {
for _, path := range paths {
if gregex.IsMatchString(`package\s+main`, GetContents(path)) {
mainPkgPath.Set(p)
return p
}
}
}
f = p
}
}
// 找不到,下次不用再检索了
mainPkgPath.Set("-")
return ""
}

View File

@ -0,0 +1,334 @@
package gfile_test
import (
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
// 创建测试文件
func createTestFile(filename, content string) error {
TempDir := testpath()
err := ioutil.WriteFile(TempDir+filename, []byte(content), 0666)
return err
}
// 测试完删除文件或目录
func delTestFiles(filenames string) {
os.RemoveAll(testpath() + filenames)
}
// 创建目录
func createDir(paths string) {
TempDir := testpath()
os.Mkdir(TempDir+paths, 0777)
}
// 统一格式化文件目录为"/"
func formatpaths(paths []string) []string {
for k, v := range paths {
paths[k] = filepath.ToSlash(v)
paths[k] = strings.Replace(paths[k], "./", "/", 1)
}
return paths
}
// 统一格式化文件目录为"/"
func formatpath(paths string) string {
paths = filepath.ToSlash(paths)
paths = strings.Replace(paths, "./", "/", 1)
return paths
}
// 指定返回要测试的目录
func testpath() string {
return os.TempDir()
}
func TestGetContents(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_t1.txt"
)
createTestFile(filepaths, "my name is jroam")
defer delTestFiles(filepaths)
gtest.Assert(gfile.GetContents(testpath()+filepaths), "my name is jroam")
gtest.Assert(gfile.GetContents(""), "")
})
}
func TestGetBinContents(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths1 string = "/testfile_t1.txt" // 文件存在时
filepaths2 string = testpath() + "/testfile_t1_no.txt" // 文件不存在时
readcontent []byte
str1 string = "my name is jroam"
)
createTestFile(filepaths1, str1)
defer delTestFiles(filepaths1)
readcontent = gfile.GetBinContents(testpath() + filepaths1)
gtest.Assert(readcontent, []byte(str1))
readcontent = gfile.GetBinContents(filepaths2)
gtest.Assert(string(readcontent), "")
gtest.Assert(string(gfile.GetBinContents(filepaths2)), "")
})
}
// 截断文件为指定的大小
func TestTruncate(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths1 string = "/testfile_GetContentsyyui.txt" //文件存在时
err error
files *os.File
)
createTestFile(filepaths1, "abcdefghijkmln")
defer delTestFiles(filepaths1)
err = gfile.Truncate(testpath()+filepaths1, 10)
gtest.Assert(err, nil)
//=========================检查修改文后的大小,是否与期望一致
files, err = os.Open(testpath() + filepaths1)
defer files.Close()
gtest.Assert(err, nil)
fileinfo, err2 := files.Stat()
gtest.Assert(err2, nil)
gtest.Assert(fileinfo.Size(), 10)
//====测试当为空时,是否报错
err = gfile.Truncate("", 10)
gtest.AssertNE(err, nil)
})
}
func TestPutContents(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_PutContents.txt"
err error
readcontent []byte
)
createTestFile(filepaths, "a")
defer delTestFiles(filepaths)
err = gfile.PutContents(testpath()+filepaths, "test!")
gtest.Assert(err, nil)
//==================判断是否真正写入
readcontent, err = ioutil.ReadFile(testpath() + filepaths)
gtest.Assert(err, nil)
gtest.Assert(string(readcontent), "test!")
err = gfile.PutContents("", "test!")
gtest.AssertNE(err, nil)
})
}
func TestPutContentsAppend(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_PutContents.txt"
err error
readcontent []byte
)
createTestFile(filepaths, "a")
defer delTestFiles(filepaths)
err = gfile.PutContentsAppend(testpath()+filepaths, "hello")
gtest.Assert(err, nil)
//==================判断是否真正写入
readcontent, err = ioutil.ReadFile(testpath() + filepaths)
gtest.Assert(err, nil)
gtest.Assert(string(readcontent), "ahello")
err = gfile.PutContentsAppend("", "hello")
gtest.AssertNE(err, nil)
})
}
func TestPutBinContents(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_PutContents.txt"
err error
readcontent []byte
)
createTestFile(filepaths, "a")
defer delTestFiles(filepaths)
err = gfile.PutBinContents(testpath()+filepaths, []byte("test!!"))
gtest.Assert(err, nil)
// 判断是否真正写入
readcontent, err = ioutil.ReadFile(testpath() + filepaths)
gtest.Assert(err, nil)
gtest.Assert(string(readcontent), "test!!")
err = gfile.PutBinContents("", []byte("test!!"))
gtest.AssertNE(err, nil)
})
}
func TestPutBinContentsAppend(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_PutContents.txt" //原文件内容: yy
err error
readcontent []byte
)
createTestFile(filepaths, "test!!")
defer delTestFiles(filepaths)
err = gfile.PutBinContentsAppend(testpath()+filepaths, []byte("word"))
gtest.Assert(err, nil)
// 判断是否真正写入
readcontent, err = ioutil.ReadFile(testpath() + filepaths)
gtest.Assert(err, nil)
gtest.Assert(string(readcontent), "test!!word")
err = gfile.PutBinContentsAppend("", []byte("word"))
gtest.AssertNE(err, nil)
})
}
func TestGetBinContentsByTwoOffsetsByPath(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_GetContents.txt" // 文件内容: abcdefghijk
readcontent []byte
)
createTestFile(filepaths, "abcdefghijk")
defer delTestFiles(filepaths)
readcontent = gfile.GetBinContentsByTwoOffsetsByPath(testpath()+filepaths, 2, 5)
gtest.Assert(string(readcontent), "cde")
readcontent = gfile.GetBinContentsByTwoOffsetsByPath("", 2, 5)
gtest.Assert(len(readcontent), 0)
})
}
func TestGetNextCharOffsetByPath(t *testing.T) {
gtest.Case(t, func() {
var (
filepaths string = "/testfile_GetContents.txt" // 文件内容: abcdefghijk
localindex int64
)
createTestFile(filepaths, "abcdefghijk")
defer delTestFiles(filepaths)
localindex = gfile.GetNextCharOffsetByPath(testpath()+filepaths, 'd', 1)
gtest.Assert(localindex, 3)
localindex = gfile.GetNextCharOffsetByPath("", 'd', 1)
gtest.Assert(localindex, -1)
})
}
func TestGetNextCharOffset(t *testing.T) {
gtest.Case(t, func() {
var (
localindex int64
)
reader := strings.NewReader("helloword")
localindex = gfile.GetNextCharOffset(reader, 'w', 1)
gtest.Assert(localindex, 5)
localindex = gfile.GetNextCharOffset(reader, 'j', 1)
gtest.Assert(localindex, -1)
})
}
func TestGetBinContentsByTwoOffsets(t *testing.T) {
gtest.Case(t, func() {
var (
reads []byte
)
reader := strings.NewReader("helloword")
reads = gfile.GetBinContentsByTwoOffsets(reader, 1, 3)
gtest.Assert(string(reads), "el")
reads = gfile.GetBinContentsByTwoOffsets(reader, 10, 30)
gtest.Assert(string(reads), "")
})
}
func TestGetBinContentsTilChar(t *testing.T) {
gtest.Case(t, func() {
var (
reads []byte
indexs int64
)
reader := strings.NewReader("helloword")
reads, _ = gfile.GetBinContentsTilChar(reader, 'w', 2)
gtest.Assert(string(reads), "llow")
_, indexs = gfile.GetBinContentsTilChar(reader, 'w', 20)
gtest.Assert(indexs, -1)
})
}
func TestGetBinContentsTilCharByPath(t *testing.T) {
gtest.Case(t, func() {
var (
reads []byte
indexs int64
filepaths string = "/testfile_GetContents.txt"
)
createTestFile(filepaths, "abcdefghijklmn")
defer delTestFiles(filepaths)
reads, _ = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'c', 2)
gtest.Assert(string(reads), "c")
reads, _ = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'y', 1)
gtest.Assert(string(reads), "")
_, indexs = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'x', 1)
gtest.Assert(indexs, -1)
})
}
func TestHome(t *testing.T) {
gtest.Case(t, func() {
var (
reads string
err error
)
reads, err = gfile.Home()
gtest.Assert(err, nil)
gtest.AssertNE(reads, "")
})
}

View File

@ -0,0 +1,63 @@
package gfile_test
import (
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"path/filepath"
"testing"
)
func TestSearch(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfiless"
paths2 string = "./testfile/dirfiles_no"
tpath string
tpath2 string
tempstr string
ypaths1 string
err error
)
createDir(paths1)
defer delTestFiles(paths1)
ypaths1 = paths1
tpath, err = gfile.Search(testpath() + paths1)
gtest.Assert(err, nil)
tpath = filepath.ToSlash(tpath)
// 自定义优先路径
tpath2, err = gfile.Search(testpath() + paths1)
gtest.Assert(err, nil)
tpath2 = filepath.ToSlash(tpath2)
tempstr = testpath()
paths1 = tempstr + paths1
paths1 = filepath.ToSlash(paths1)
gtest.Assert(tpath, paths1)
gtest.Assert(tpath2, tpath)
// 测试给定目录
tpath2, err = gfile.Search(paths1, "testfiless")
tpath2 = filepath.ToSlash(tpath2)
tempss := filepath.ToSlash(paths1)
gtest.Assert(tpath2, tempss)
// 测试当前目录
tempstr, _ = filepath.Abs("./")
tempstr = testpath()
paths1 = tempstr + ypaths1
paths1 = filepath.ToSlash(paths1)
gtest.Assert(tpath2, paths1)
// 测试目录不存在时
_, err = gfile.Search(paths2)
gtest.AssertNE(err, nil)
})
}

View File

@ -0,0 +1,59 @@
package gfile_test
import (
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func TestSize(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_t1.txt"
sizes int64
)
createTestFile(paths1, "abcdefghijklmn")
defer delTestFiles(paths1)
sizes = gfile.Size(testpath() + paths1)
gtest.Assert(sizes, 14)
sizes = gfile.Size("")
gtest.Assert(sizes, 0)
})
}
func TestFormatSize(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gfile.FormatSize(0), "0.00B")
gtest.Assert(gfile.FormatSize(16), "16.00B")
gtest.Assert(gfile.FormatSize(1024), "1.00K")
gtest.Assert(gfile.FormatSize(16000000), "15.26M")
gtest.Assert(gfile.FormatSize(1600000000), "1.49G")
gtest.Assert(gfile.FormatSize(9600000000000), "8.73T")
gtest.Assert(gfile.FormatSize(9600000000000000), "8.53P")
gtest.Assert(gfile.FormatSize(9600000000000000000), "TooLarge")
})
}
func TestReadableSize(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_t1.txt"
)
createTestFile(paths1, "abcdefghijklmn")
defer delTestFiles(paths1)
gtest.Assert(gfile.ReadableSize(testpath()+paths1), "14.00B")
gtest.Assert(gfile.ReadableSize(""), "0.00B")
})
}

686
g/os/gfile/gfile_test.go Normal file
View File

@ -0,0 +1,686 @@
package gfile_test
import (
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"os"
"path/filepath"
"strings"
"testing"
)
func TestIsDir(t *testing.T) {
gtest.Case(t, func() {
paths := "/testfile"
createDir(paths)
defer delTestFiles(paths)
gtest.Assert(gfile.IsDir(testpath()+paths), true)
gtest.Assert(gfile.IsDir("./testfile2"), false)
gtest.Assert(gfile.IsDir("./testfile/tt.txt"), false)
gtest.Assert(gfile.IsDir(""), false)
})
}
func TestCreate(t *testing.T) {
gtest.Case(t, func() {
var (
err error
filepaths []string
fileobj *os.File
)
filepaths = append(filepaths, "/testfile_cc1.txt")
filepaths = append(filepaths, "/testfile_cc2.txt")
for _, v := range filepaths {
fileobj, err = gfile.Create(testpath() + v)
defer delTestFiles(v)
fileobj.Close()
gtest.Assert(err, nil)
}
})
}
func TestOpen(t *testing.T) {
gtest.Case(t, func() {
var (
err error
files []string
flags []bool
fileobj *os.File
)
file1 := "/testfile_nc1.txt"
createTestFile(file1, "")
defer delTestFiles(file1)
files = append(files, file1)
flags = append(flags, true)
files = append(files, "./testfile/file1/c1.txt")
flags = append(flags, false)
for k, v := range files {
fileobj, err = gfile.Open(testpath() + v)
fileobj.Close()
if flags[k] {
gtest.Assert(err, nil)
} else {
gtest.AssertNE(err, nil)
}
}
})
}
func TestOpenFile(t *testing.T) {
gtest.Case(t, func() {
var (
err error
files []string
flags []bool
fileobj *os.File
)
files = append(files, "./testfile/file1/nc1.txt")
flags = append(flags, false)
f1 := "/testfile_tt.txt"
createTestFile(f1, "")
defer delTestFiles(f1)
files = append(files, f1)
flags = append(flags, true)
for k, v := range files {
fileobj, err = gfile.OpenFile(testpath()+v, os.O_RDWR, 0666)
fileobj.Close()
if flags[k] {
gtest.Assert(err, nil)
} else {
gtest.AssertNE(err, nil)
}
}
})
}
func TestOpenWithFlag(t *testing.T) {
gtest.Case(t, func() {
var (
err error
files []string
flags []bool
fileobj *os.File
)
file1 := "/testfile_t1.txt"
createTestFile(file1, "")
defer delTestFiles(file1)
files = append(files, file1)
flags = append(flags, true)
files = append(files, "/testfiless/dirfiles/t1_no.txt")
flags = append(flags, false)
for k, v := range files {
fileobj, err = gfile.OpenWithFlag(testpath()+v, os.O_RDWR)
fileobj.Close()
if flags[k] {
gtest.Assert(err, nil)
} else {
gtest.AssertNE(err, nil)
}
}
})
}
func TestOpenWithFlagPerm(t *testing.T) {
gtest.Case(t, func() {
var (
err error
files []string
flags []bool
fileobj *os.File
)
file1 := "/testfile_nc1.txt"
createTestFile(file1, "")
defer delTestFiles(file1)
files = append(files, file1)
flags = append(flags, true)
files = append(files, "/testfileyy/tt.txt")
flags = append(flags, false)
for k, v := range files {
fileobj, err = gfile.OpenWithFlagPerm(testpath()+v, os.O_RDWR, 666)
fileobj.Close()
if flags[k] {
gtest.Assert(err, nil)
} else {
gtest.AssertNE(err, nil)
}
}
})
}
func TestExists(t *testing.T) {
gtest.Case(t, func() {
var (
flag bool
files []string
flags []bool
)
file1 := "/testfile_GetContents.txt"
createTestFile(file1, "")
defer delTestFiles(file1)
files = append(files, file1)
flags = append(flags, true)
files = append(files, "./testfile/havefile1/tt_no.txt")
flags = append(flags, false)
for k, v := range files {
flag = gfile.Exists(testpath() + v)
if flags[k] {
gtest.Assert(flag, true)
} else {
gtest.Assert(flag, false)
}
}
})
}
func TestPwd(t *testing.T) {
gtest.Case(t, func() {
paths, err := os.Getwd()
gtest.Assert(err, nil)
gtest.Assert(gfile.Pwd(), paths)
})
}
func TestIsFile(t *testing.T) {
gtest.Case(t, func() {
var (
flag bool
files []string
flags []bool
)
file1 := "/testfile_tt.txt"
createTestFile(file1, "")
defer delTestFiles(file1)
files = append(files, file1)
flags = append(flags, true)
dir1 := "/testfiless"
createDir(dir1)
defer delTestFiles(dir1)
files = append(files, dir1)
flags = append(flags, false)
files = append(files, "./testfiledd/tt1.txt")
flags = append(flags, false)
for k, v := range files {
flag = gfile.IsFile(testpath() + v)
if flags[k] {
gtest.Assert(flag, true)
} else {
gtest.Assert(flag, false)
}
}
})
}
func TestInfo(t *testing.T) {
gtest.Case(t, func() {
var (
err error
paths string = "/testfile_t1.txt"
files os.FileInfo
files2 os.FileInfo
)
createTestFile(paths, "")
defer delTestFiles(paths)
files, err = gfile.Info(testpath() + paths)
gtest.Assert(err, nil)
files2, err = os.Stat(testpath() + paths)
gtest.Assert(err, nil)
gtest.Assert(files, files2)
})
}
func TestMove(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/ovetest"
filepaths string = "/testfile_ttn1.txt"
topath string = "/testfile_ttn2.txt"
)
createDir("/ovetest")
createTestFile(paths+filepaths, "a")
defer delTestFiles(paths)
yfile := testpath() + paths + filepaths
tofile := testpath() + paths + topath
gtest.Assert(gfile.Move(yfile, tofile), nil)
// 检查移动后的文件是否真实存在
_, err := os.Stat(tofile)
gtest.Assert(os.IsNotExist(err), false)
})
}
func TestRename(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/testfiles"
ypath string = "/testfilettm1.txt"
topath string = "/testfilettm2.txt"
)
createDir(paths)
createTestFile(paths+ypath, "a")
defer delTestFiles(paths)
ypath = testpath() + paths + ypath
topath = testpath() + paths + topath
gtest.Assert(gfile.Rename(ypath, topath), nil)
gtest.Assert(gfile.IsFile(topath), true)
gtest.AssertNE(gfile.Rename("", ""), nil)
})
}
func TestCopy(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/testfile_copyfile1.txt"
topath string = "/testfile_copyfile2.txt"
)
createTestFile(paths, "")
defer delTestFiles(paths)
gtest.Assert(gfile.Copy(testpath()+paths, testpath()+topath), nil)
defer delTestFiles(topath)
gtest.Assert(gfile.IsFile(testpath()+topath), true)
gtest.AssertNE(gfile.Copy("", ""), nil)
})
}
func TestDirNames(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/testdirs"
err error
readlist []string
)
havelist := []string{
"t1.txt",
"t2.txt",
}
// 创建测试文件
createDir(paths)
for _, v := range havelist {
createTestFile(paths+"/"+v, "")
}
defer delTestFiles(paths)
readlist, err = gfile.DirNames(testpath() + paths)
gtest.Assert(err, nil)
gtest.Assert(havelist, readlist)
_, err = gfile.DirNames("")
gtest.AssertNE(err, nil)
})
}
func TestGlob(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/testfiles/*.txt"
dirpath string = "/testfiles"
err error
resultlist []string
)
havelist1 := []string{
"t1.txt",
"t2.txt",
}
havelist2 := []string{
testpath() + "/testfiles/t1.txt",
testpath() + "/testfiles/t2.txt",
}
//===============================构建测试文件
createDir(dirpath)
for _, v := range havelist1 {
createTestFile(dirpath+"/"+v, "")
}
defer delTestFiles(dirpath)
resultlist, err = gfile.Glob(testpath()+paths, true)
gtest.Assert(err, nil)
gtest.Assert(resultlist, havelist1)
resultlist, err = gfile.Glob(testpath()+paths, false)
gtest.Assert(err, nil)
gtest.Assert(formatpaths(resultlist), formatpaths(havelist2))
_, err = gfile.Glob("", true)
gtest.Assert(err, nil)
})
}
func TestRemove(t *testing.T) {
gtest.Case(t, func() {
var (
paths string = "/testfile_t1.txt"
)
createTestFile(paths, "")
gtest.Assert(gfile.Remove(testpath()+paths), nil)
gtest.Assert(gfile.Remove(""), nil)
defer delTestFiles(paths)
})
}
func TestIsReadable(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_GetContents.txt"
paths2 string = "./testfile_GetContents_no.txt"
)
createTestFile(paths1, "")
defer delTestFiles(paths1)
gtest.Assert(gfile.IsReadable(testpath()+paths1), true)
gtest.Assert(gfile.IsReadable(paths2), false)
})
}
func TestIsWritable(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_GetContents.txt"
paths2 string = "./testfile_GetContents_no.txt"
)
createTestFile(paths1, "")
defer delTestFiles(paths1)
gtest.Assert(gfile.IsWritable(testpath()+paths1), true)
gtest.Assert(gfile.IsWritable(paths2), false)
})
}
func TestChmod(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_GetContents.txt"
paths2 string = "./testfile_GetContents_no.txt"
)
createTestFile(paths1, "")
defer delTestFiles(paths1)
gtest.Assert(gfile.Chmod(testpath()+paths1, 0777), nil)
gtest.AssertNE(gfile.Chmod(paths2, 0777), nil)
})
}
func TestScanDir(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfiledirs"
files []string
err error
)
createDir(paths1)
createTestFile(paths1+"/t1.txt", "")
createTestFile(paths1+"/t2.txt", "")
defer delTestFiles(paths1)
files, err = gfile.ScanDir(testpath()+paths1, "t*")
result := []string{
testpath() + paths1 + "/t1.txt",
testpath() + paths1 + "/t2.txt",
}
gtest.Assert(err, nil)
gtest.Assert(formatpaths(files), formatpaths(result))
_, err = gfile.ScanDir("", "t*")
gtest.AssertNE(err, nil)
})
}
// 获取绝对目录地址
func TestRealPath(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_files"
readlPath string
tempstr string
)
createDir(paths1)
defer delTestFiles(paths1)
readlPath = gfile.RealPath("./")
tempstr, _ = filepath.Abs("./")
gtest.Assert(readlPath, tempstr)
gtest.Assert(gfile.RealPath("./nodirs"), "")
})
}
// 获取当前执行文件的目录
func TestSelfPath(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string
readlPath string
tempstr string
)
readlPath = gfile.SelfPath()
readlPath = filepath.ToSlash(readlPath)
tempstr, _ = filepath.Abs(os.Args[0])
paths1 = filepath.ToSlash(tempstr)
paths1 = strings.Replace(paths1, "./", "/", 1)
gtest.Assert(readlPath, paths1)
})
}
func TestSelfDir(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string
readlPath string
tempstr string
)
readlPath = gfile.SelfDir()
tempstr, _ = filepath.Abs(os.Args[0])
paths1 = filepath.Dir(tempstr)
gtest.Assert(readlPath, paths1)
})
}
func TestBasename(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfilerr_GetContents.txt"
readlPath string
)
createTestFile(paths1, "")
defer delTestFiles(paths1)
readlPath = gfile.Basename(testpath() + paths1)
gtest.Assert(readlPath, "testfilerr_GetContents.txt")
})
}
func TestDir(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfiless"
readlPath string
)
createDir(paths1)
defer delTestFiles(paths1)
readlPath = gfile.Dir(testpath() + paths1)
gtest.Assert(readlPath, testpath())
})
}
// 获取文件名
func TestExt(t *testing.T) {
gtest.Case(t, func() {
var (
paths1 string = "/testfile_GetContents.txt"
dirpath1 = "/testdirs"
)
createTestFile(paths1, "")
defer delTestFiles(paths1)
createDir(dirpath1)
defer delTestFiles(dirpath1)
gtest.Assert(gfile.Ext(testpath()+paths1), ".txt")
gtest.Assert(gfile.Ext(testpath()+dirpath1), "")
})
}
func TestTempDir(t *testing.T) {
gtest.Case(t, func() {
var (
tpath string
)
tpath = gfile.TempDir()
gtest.Assert(tpath, os.TempDir())
})
}
func TestMkdir(t *testing.T) {
gtest.Case(t, func() {
var (
tpath string = "/testfile/createdir"
err error
)
defer delTestFiles("/testfile")
err = gfile.Mkdir(testpath() + tpath)
gtest.Assert(err, nil)
err = gfile.Mkdir("")
gtest.AssertNE(err, nil)
err = gfile.Mkdir(testpath() + tpath + "2/t1")
gtest.Assert(err, nil)
})
}
func TestStat(t *testing.T) {
gtest.Case(t, func() {
var (
tpath1 string = "/testfile_t1.txt"
tpath2 string = "./testfile_t1_no.txt"
err error
fileiofo os.FileInfo
)
createTestFile(tpath1, "a")
defer delTestFiles(tpath1)
fileiofo, err = gfile.Stat(testpath() + tpath1)
gtest.Assert(err, nil)
gtest.Assert(fileiofo.Size(), 1)
_, err = gfile.Stat(tpath2)
gtest.AssertNE(err, nil)
})
}
func TestMainPkgPath(t *testing.T) {
gtest.Case(t, func() {
var (
reads string
)
reads = gfile.MainPkgPath()
gtest.Assert(reads, "")
})
}

View File

@ -0,0 +1,45 @@
package gfile_test
import (
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"os"
"testing"
)
func TestMTime(t *testing.T) {
gtest.Case(t, func() {
var (
file1 string = "/testfile_t1.txt"
err error
fileobj os.FileInfo
)
createTestFile(file1, "")
defer delTestFiles(file1)
fileobj, err = os.Stat(testpath() + file1)
gtest.Assert(err, nil)
gtest.Assert(gfile.MTime(testpath()+file1), fileobj.ModTime().Unix())
gtest.Assert(gfile.MTime(""), 0)
})
}
func TestMTimeMillisecond(t *testing.T) {
gtest.Case(t, func() {
var (
file1 string = "/testfile_t1.txt"
err error
fileobj os.FileInfo
)
createTestFile(file1, "")
defer delTestFiles(file1)
fileobj, err = os.Stat(testpath() + file1)
gtest.Assert(err, nil)
gtest.AssertGTE(gfile.MTimeMillisecond(testpath()+file1), fileobj.ModTime().Nanosecond()/1000000)
gtest.Assert(gfile.MTimeMillisecond(""), 0)
})
}

View File

@ -3,11 +3,10 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
// @author john, zseeker
// Package glog implements powerful and easy-to-use levelled logging functionality.
//
// 日志模块, 直接文件/输出操作,没有异步逻辑,没有使用缓存或者通道
package glog
import (
@ -29,10 +28,10 @@ const (
)
var (
// default level for log
// Default level for log
defaultLevel = gtype.NewInt(LEVEL_ALL)
// default logger object, for package method usage
// Default logger object, for package method usage
logger = New()
)
@ -41,8 +40,6 @@ func init() {
}
// SetPath sets the directory path for file logging.
//
// 日志日志目录绝对路径.
func SetPath(path string) {
logger.SetPath(path)
}
@ -50,151 +47,113 @@ func SetPath(path string) {
// SetFile sets the file name <pattern> for file logging.
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
//
// 日志文件名称.
func SetFile(pattern string) {
logger.SetFile(pattern)
}
// SetLevel sets the default logging level.
//
// 设置全局的日志记录等级.
func SetLevel(level int) {
logger.SetLevel(level)
defaultLevel.Set(level)
}
// SetWriter sets the customed logging <writer> for logging.
// SetWriter sets the customized logging <writer> for logging.
// The <writer> object should implements the io.Writer interface.
// Developer can use customed logging <writer> to redirect logging output to another service,
// Developer can use customized logging <writer> to redirect logging output to another service,
// eg: kafka, mysql, mongodb, etc.
//
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出.
func SetWriter(writer io.Writer) {
logger.SetWriter(writer)
}
// GetWriter returns the customed writer object, which implements the io.Writer interface.
// It returns nil if no customed writer set.
//
// 返回自定义的IO默认为nil.
// GetWriter returns the customized writer object, which implements the io.Writer interface.
// It returns nil if no customized writer set.
func GetWriter() io.Writer {
return logger.GetWriter()
}
// GetLevel returns the default logging level value.
//
// 获取全局的日志记录等级.
func GetLevel() int {
return defaultLevel.Val()
}
// SetDebug enables/disables the debug level for default logger.
// The debug level is enbaled in default.
//
// 设置是否允许输出DEBUG信息.
func SetDebug(debug bool) {
logger.SetDebug(debug)
}
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
//
// 设置写日志的同时开启or关闭控制台打印默认是关闭的
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false in default.
func SetStdPrint(open bool) {
logger.SetStdPrint(open)
}
// GetPath returns the logging directory path for file logging.
// It returns empty string if no directory path set.
//
// 获取日志目录绝对路径
func GetPath() string {
return logger.GetPath()
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 打印文件调用回溯信息
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func PrintBacktrace(skip...int) {
logger.PrintBacktrace(skip...)
}
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 获取文件调用回溯信息.
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func GetBacktrace(skip...int) string {
return logger.GetBacktrace(skip...)
}
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
//
// 是否关闭全局的backtrace信息
func SetBacktrace(enabled bool) {
logger.SetBacktrace(enabled)
}
// To is a chaining function,
// which redirects current logging content output to the sepecified <writer>.
//
// 链式操作设置下一次写入日志内容的Writer
func To(writer io.Writer) *Logger {
return logger.To(writer)
}
// Path is a chaining function,
// which sets the directory path to <path> for current logging content output.
//
// 链式操作,设置下一次输出的日志路径。
func Path(path string) *Logger {
return logger.Path(path)
}
// Cat is a chaining function,
// which sets the category to <category> for current logging content output.
//
// 设置下一次输出的分类,支持多级分类设置.
func Cat(category string) *Logger {
return logger.Cat(category)
}
// File is a chaining function,
// which sets file name <pattern> for the current logging content output.
//
// 设置日志输出文件名称格式
func File(pattern string) *Logger {
return logger.File(pattern)
}
// Level is a chaining function,
// which sets logging level for the current logging content output.
//
// 设置日志打印等级.
func Level(level int) *Logger {
return logger.Level(level)
}
// Backtrace is a chaining function,
// which sets backtrace options for the current logging content output .
//
// 设置文件调用回溯信息.
func Backtrace(enabled bool, skip...int) *Logger {
return logger.Backtrace(enabled, skip...)
}
// StdPrint is a chaining function,
// which enables/disables stdout for the current logging content output.
//
// 是否允许在设置输出文件时同时也输出到终端
func StdPrint(enabled bool) *Logger {
return logger.StdPrint(enabled)
}
// Header is a chaining function,
// which enables/disables log header for the current logging content output.
//
// 是否打印每行日志头信息(默认开启)
func Header(enabled bool) *Logger {
return logger.Header(enabled)
}

View File

@ -3,6 +3,7 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
// @author john, zseeker
package glog
@ -26,15 +27,15 @@ import (
type Logger struct {
mu sync.RWMutex
pr *Logger // 父级Logger
io io.Writer // 日志内容写入的IO接口
path *gtype.String // 日志写入的目录路径
file *gtype.String // 日志文件名称格式
level *gtype.Int // 日志输出等级
btSkip *gtype.Int // 错误产生时的backtrace回调信息skip条数
btStatus *gtype.Int // 是否当打印错误时同时开启backtrace打印(默认-1表示默认打印逻辑 - 错误才打印)
printHeader *gtype.Bool // 是否不打印前缀信息(时间,级别等)
alsoStdPrint *gtype.Bool // 控制台打印开关,当输出到文件/自定义输出时也同时打印到终端
pr *Logger // Parent logger.
writer io.Writer // Customized io.Writer.
path *gtype.String // Logging directory path.
file *gtype.String // Format for logging file.
level *gtype.Int // Output level.
btSkip *gtype.Int // Skip count for backtrace.
btStatus *gtype.Int // Backtrace status(1: enabled - default; 0: disabled)
printHeader *gtype.Bool // Print header or not(true in default).
alsoStdPrint *gtype.Bool // Output to stdout or not(true in default).
}
const (
@ -45,70 +46,66 @@ const (
)
var (
// 默认的日志换行符
// Default line break.
ln = "\n"
// 标准输出互斥锁,防止标准输出串日志
// Mutex to ensure log output sequence.
stdMu = sync.RWMutex{}
)
// 初始化日志换行符
func init() {
// Initialize log line breaks depending on underlying os.
if runtime.GOOS == "windows" {
ln = "\r\n"
}
}
// New creates a custom logger.
//
// 新建自定义的日志操作对象
// New creates and returns a custom logger.
func New() *Logger {
return &Logger {
io : nil,
logger := &Logger {
path : gtype.NewString(),
file : gtype.NewString(gDEFAULT_FILE_FORMAT),
level : gtype.NewInt(defaultLevel.Val()),
btSkip : gtype.NewInt(),
btStatus : gtype.NewInt(-1),
btStatus : gtype.NewInt(1),
printHeader : gtype.NewBool(true),
alsoStdPrint : gtype.NewBool(true),
}
logger.writer = &Writer {
logger : logger,
}
return logger
}
// Clone returns a new logger, which is the clone the current logger.
//
// Logger拷贝.
func (l *Logger) Clone() *Logger {
return &Logger {
logger := &Logger {
pr : l,
io : l.GetWriter(),
path : l.path.Clone(),
file : l.file.Clone(),
level : l.level.Clone(),
btSkip : l.btSkip.Clone(),
btStatus : l.btStatus.Clone(),
btStatus : l.btStatus.Clone(),
printHeader : l.printHeader.Clone(),
alsoStdPrint : l.alsoStdPrint.Clone(),
}
logger.writer = &Writer {
logger : logger,
}
return logger
}
// SetLevel sets the logging level.
//
// 设置日志记录等级
func (l *Logger) SetLevel(level int) {
l.level.Set(level)
}
// GetLevel returns the logging level value.
//
// 获取日志记录等级
func (l *Logger) GetLevel() int {
return l.level.Val()
}
// SetDebug enables/disables the debug level for logger.
// The debug level is enbaled in default.
//
// 快捷方法打开或关闭DEBU日志信息
// The debug level is enabled in default.
func (l *Logger) SetDebug(debug bool) {
if debug {
l.level.Set(l.level.Val() | LEVEL_DEBU)
@ -124,7 +121,6 @@ func (l *Logger) SetBacktrace(enabled bool) {
} else {
l.btStatus.Set(0)
}
}
// SetBacktraceSkip sets the backtrace offset from the end point.
@ -132,47 +128,41 @@ func (l *Logger) SetBacktraceSkip(skip int) {
l.btSkip.Set(skip)
}
// SetWriter sets the customed logging <writer> for logging.
// SetWriter sets the customized logging <writer> for logging.
// The <writer> object should implements the io.Writer interface.
// Developer can use customed logging <writer> to redirect logging output to another service,
// Developer can use customized logging <writer> to redirect logging output to another service,
// eg: kafka, mysql, mongodb, etc.
//
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
func (l *Logger) SetWriter(writer io.Writer) {
l.mu.Lock()
l.io = writer
l.writer = writer
l.mu.Unlock()
}
// GetWriter returns the customed writer object, which implements the io.Writer interface.
// It returns nil if no customed writer set.
//
// 返回自定义的IO默认为nil
// GetWriter returns the customized writer object, which implements the io.Writer interface.
// It returns a default writer if no customized writer set.
func (l *Logger) GetWriter() io.Writer {
l.mu.RLock()
r := l.io
r := l.writer
l.mu.RUnlock()
return r
}
// getFilePointer returns the file pinter for file logging.
// It returns nil if file logging disabled, or file open fails.
//
// 获取默认的文件IO.
// It returns nil if file logging is disabled, or file opening fails.
func (l *Logger) getFilePointer() *gfpool.File {
if path := l.path.Val(); path != "" {
// 文件名称中使用"{}"包含的内容使用gtime格式化
// Content containing "{}" in the file name is formatted using gtime
file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string {
return gtime.Now().Format(strings.Trim(s, "{}"))
})
// 如果日志目录不存在则创建目录路径
// Create path if it does not exist。
if !gfile.Exists(path) {
if err := gfile.Mkdir(path); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
return nil
}
}
fpath := path + gfile.Separator + file
fpath := path + gfile.Separator + file
if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, gDEFAULT_FPOOL_PERM, gDEFAULT_FPOOL_EXPIRE); err == nil {
return fp
} else {
@ -183,14 +173,10 @@ func (l *Logger) getFilePointer() *gfpool.File {
}
// SetPath sets the directory path for file logging.
//
// 设置日志文件的存储目录路径.
func (l *Logger) SetPath(path string) error {
// path必须有值
if path == "" {
return errors.New("path is empty")
}
// 如果目录不存在,则递归创建
if !gfile.Exists(path) {
if err := gfile.Mkdir(path); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
@ -203,8 +189,6 @@ func (l *Logger) SetPath(path string) error {
// GetPath returns the logging directory path for file logging.
// It returns empty string if no directory path set.
//
// 获取设置的日志目录路径
func (l *Logger) GetPath() string {
return l.path.Val()
}
@ -212,29 +196,24 @@ func (l *Logger) GetPath() string {
// SetFile sets the file name <pattern> for file logging.
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
//
// 设置日志文件名称格式.
func (l *Logger) SetFile(pattern string) {
l.file.Set(pattern)
}
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
//
// 设置写日志时开启or关闭控制台打印默认是关闭的
// SetStdPrint sets whether output the logging contents to stdout, which is false in default.
func (l *Logger) SetStdPrint(enabled bool) {
l.alsoStdPrint.Set(enabled)
}
// 这里的写锁保证统一时刻只会写入一行日志,防止串日志的情况
// print prints <s> to defined writer, logging file or passed <std>.
// It internally uses memory lock for file logging to ensure logging sequence.
func (l *Logger) print(std io.Writer, s string) {
// 优先使用自定义的IO输出
// Customized writer has the most high priority.
if l.printHeader.Val() {
s = l.format(s)
}
writer := l.GetWriter()
if writer == nil {
// 如果设置的writer为空那么其次判断是否有文件输出设置
// 内部使用了内存锁保证在glog中对同一个日志文件的并发写入不会串日志(并发安全)
if _, ok := writer.(*Writer); ok {
if f := l.getFilePointer(); f != nil {
defer f.Close()
key := l.path.Val()
@ -245,7 +224,7 @@ func (l *Logger) print(std io.Writer, s string) {
fmt.Fprintln(os.Stderr, err.Error())
}
}
// 当没有设置writer时需要判断是否允许输出到标准输出
// Also output to stdout?
if l.alsoStdPrint.Val() {
l.doStdLockPrint(std, s)
}
@ -254,7 +233,7 @@ func (l *Logger) print(std io.Writer, s string) {
}
}
// 并发安全打印到标准输出
// doStdLockPrint prints <s> to <std> concurrent-safely.
func (l *Logger) doStdLockPrint(std io.Writer, s string) {
stdMu.Lock()
if _, err := std.Write([]byte(s)); err != nil {
@ -263,23 +242,21 @@ func (l *Logger) doStdLockPrint(std io.Writer, s string) {
stdMu.Unlock()
}
// 核心打印数据方法(标准输出)
// stdPrint prints content <s> without backtrace.
func (l *Logger) stdPrint(s string) {
l.print(os.Stdout, s)
}
// 核心打印数据方法(标准错误)
// stdPrint prints content <s> with backtrace check.
func (l *Logger) errPrint(s string) {
// 记录调用回溯信息
status := l.btStatus.Val()
if status == -1 || status == 1 {
if l.btStatus.Val() == 1 {
s = l.appendBacktrace(s)
}
// 防止串日志情况这里不使用stderr而是使用stdout
// In matter of sequence, do not use stderr here, but use the same stdout.
l.print(os.Stdout, s)
}
// 输出内容中添加回溯信息
// appendBacktrace appends backtrace to the <s>.
func (l *Logger) appendBacktrace(s string, skip...int) string {
trace := l.GetBacktrace(skip...)
if trace != "" {
@ -298,17 +275,13 @@ func (l *Logger) appendBacktrace(s string, skip...int) string {
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 直接打印回溯信息参数skip表示调用端往上多少级开始回溯
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func (l *Logger) PrintBacktrace(skip...int) {
l.Println(l.appendBacktrace("", skip...))
}
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 获取文件调用回溯字符串参数skip表示调用端往上多少级开始回溯
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func (l *Logger) GetBacktrace(skip...int) string {
customSkip := 0
if len(skip) > 0 {
@ -507,8 +480,6 @@ func (l *Logger) Criticalfln(format string, v ...interface{}) {
}
// checkLevel checks whether the given <level> could be output.
//
// 判断给定level是否满足
func (l *Logger) checkLevel(level int) bool {
return l.level.Val() & level > 0
}

View File

@ -13,8 +13,6 @@ import (
// To is a chaining function,
// which redirects current logging content output to the specified <writer>.
//
// 链式操作设置下一次写入日志内容的Writer
func (l *Logger) To(writer io.Writer) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -28,8 +26,6 @@ func (l *Logger) To(writer io.Writer) *Logger {
// Path is a chaining function,
// which sets the directory path to <path> for current logging content output.
//
// 链式操作,设置下一次输出的日志路径。
func (l *Logger) Path(path string) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -45,9 +41,7 @@ func (l *Logger) Path(path string) *Logger {
// Cat is a chaining function,
// which sets the category to <category> for current logging content output.
//
// 链式操作,设置下一次输出的日志分类(可以按照文件目录层级设置)在当前logpath或者当前工作目录下创建category目录
// 这是一个链式操作,可以设置多个分类,将会创建层级的日志分类目录。
// Param <category> can be hierarchical, eg: module/user.
func (l *Logger) Cat(category string) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -64,8 +58,6 @@ func (l *Logger) Cat(category string) *Logger {
// File is a chaining function,
// which sets file name <pattern> for the current logging content output.
//
// 日志文件格式
func (l *Logger) File(file string) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -79,8 +71,6 @@ func (l *Logger) File(file string) *Logger {
// Level is a chaining function,
// which sets logging level for the current logging content output.
//
// 设置日志打印等级
func (l *Logger) Level(level int) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -94,8 +84,6 @@ func (l *Logger) Level(level int) *Logger {
// Backtrace is a chaining function,
// which sets backtrace options for the current logging content output .
//
// 设置文件调用回溯信息
func (l *Logger) Backtrace(enabled bool, skip...int) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -112,8 +100,6 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger {
// StdPrint is a chaining function,
// which enables/disables stdout for the current logging content output.
//
// 是否允许在设置输出文件时同时也输出到终端
func (l *Logger) StdPrint(enabled bool) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
@ -127,8 +113,6 @@ func (l *Logger) StdPrint(enabled bool) *Logger {
// Header is a chaining function,
// which enables/disables log header for the current logging content output.
//
// 是否打印每行日志头信息(默认开启)
func (l *Logger) Header(enabled bool) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {

View File

@ -0,0 +1,18 @@
// 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 glog
type Writer struct {
logger *Logger
}
// Write implements the io.Writer interface.
// It just prints the content with header or level.
func (w *Writer) Write(p []byte) (n int, err error) {
w.logger.Header(false).Print(string(p))
return len(p), nil
}

View File

@ -7,142 +7,159 @@
package gtime
import (
"bytes"
"github.com/gogf/gf/g/text/gregex"
"bytes"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"strings"
)
var (
// 参考http://php.net/manual/zh/function.date.php
formats = map[byte]string {
// ================== 日 ==================
'd' : "02", // 月份中的第几天,有前导零的 2 位数字(01 到 31)
'D' : "Mon", // 星期中的第几天文本表示3 个字母(Mon 到 Sun)
'j' : "=j=02", // 月份中的第几天,没有前导零(1 到 31)
'l' : "Monday", // ("L"的小写字母)星期几,完整的文本格式(Sunday 到 Saturday)
// 参考http://php.net/manual/zh/function.date.php
formats = map[byte]string{
// ================== 日 ==================
'd': "02", // 月份中的第几天,有前导零的 2 位数字(01 到 31)
'D': "Mon", // 星期中的第几天文本表示3 个字母(Mon 到 Sun)
'w': "Monday", // 星期中的第几天,数字型式的文本表示 0为星期天 6为星期六
'j': "=j=02", // 月份中的第几天,没有前导零(1 到 31)
'l': "Monday", // ("L"的小写字母)星期几,完整的文本格式(Sunday 到 Saturday)
// ================== 月 ==================
'F' : "January", // 月份,完整的文本格式,例如 January 或者 March January 到 December
'm' : "01", // 数字表示的月份,有前导零(01 到 12)
'M' : "Jan", // 三个字母缩写表示的月份(Jan 到 Dec)
'n' : "1", // 数字表示的月份,没有前导零(1 到 12)
// ================== 月 ==================
'F': "January", // 月份,完整的文本格式,例如 January 或者 March January 到 December
'm': "01", // 数字表示的月份,有前导零(01 到 12)
'M': "Jan", // 三个字母缩写表示的月份(Jan 到 Dec)
'n': "1", // 数字表示的月份,没有前导零(1 到 12)
// ================== 年 ==================
'Y' : "2006", // 4 位数字完整表示的年份, 例如1999 或 2003
'y' : "06", // 2 位数字表示的年份, 例如99 或 03
// ================== 年 ==================
'Y': "2006", // 4 位数字完整表示的年份, 例如1999 或 2003
'y': "06", // 2 位数字表示的年份, 例如99 或 03
// ================== 时间 ==================
'a' : "pm", // 小写的上午和下午值 am 或 pm
'A' : "PM", // 大写的上午和下午值 AM 或 PM
'g' : "3", // 小时12 小时格式,没有前导零, 1 到 12
'G' : "=G=15", // 小时24 小时格式,没有前导零, 0 到 23
'h' : "03", // 小时12 小时格式,有前导零, 01 到 12
'H' : "15", // 小时24 小时格式,有前导零, 00 到 23
'i' : "04", // 有前导零的分钟数, 00 到 59
's' : "05", // 秒数,有前导零, 00 到 59
'u' : "=u=.000", // 毫秒(3位)
// ================== 时间 ==================
'a': "pm", // 小写的上午和下午值 am 或 pm
'A': "PM", // 大写的上午和下午值 AM 或 PM
'g': "3", // 小时12 小时格式,没有前导零, 1 到 12
'G': "=G=15", // 小时24 小时格式,没有前导零, 0 到 23
'h': "03", // 小时12 小时格式,有前导零, 01 到 12
'H': "15", // 小时24 小时格式,有前导零, 00 到 23
'i': "04", // 有前导零的分钟数, 00 到 59
's': "05", // 秒数,有前导零, 00 到 59
'u': "=u=.000", // 毫秒(3位)
// ================== 时区 ==================
'O' : "-0700", // 与UTC相差的小时数, 例如:+0200
'P' : "-07:00", // 与UTC的差别小时和分钟之间有冒号分隔, 例如:+02:00
'T' : "MST", // 时区缩写, 例如UTCGMTCST
// ================== 时区 ==================
'O': "-0700", // 与UTC相差的小时数, 例如:+0200
'P': "-07:00", // 与UTC的差别小时和分钟之间有冒号分隔, 例如:+02:00
'T': "MST", // 时区缩写, 例如UTCGMTCST
// ================== 完整的日期/时间 ==================
'c' : "2006-01-02T15:04:05-07:00", // ISO 8601 格式的日期例如2004-02-12T15:19:21+00:00
'r' : "Mon, 02 Jan 06 15:04 MST", // RFC 822 格式的日期例如Thu, 21 Dec 2000 16:01:07 +0200
}
// ================== 完整的日期/时间 ==================
'c': "2006-01-02T15:04:05-07:00", // ISO 8601 格式的日期例如2004-02-12T15:19:21+00:00
'r': "Mon, 02 Jan 06 15:04 MST", // RFC 822 格式的日期例如Thu, 21 Dec 2000 16:01:07 +0200
}
// 星期的英文值和数字值对应map
weekMap = map[string]string{
"Sunday": "0",
"Monday": "1",
"Tuesday": "2",
"Wednesday": "3",
"Thursday": "4",
"Friday": "5",
"Saturday": "6",
}
)
// 将自定义的格式转换为标准库时间格式
func formatToStdLayout(format string) string {
b := bytes.NewBuffer(nil)
for i := 0; i < len(format); {
switch format[i] {
case '\\':
if i < len(format) - 1 {
b.WriteByte(format[i + 1])
i += 2
continue
} else {
return b.String()
}
b := bytes.NewBuffer(nil)
for i := 0; i < len(format); {
switch format[i] {
case '\\':
if i < len(format)-1 {
b.WriteByte(format[i+1])
i += 2
continue
} else {
return b.String()
}
default:
if f, ok := formats[format[i]]; ok {
// 有几个转换的符号需要特殊处理
switch format[i] {
case 'j':
b.WriteString("02")
case 'G':
b.WriteString("15")
case 'u':
if i > 0 && format[i - 1] == '.' {
b.WriteString("000")
} else {
b.WriteString(".000")
}
default:
if f, ok := formats[format[i]]; ok {
// 有几个转换的符号需要特殊处理
switch format[i] {
case 'j':
b.WriteString("02")
case 'G':
b.WriteString("15")
case 'u':
if i > 0 && format[i-1] == '.' {
b.WriteString("000")
} else {
b.WriteString(".000")
}
default:
b.WriteString(f)
}
} else {
b.WriteByte(format[i])
}
i++
}
}
return b.String()
default:
b.WriteString(f)
}
} else {
b.WriteByte(format[i])
}
i++
}
}
return b.String()
}
// 将format格式转换为正则表达式规则
func formatToRegexPattern(format string) string {
s := gregex.Quote(formatToStdLayout(format))
s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
return s
s := gregex.Quote(formatToStdLayout(format))
s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
return s
}
// 格式化,使用自定义日期格式
func (t *Time) Format(format string) string {
runes := []rune(format)
buffer := bytes.NewBuffer(nil)
for i := 0; i < len(runes); {
switch runes[i] {
case '\\':
if i < len(runes) - 1 {
buffer.WriteRune(runes[i + 1])
i += 2
continue
} else {
return buffer.String()
}
runes := []rune(format)
buffer := bytes.NewBuffer(nil)
for i := 0; i < len(runes); {
switch runes[i] {
case '\\':
if i < len(runes)-1 {
buffer.WriteRune(runes[i+1])
i += 2
continue
} else {
return buffer.String()
}
default:
if runes[i] > 255 {
buffer.WriteRune(runes[i])
break
}
if f, ok := formats[byte(runes[i])]; ok {
result := t.Time.Format(f)
// 有几个转换的符号需要特殊处理
switch runes[i] {
case 'j': buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""}))
case 'G': buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""}))
case 'u': buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
default:
buffer.WriteString(result)
}
} else {
buffer.WriteRune(runes[i])
}
}
i++
}
return buffer.String()
default:
if runes[i] > 255 {
buffer.WriteRune(runes[i])
break
}
if f, ok := formats[byte(runes[i])]; ok {
result := t.Time.Format(f)
// 有几个转换的符号需要特殊处理
switch runes[i] {
case 'j':
buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""}))
case 'G':
buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""}))
case 'u':
buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
case 'w':
buffer.WriteString(weekMap[result])
default:
buffer.WriteString(result)
}
} else {
buffer.WriteRune(runes[i])
}
}
i++
}
return buffer.String()
}
// 格式化,使用标准库格式
func (t *Time) Layout(layout string) string {
return t.Time.Format(layout)
}
return t.Time.Format(layout)
}

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