Compare commits

...

115 Commits

Author SHA1 Message Date
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
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
0b57771d76 用fmt格式化代码 2019-04-12 09:47:55 +08:00
8a32a8271c fix issue in empty router group error of ghttp.Server 2019-04-12 00:19:15 +08:00
c3e716dafd Merge branch 'master' of https://github.com/gogf/gf 2019-04-11 23:07:28 +08:00
119a11eb8d README updates 2019-04-11 23:07:22 +08:00
e464d14842 Merge pull request #85 from qq976739120/gmap-test
Gmap 测试添加
2019-04-11 21:59:00 +08:00
Jay
905f46359a Gmap string-string 类型测试提交 2019-04-11 17:50:26 +08:00
Jay
8e7bf1f908 bug 修复 2019-04-11 17:46:19 +08:00
Jay
3a1524ae6d Gmap 初测完成 2019-04-11 17:33:52 +08:00
5c04befea3 edit ci yml file 2019-04-11 16:39:20 +08:00
ae3584cdff edit ci 2019-04-11 16:27:47 +08:00
a60578c82c edit ci 2019-04-11 16:23:29 +08:00
74558a500c edit c1 2019-04-11 16:17:44 +08:00
d963d8c8c1 testci 2019-04-11 16:13:56 +08:00
853892c24f config ci 2019-04-11 16:05:33 +08:00
641f939e3d edit test 2019-04-11 15:57:48 +08:00
af77504eaf 修改测试 2019-04-11 15:49:00 +08:00
62649d6468 config ci 2019-04-11 15:22:29 +08:00
bb914e605e 配置ci 2019-04-11 15:08:17 +08:00
62ee88bbfa Update .travis.yml 2019-04-11 14:45:47 +08:00
99c964bb4a 添加ci配置文 2019-04-11 11:02:43 +08:00
09ceaef3e9 配置ci 2019-04-11 10:59:53 +08:00
a82900af55 对分支添加ci检查 2019-04-11 10:03:47 +08:00
5ef589f31a Merge pull request #8 from gogf/master
合并前更新。
2019-04-11 09:56:01 +08:00
cd719f134d 测试率达到82.9% 2019-04-11 09:47:51 +08:00
f69eb219b5 version updates 2019-04-11 09:43:17 +08:00
0ffe17ee3d revert SearchArray/InArray functions for gstr 2019-04-11 09:26:52 +08:00
4ac647a215 gcmd updates 2019-04-11 09:05:27 +08:00
872536c035 Merge branch 'master' of https://github.com/gogf/gf 2019-04-10 23:09:59 +08:00
c7a6a6fff0 improve grand.Intn; README updates 2019-04-10 23:09:38 +08:00
2534655bc8 测试覆盖率达到76.3% 2019-04-10 22:16:16 +08:00
0cb82d70fd Merge pull request #96 from aloncn/master
update unit test for gring
2019-04-10 21:42:25 +08:00
acac5a2ad6 version updates 2019-04-10 18:33:51 +08:00
9ec15ad2ca fix issue in repeated rand value of grand.Intn 2019-04-10 18:33:12 +08:00
a9b7d56d0b 测试覆盖率达到63.1% 2019-04-10 17:54:28 +08:00
e9ca1eb538 完成第一遍功能测试 2019-04-10 16:45:33 +08:00
0532800895 添加测试代码 2019-04-10 16:22:29 +08:00
ad50ca6e60 update unit test for gring 2019-04-10 14:26:21 +08:00
c1ad999c25 添加测试 2019-04-10 13:45:42 +08:00
4accd1264d 添加内容测试 2019-04-10 13:39:36 +08:00
e1f3da3aa8 去掉mod设置的版本信息 2019-04-10 11:31:24 +08:00
4bf9a7950b Merge branch 'master' into test_gfile 2019-04-10 11:28:57 +08:00
8285c31bf1 Update unit test for gring 2019-04-10 10:44:40 +08:00
230be66fa9 update unit test for gring 2019-04-10 10:13:56 +08:00
2c53f934c9 Merge branch 'master' of https://github.com/gogf/gf 2019-04-10 09:50:29 +08:00
08785cb272 update unit test cases of gjson 2019-04-10 08:54:49 +08:00
bd0207c938 update unit test for gring 2019-04-10 01:16:45 +08:00
6fad737617 update unit test cases of gjson/gparser 2019-04-10 01:00:51 +08:00
af4148d985 Merge branch 'master' of github.com:aloncn/gf 2019-04-10 00:37:22 +08:00
922eaf4d42 Update unit test for gring 2019-04-10 00:37:06 +08:00
4b5153950f Add unit test for gring 2019-04-10 00:37:06 +08:00
78010d2bd7 Update unit test for gring 2019-04-10 00:22:10 +08:00
6cc0017826 测试空的断言。 2019-04-09 23:25:20 +08:00
8a9131c3dc Merge pull request #7 from gogf/master
日常更新
2019-04-09 23:16:18 +08:00
85c2ed1bf2 improve nil checks for gtest.Assert* 2019-04-09 23:12:22 +08:00
aca1df634d 添加测试文件内容相关函数。 2019-04-09 23:11:04 +08:00
429aa90e0d Merge branch 'master' of github.com:aloncn/gf 2019-04-09 18:42:16 +08:00
4f792b347d Add unit test for gring 2019-04-09 18:40:48 +08:00
6d8ced21b9 Add unit test for gring 2019-04-09 17:27:58 +08:00
Jay
277b7a4536 测试修改 2019-04-09 15:06:12 +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
61a67892ac Merge pull request #6 from gogf/master
同步最新代码
2019-04-04 11:37:58 +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
61 changed files with 3652 additions and 804 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,14 +64,18 @@ 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)
- [wenzi1](https://gitee.com/wenzi1)
- [wxkj001](https://github.com/wxkj001)
- [ymrjqyy](https://gitee.com/ymrjqyy)
@ -84,6 +88,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)
@ -96,4 +101,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
@ -86,12 +85,15 @@ func main() {
# 贡献者
- [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)
- [wenzi1](https://gitee.com/wenzi1)
- [wxkj001](https://github.com/wxkj001)
- [ymrjqyy](https://gitee.com/ymrjqyy)
@ -104,6 +106,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,7 +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

@ -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,87 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getBool() bool {
return true
}
func intBoolCallBack(int, bool) bool {
return true
}
func Test_IntBoolMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntBoolMap()
m.Set(1, true)
gtest.Assert(m.Get(1), true)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, false), false)
gtest.Assert(m.SetIfNotExist(2, false), false)
gtest.Assert(m.SetIfNotExist(3, false), true)
gtest.Assert(m.Remove(2), false)
gtest.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false})
m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false})
gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false})
})
}
func Test_IntBoolMap_Set_Fun(t *testing.T) {
m := gmap.NewIntBoolMap()
m.GetOrSetFunc(1, getBool)
m.GetOrSetFuncLock(2, getBool)
gtest.Assert(m.Get(1), true)
gtest.Assert(m.Get(2), true)
gtest.Assert(m.SetIfNotExistFunc(1, getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false)
}
func Test_IntBoolMap_Batch(t *testing.T) {
m := gmap.NewIntBoolMap()
m.BatchSet(map[int]bool{1: true, 2: false, 3: true})
m.Iterator(intBoolCallBack)
gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]bool{3: true})
}
func Test_IntBoolMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntBoolMap_Merge(t *testing.T) {
m1 := gmap.NewIntBoolMap()
m2 := gmap.NewIntBoolMap()
m1.Set(1, true)
m2.Set(2, false)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false})
}

View File

@ -0,0 +1,91 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getInt() int {
return 123
}
func intIntCallBack(int, int) bool {
return true
}
func Test_IntIntMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntIntMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, 2), 2)
gtest.Assert(m.SetIfNotExist(2, 2), false)
gtest.Assert(m.SetIfNotExist(3, 3), true)
gtest.Assert(m.Remove(2), 2)
gtest.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[int]int{1: 1, 3: 3})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
m3 := gmap.NewIntIntMapFromArray([]int{1, 2}, []int{1, 2})
gtest.Assert(m3.Map(), map[int]int{1: 1, 2: 2})
})
}
func Test_IntIntMap_Set_Fun(t *testing.T) {
m := gmap.NewIntIntMap()
m.GetOrSetFunc(1, getInt)
m.GetOrSetFuncLock(2, getInt)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getInt), false)
gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
}
func Test_IntIntMap_Batch(t *testing.T) {
m := gmap.NewIntIntMap()
m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3})
m.Iterator(intIntCallBack)
gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]int{3: 3})
}
func Test_IntIntMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntIntMap_Merge(t *testing.T) {
m1 := gmap.NewIntIntMap()
m2 := gmap.NewIntIntMap()
m1.Set(1, 1)
m2.Set(2, 2)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
}

View File

@ -0,0 +1,91 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getInterface() interface{} {
return 123
}
func intInterfaceCallBack(int, interface{}) bool {
return true
}
func Test_IntInterfaceMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntInterfaceMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, "2"), "2")
gtest.Assert(m.SetIfNotExist(2, "2"), false)
gtest.Assert(m.SetIfNotExist(3, 3), true)
gtest.Assert(m.Remove(2), "2")
gtest.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
m3 := gmap.NewIntInterfaceMapFromArray([]int{1, 2}, []interface{}{1, "2"})
gtest.Assert(m3.Map(), map[int]interface{}{1: 1, 2: "2"})
})
}
func Test_IntInterfaceMap_Set_Fun(t *testing.T) {
m := gmap.NewIntInterfaceMap()
m.GetOrSetFunc(1, getInterface)
m.GetOrSetFuncLock(2, getInterface)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false)
}
func Test_IntInterfaceMap_Batch(t *testing.T) {
m := gmap.NewIntInterfaceMap()
m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3})
m.Iterator(intInterfaceCallBack)
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
}
func Test_IntInterfaceMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntInterfaceMap_Merge(t *testing.T) {
m1 := gmap.NewIntInterfaceMap()
m2 := gmap.NewIntInterfaceMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
}

View File

@ -0,0 +1,96 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getString() string {
return "z"
}
func intStringCallBack(int, string) bool {
return true
}
func Test_IntStringMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntStringMap()
m.Set(1, "a")
gtest.Assert(m.Get(1), "a")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, "b"), "b")
gtest.Assert(m.SetIfNotExist(2, "b"), false)
gtest.Assert(m.SetIfNotExist(3, "c"), true)
gtest.Assert(m.Remove(2), "b")
gtest.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN("a", m.Values())
gtest.AssertIN("c", m.Values())
//反转之后不成为以下 map,flip 操作只是翻转原 map
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
m_f := gmap.NewIntStringMap()
m_f.Set(1, "2")
m_f.Flip()
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"})
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
m3 := gmap.NewIntStringMapFromArray([]int{1, 2}, []string{"a", "b"})
gtest.Assert(m3.Map(), map[int]string{1: "a", 2: "b"})
})
}
func Test_IntStringMap_Set_Fun(t *testing.T) {
m := gmap.NewIntStringMap()
m.GetOrSetFunc(1, getString)
m.GetOrSetFuncLock(2, getString)
gtest.Assert(m.Get(1), "z")
gtest.Assert(m.Get(2), "z")
gtest.Assert(m.SetIfNotExistFunc(1, getString), false)
gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false)
}
func Test_IntStringMap_Batch(t *testing.T) {
m := gmap.NewIntStringMap()
m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"})
m.Iterator(intStringCallBack)
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
m.BatchRemove([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
}
func Test_IntStringMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
}
func Test_IntStringMap_Merge(t *testing.T) {
m1 := gmap.NewIntStringMap()
m2 := gmap.NewIntStringMap()
m1.Set(1, "a")
m2.Set(2, "b")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
}

View File

@ -0,0 +1,91 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func getValue() interface{} {
return 3
}
func callBack(k interface{}, v interface{}) bool {
return true
}
func Test_Map_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.New()
m.Set("key1", "val1")
gtest.Assert(m.Keys(), []interface{}{"key1"})
gtest.Assert(m.Get("key1"), "val1")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
gtest.Assert(m.Remove("key2"), "val2")
gtest.Assert(m.Contains("key2"), false)
gtest.AssertIN("key3", m.Keys())
gtest.AssertIN("key1", m.Keys())
gtest.AssertIN("val3", m.Values())
gtest.AssertIN("val1", m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
m3 := gmap.NewFromArray([]interface{}{1, "key1"}, []interface{}{1, "val1"})
gtest.Assert(m3.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_Map_Set_Fun(t *testing.T) {
m := gmap.New()
m.GetOrSetFunc("fun", getValue)
m.GetOrSetFuncLock("funlock", getValue)
gtest.Assert(m.Get("funlock"), 3)
gtest.Assert(m.Get("fun"), 3)
m.GetOrSetFunc("fun", getValue)
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
}
func Test_Map_Batch(t *testing.T) {
m := gmap.New()
m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Iterator(callBack)
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.BatchRemove([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_Map_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone.Remove("key1")
//修改clone map,原 map 不影响
gtest.AssertIN("key1", m.Keys())
}
func Test_Map_Basic_Merge(t *testing.T) {
m1 := gmap.New()
m2 := gmap.New()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
}

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,85 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func StringBoolCallBack( string, bool) bool {
return true
}
func Test_StringBoolMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringBoolMap()
m.Set("a", true)
gtest.Assert(m.Get("a"), true)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("b", false), false)
gtest.Assert(m.SetIfNotExist("b", false), false)
gtest.Assert(m.SetIfNotExist("c", false), true)
gtest.Assert(m.Remove("b"), false)
gtest.Assert(m.Contains("b"), false)
gtest.AssertIN("c", m.Keys())
gtest.AssertIN("a", m.Keys())
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false})
m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false})
gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false})
})
}
func Test_StringBoolMap_Set_Fun(t *testing.T) {
m := gmap.NewStringBoolMap()
m.GetOrSetFunc("a", getBool)
m.GetOrSetFuncLock("b", getBool)
gtest.Assert(m.Get("a"), true)
gtest.Assert(m.Get("b"), true)
gtest.Assert(m.SetIfNotExistFunc("a", getBool), false)
gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false)
}
func Test_StringBoolMap_Batch(t *testing.T) {
m := gmap.NewStringBoolMap()
m.BatchSet(map[string]bool{"a": true, "b": false, "c": true})
m.Iterator(StringBoolCallBack)
gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]bool{"c": true})
}
func Test_StringBoolMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
m_clone := m.Clone()
m.Remove("a")
//修改原 map,clone 后的 map 不影响
gtest.AssertIN("a", m_clone.Keys())
m_clone.Remove("b")
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringBoolMap_Merge(t *testing.T) {
m1 := gmap.NewStringBoolMap()
m2 := gmap.NewStringBoolMap()
m1.Set("a", true)
m2.Set("b", false)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false})
}

View File

@ -0,0 +1,91 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func stringIntCallBack(string, int) bool {
return true
}
func Test_StringIntMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringIntMap()
m.Set("a", 1)
gtest.Assert(m.Get("a"), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("b", 2), 2)
gtest.Assert(m.SetIfNotExist("b", 2), false)
gtest.Assert(m.SetIfNotExist("c", 3), true)
gtest.Assert(m.Remove("b"), 2)
gtest.Assert(m.Contains("b"), false)
gtest.AssertIN("c", m.Keys())
gtest.AssertIN("a", m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
m_f := gmap.NewStringIntMap()
m_f.Set("1", 2)
m_f.Flip()
gtest.Assert(m_f.Map(), map[string]int{"2": 1})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2})
gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
m3 := gmap.NewStringIntMapFromArray([]string{"a", "b"}, []int{1, 2})
gtest.Assert(m3.Map(), map[string]int{"a": 1, "b": 2})
})
}
func Test_StringIntMap_Set_Fun(t *testing.T) {
m := gmap.NewStringIntMap()
m.GetOrSetFunc("a", getInt)
m.GetOrSetFuncLock("b", getInt)
gtest.Assert(m.Get("a"), 123)
gtest.Assert(m.Get("b"), 123)
gtest.Assert(m.SetIfNotExistFunc("a", getInt), false)
gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
}
func Test_StringIntMap_Batch(t *testing.T) {
m := gmap.NewStringIntMap()
m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3})
m.Iterator(stringIntCallBack)
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]int{"c": 3})
}
func Test_StringIntMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
m_clone := m.Clone()
m.Remove("a")
//修改原 map,clone 后的 map 不影响
gtest.AssertIN("a", m_clone.Keys())
m_clone.Remove("b")
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringIntMap_Merge(t *testing.T) {
m1 := gmap.NewStringIntMap()
m2 := gmap.NewStringIntMap()
m1.Set("a", 1)
m2.Set("b", 2)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
}

View File

@ -0,0 +1,89 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func stringInterfaceCallBack(string, interface{}) bool {
return true
}
func Test_StringInterfaceMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringInterfaceMap()
m.Set("a", 1)
gtest.Assert(m.Get("a"), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("b", "2"), "2")
gtest.Assert(m.SetIfNotExist("b", "2"), false)
gtest.Assert(m.SetIfNotExist("c", 3), true)
gtest.Assert(m.Remove("b"), "2")
gtest.Assert(m.Contains("b"), false)
gtest.AssertIN("c", m.Keys())
gtest.AssertIN("a", m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
m3 := gmap.NewStringInterfaceMapFromArray([]string{"a", "b"}, []interface{}{1, "2"})
gtest.Assert(m3.Map(), map[string]interface{}{"a": 1, "b": "2"})
})
}
func Test_StringInterfaceMap_Set_Fun(t *testing.T) {
m := gmap.NewStringInterfaceMap()
m.GetOrSetFunc("a", getInterface)
m.GetOrSetFuncLock("b", getInterface)
gtest.Assert(m.Get("a"), 123)
gtest.Assert(m.Get("b"), 123)
gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false)
gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false)
}
func Test_StringInterfaceMap_Batch(t *testing.T) {
m := gmap.NewStringInterfaceMap()
m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3})
m.Iterator(stringInterfaceCallBack)
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
}
func Test_StringInterfaceMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
m_clone := m.Clone()
m.Remove("a")
//修改原 map,clone 后的 map 不影响
gtest.AssertIN("a", m_clone.Keys())
m_clone.Remove("b")
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringInterfaceMap_Merge(t *testing.T) {
m1 := gmap.NewStringInterfaceMap()
m2 := gmap.NewStringInterfaceMap()
m1.Set("a", 1)
m2.Set("b", "2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
}

View File

@ -0,0 +1,90 @@
package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func stringStringCallBack(string, string) bool {
return true
}
func Test_StringStringMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewStringStringMap()
m.Set("a", "a")
gtest.Assert(m.Get("a"), "a")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("b", "b"), "b")
gtest.Assert(m.SetIfNotExist("b", "b"), false)
gtest.Assert(m.SetIfNotExist("c", "c"), true)
gtest.Assert(m.Remove("b"), "b")
gtest.Assert(m.Contains("b"), false)
gtest.AssertIN("c", m.Keys())
gtest.AssertIN("a", m.Keys())
gtest.AssertIN("a", m.Values())
gtest.AssertIN("c", m.Values())
m.Flip()
gtest.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"})
gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
m3 := gmap.NewStringStringMapFromArray([]string{"a", "b"}, []string{"a", "b"})
gtest.Assert(m3.Map(), map[string]string{"a": "a", "b": "b"})
})
}
func Test_StringStringMap_Set_Fun(t *testing.T) {
m := gmap.NewStringStringMap()
m.GetOrSetFunc("a", getString)
m.GetOrSetFuncLock("b", getString)
gtest.Assert(m.Get("a"), "z")
gtest.Assert(m.Get("b"), "z")
gtest.Assert(m.SetIfNotExistFunc("a", getString), false)
gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false)
}
func Test_StringStringMap_Batch(t *testing.T) {
m := gmap.NewStringStringMap()
m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"})
m.Iterator(stringStringCallBack)
gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"})
m.BatchRemove([]string{"a", "b"})
gtest.Assert(m.Map(), map[string]string{"c": "c"})
}
func Test_StringStringMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
m_clone := m.Clone()
m.Remove("a")
//修改原 map,clone 后的 map 不影响
gtest.AssertIN("a", m_clone.Keys())
m_clone.Remove("b")
//修改clone map,原 map 不影响
gtest.AssertIN("b", m.Keys())
}
func Test_StringStringMap_Merge(t *testing.T) {
m1 := gmap.NewStringStringMap()
m2 := gmap.NewStringStringMap()
m1.Set("a", "a")
m2.Set("b", "b")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
}

View File

@ -0,0 +1,229 @@
package gring_test
import (
"container/ring"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
type Student struct {
position int
name string
upgrade bool
}
func TestRing_Val(t *testing.T) {
gtest.Case(t, func() {
//定义cap 为3的ring类型数据
r := gring.New(3, true)
//分别给3个元素初始化赋值
r.Put(&Student{1,"jimmy", true})
r.Put(&Student{2,"tom", true})
r.Put(&Student{3,"alon", false})
//元素取值并判断和预设值是否相等
gtest.Assert(r.Val().(*Student).name,"jimmy")
//从当前位置往后移两个元素
r.Move(2)
gtest.Assert(r.Val().(*Student).name,"alon")
//更新元素值
//测试 value == nil
r.Set(nil)
gtest.Assert(r.Val(),nil)
//测试value != nil
r.Set(&Student{3, "jack", true})
})
}
func TestRing_CapLen(t *testing.T) {
gtest.Case(t, func() {
r := gring.New(10)
r.Put("goframe")
//cap长度 10
gtest.Assert(r.Cap(), 10)
//已有数据项 1
gtest.Assert(r.Len(), 1)
})
}
func TestRing_Position(t *testing.T) {
gtest.Case(t, func() {
r := gring.New(2)
r.Put(1)
r.Put(2)
//往后移动1个元素
r.Next()
gtest.Assert(r.Val(),2)
//往前移动1个元素
r.Prev()
gtest.Assert(r.Val(),1)
})
}
func TestRing_Link(t *testing.T) {
gtest.Case(t, func() {
r := gring.New(3)
r.Put(1)
r.Put(2)
r.Put(3)
s := gring.New(2)
s.Put("a")
s.Put("b")
rs := r.Link(s)
gtest.Assert(rs.Move(2).Val(), "b")
})
}
func TestRing_Unlink(t *testing.T) {
gtest.Case(t, func() {
r := gring.New(5)
for i := 0; i< 5; i++ {
r.Put(i+1)
}
// 1 2 3 4
// 删除当前位置往后的2个数据返回被删除的数据
// 重新计算s len
s := r.Unlink(2) // 2 3
gtest.Assert(s.Val(), 2)
gtest.Assert(s.Len(), 1)
})
}
func TestRing_Slice(t *testing.T) {
gtest.Case(t, func() {
ringLen := 5
r := gring.New(ringLen)
for i := 0; i< ringLen; i++ {
r.Put(i+1)
}
r.Move(2) // 3
array := r.SliceNext() // [3 4 5 1 2]
gtest.Assert(array[0], 3)
gtest.Assert(len(array), 5)
//判断array是否等于[3 4 5 1 2]
ra := []int{3,4,5,1,2}
gtest.Assert(ra, array)
//第3个元素设为nil
r.Set(nil)
array2 := r.SliceNext() //[4 5 1 2]
//返回当前位置往后不为空的元素数组长度为4
gtest.Assert(array2, g.Slice{4,5,1,2})
array3 := r.SlicePrev() //[2 1 5 4]
gtest.Assert(array3, g.Slice{2,1,5,4})
s := gring.New(ringLen)
for i := 0; i< ringLen; i++ {
s.Put(i+1)
}
array4 := s.SlicePrev() // []
gtest.Assert(array4, g.Slice{1,5,4,3,2})
})
}
func TestRing_RLockIterator(t *testing.T) {
gtest.Case(t, func() {
ringLen := 5
r := gring.New(ringLen)
//ring不存在有值元素
r.RLockIteratorNext(func(v interface{}) bool {
gtest.Assert(v, nil)
return false
})
r.RLockIteratorNext(func(v interface{}) bool {
gtest.Assert(v, nil)
return true
})
r.RLockIteratorPrev(func(v interface{}) bool {
gtest.Assert(v, nil)
return true
})
for i := 0; i< ringLen; i++ {
r.Put(i+1)
}
//回调函数返回true,RLockIteratorNext遍历5次,期望值分别是1、2、3、4、5
i := 0
r.RLockIteratorNext(func(v interface{}) bool {
gtest.Assert(v, i+1)
i++;
return true
})
//RLockIteratorPrev遍历1次返回 false,退出遍历
r.RLockIteratorPrev(func(v interface{}) bool {
gtest.Assert(v, 1)
return false
})
})
}
func TestRing_LockIterator(t *testing.T) {
gtest.Case(t, func() {
ringLen := 5
r := gring.New(ringLen)
//不存在有值元素
r.LockIteratorNext(func(item *ring.Ring) bool {
gtest.Assert(item.Value, nil)
return false
})
r.LockIteratorNext(func(item *ring.Ring) bool {
gtest.Assert(item.Value, nil)
return false
})
r.LockIteratorNext(func(item *ring.Ring) bool {
gtest.Assert(item.Value, nil)
return true
})
r.LockIteratorPrev(func(item *ring.Ring) bool {
gtest.Assert(item.Value, nil)
return false
})
r.LockIteratorPrev(func(item *ring.Ring) bool {
gtest.Assert(item.Value, nil)
return true
})
//ring初始化元素值
for i := 0; i< ringLen; i++ {
r.Put(i+1)
}
//往后遍历组成数据 [1,2,3,4,5]
array1 := g.Slice{1,2,3,4,5}
ii := 0
r.LockIteratorNext(func(item *ring.Ring) bool {
//校验每一次遍历取值是否是期望值
gtest.Assert(item.Value, array1[ii])
ii++;
return true
})
//往后取3个元素组成数组
//获得 [1,5,4]
i := 0
a := g.Slice{1,5,4}
r.LockIteratorPrev(func(item *ring.Ring) bool {
if i > 2 {
return false
}
gtest.Assert(item.Value, a[i])
i++;
return true
})
})
}

View File

@ -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,62 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*"
package gaes_test
import (
"testing"
"github.com/gogf/gf/g/crypto/gaes"
"github.com/gogf/gf/g/test/gtest"
)
var (
content = []byte("pibigstar")
// iv 长度必须等于blockSize只能为16
iv = []byte("Hello My GoFrame")
key_16 = []byte("1234567891234567")
key_24 = []byte("123456789123456789123456")
key_32 = []byte("12345678912345678912345678912345")
)
func TestEncrypt(t *testing.T) {
gtest.Case(t, func() {
_, err := gaes.Encrypt(content, key_16)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_24)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_32)
gtest.Assert(err, nil)
_, err = gaes.Encrypt(content, key_16, iv)
gtest.Assert(err, nil)
})
}
func TestDecrypt(t *testing.T) {
gtest.Case(t, func() {
encrypt, err := gaes.Encrypt(content, key_16)
decrypt, err := gaes.Decrypt(encrypt, key_16)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
encrypt, err = gaes.Encrypt(content, key_24)
decrypt, err = gaes.Decrypt(encrypt, key_24)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
encrypt, err = gaes.Encrypt(content, key_32)
decrypt, err = gaes.Decrypt(encrypt, key_32)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
encrypt, err = gaes.Encrypt(content, key_32, iv)
decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
gtest.Assert(err, nil)
gtest.Assert(string(decrypt), string(content))
})
}

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

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

View File

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

View File

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

@ -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
}
// 获取分组路由对象

View File

@ -6,33 +6,28 @@
//
// 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"
)
// 命令行参数列表
// Console values.
type gCmdValue struct {
values []string
}
// 命令行选项列表
// Console options.
type gCmdOption struct {
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)
@ -46,67 +41,34 @@ func init() {
}
}
// 返回所有的命令行参数values
func (c *gCmdValue) GetAll() []string {
return c.values
}
// 返回所有的命令行参数options
func (c *gCmdOption) GetAll() map[string]string {
return c.options
}
// 获得一条指定索引位置的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 {
// BindHandle registers callback function <f> with <cmd>.
func BindHandle (cmd string, f func()) {
if _, ok := cmdFuncMap[cmd]; ok {
return errors.New("duplicated handle for command:" + cmd)
glog.Fatal("duplicated handle for command:" + cmd)
} else {
cmdFuncMap[cmd] = f
return nil
}
}
// 执行命令对应的函数
func RunHandle (cmd string) error {
// RunHandle executes the callback function registered by <cmd>.
func RunHandle (cmd string) {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
glog.Fatal("no handle found for command:" + cmd)
}
}
// 自动识别命令参数并执行命令参数对应的函数
func AutoRun () error {
// 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()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
glog.Fatal("no handle found for command:" + cmd)
}
} else {
return errors.New("no command found")
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

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

@ -29,7 +29,6 @@ import (
"sync"
)
// 视图对象
type View struct {
mu sync.RWMutex
paths *garray.StringArray // 模板查找目录(绝对路径)
@ -38,29 +37,32 @@ type View struct {
delimiters []string // 模板变量分隔符号
}
// 模板变量
// Template params type.
type Params = map[string]interface{}
// 函数映射表
// Customized template function map type.
type FuncMap = map[string]interface{}
// 默认的视图对象
// Default view object.
var defaultViewObj *View
// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。
// checkAndInitDefaultView checks and initializes the default view object.
// The default view object will be initialized just once.
func checkAndInitDefaultView() {
if defaultViewObj == nil {
defaultViewObj = New(gfile.Pwd())
}
}
// 直接解析模板内容,返回解析后的内容
// ParseContent parses the template content directly using the default view object
// and returns the parsed content.
func ParseContent(content string, params Params) ([]byte, error) {
checkAndInitDefaultView()
return defaultViewObj.ParseContent(content, params)
}
// 生成一个视图对象
// New returns a new view object.
// The parameter <path> specifies the template directory path to load template files.
func New(path...string) *View {
view := &View {
paths : garray.NewStringArray(),
@ -92,11 +94,11 @@ func New(path...string) *View {
}
}
view.SetDelimiters("{{", "}}")
// 内置变量
// default build-in variables.
view.data["GF"] = map[string]interface{} {
"version" : gf.VERSION,
}
// 内置方法
// default build-in functions.
view.BindFunc("text", view.funcText)
view.BindFunc("html", view.funcHtmlEncode)
view.BindFunc("htmlencode", view.funcHtmlEncode)
@ -117,12 +119,13 @@ func New(path...string) *View {
return view
}
// 设置模板目录绝对路径
// SetPath sets the template directory path for template file search.
// The param <path> can be absolute or relative path, but absolute path is suggested.
func (view *View) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
// Absolute path.
realPath := gfile.RealPath(path)
if realPath == "" {
// 判断相对路径
// Relative path.
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
@ -132,7 +135,7 @@ func (view *View) SetPath(path string) error {
}
})
}
// 目录不存在错误处理
// Path not exist.
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
@ -149,13 +152,13 @@ func (view *View) SetPath(path string) error {
glog.Error(err)
return err
}
// 路径必须为目录类型
// Should be a directory.
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
// Repeated path check.
if view.paths.Search(realPath) != -1 {
return nil
}
@ -165,12 +168,12 @@ func (view *View) SetPath(path string) error {
return nil
}
// 添加模板目录搜索路径
// AddPath adds a absolute or relative path to the search paths.
func (view *View) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
// Absolute path.
realPath := gfile.RealPath(path)
if realPath == "" {
// 判断相对路径
// Relative path.
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
@ -180,7 +183,7 @@ func (view *View) AddPath(path string) error {
}
})
}
// 目录不存在错误处理
// Path not exist.
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
@ -197,13 +200,13 @@ func (view *View) AddPath(path string) error {
glog.Error(err)
return err
}
// 路径必须为目录类型
// realPath should be type of folder.
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
// Repeated path check.
if view.paths.Search(realPath) != -1 {
return nil
}
@ -212,7 +215,8 @@ func (view *View) AddPath(path string) error {
return nil
}
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
// Assign binds multiple template variables to current view object.
// Each goroutine will take effect after the call, so it is concurrent-safe.
func (view *View) Assigns(data Params) {
view.mu.Lock()
for k, v := range data {
@ -221,15 +225,18 @@ func (view *View) Assigns(data Params) {
view.mu.Unlock()
}
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
// Assign binds a template variable to current view object.
// Each goroutine will take effect after the call, so it is concurrent-safe.
func (view *View) Assign(key string, value interface{}) {
view.mu.Lock()
view.data[key] = value
view.mu.Unlock()
}
// 解析模板,返回解析后的内容
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
// ParseContent parses given template file <file>
// with given template parameters <params> and function map <funcMap>
// and returns the parsed content in []byte.
func (view *View) Parse(file string, params Params, funcMap...map[string]interface{}) ([]byte, error) {
path := ""
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
@ -254,19 +261,19 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa
return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file))
}
content := gfcache.GetContents(path)
// 执行模板解析互斥锁主要是用于funcmap
view.mu.RLock()
defer view.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
if len(funcmap) > 0 {
tplobj = tplobj.Funcs(funcmap[0])
tplObj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
if len(funcMap) > 0 {
tplObj = tplObj.Funcs(funcMap[0])
}
if tpl, err := tplobj.Parse(content); err != nil {
if tpl, err := tplObj.Parse(content); err != nil {
return nil, err
} else {
// 注意模板变量赋值不能改变已有的params或者view.data的值因为这两个变量都是指针
// 因此在必要条件下需要合并两个map的值到一个新的map
// Note that the template variable assignment cannot change the value
// of the existing <params> or view.data because both variables are pointers.
// It's need to merge the values of the two maps into a new map.
vars := (map[string]interface{})(nil)
if len(view.data) > 0 {
if len(params) > 0 {
@ -290,21 +297,24 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa
return buffer.Bytes(), nil
}
// 直接解析模板内容,返回解析后的内容
func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
// ParseContent parses given template content <content>
// with given template parameters <params> and function map <funcMap>
// and returns the parsed content in []byte.
func (view *View) ParseContent(content string, params Params, funcMap...map[string]interface{}) ([]byte, error) {
view.mu.RLock()
defer view.mu.RUnlock()
name := gconv.String(ghash.BKDRHash64([]byte(content)))
buffer := bytes.NewBuffer(nil)
tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
if len(funcmap) > 0 {
tplobj = tplobj.Funcs(funcmap[0])
tplObj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
if len(funcMap) > 0 {
tplObj = tplObj.Funcs(funcMap[0])
}
if tpl, err := tplobj.Parse(content); err != nil {
if tpl, err := tplObj.Parse(content); err != nil {
return nil, err
} else {
// 注意模板变量赋值不能改变已有的params或者view.data的值因为这两个变量都是指针
// 因此在必要条件下需要合并两个map的值到一个新的map
// Note that the template variable assignment cannot change the value
// of the existing <params> or view.data because both variables are pointers.
// It's need to merge the values of the two maps into a new map.
vars := (map[string]interface{})(nil)
if len(view.data) > 0 {
if len(params) > 0 {
@ -328,20 +338,33 @@ func (view *View) ParseContent(content string, params Params, funcmap...map[stri
return buffer.Bytes(), nil
}
// 设置模板变量解析分隔符号
// SetDelimiters sets customized delimiters for template parsing.
func (view *View) SetDelimiters(left, right string) {
view.delimiters[0] = left
view.delimiters[1] = right
}
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
// BindFunc registers customized template function named <name>
// with given function <function> to current view object.
// The <name> is the function name which can be called in template content.
func (view *View) BindFunc(name string, function interface{}) {
view.mu.Lock()
view.funcmap[name] = function
view.mu.Unlock()
}
// 模板内置方法include
// BindFuncMap registers customized template functions by map to current view object.
// The key of map is the template function name
// and the value of map is the address of customized function.
func (view *View) BindFuncMap(funcMap FuncMap) {
view.mu.Lock()
for k, v := range funcMap {
view.funcmap[k] = v
}
view.mu.Unlock()
}
// Build-in template function: include
func (view *View) funcInclude(file string, data...map[string]interface{}) string {
var m map[string]interface{} = nil
if len(data) > 0 {
@ -354,27 +377,27 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) string
return string(content)
}
// 模板内置方法:text
// Build-in template function: text
func (view *View) funcText(html interface{}) string {
return ghtml.StripTags(gconv.String(html))
}
// 模板内置方法:html
// Build-in template function: html
func (view *View) funcHtmlEncode(html interface{}) string {
return ghtml.Entities(gconv.String(html))
}
// 模板内置方法:htmldecode
// Build-in template function: htmldecode
func (view *View) funcHtmlDecode(html interface{}) string {
return ghtml.EntitiesDecode(gconv.String(html))
}
// 模板内置方法:url
// Build-in template function: url
func (view *View) funcUrlEncode(url interface{}) string {
return gurl.Encode(gconv.String(url))
}
// 模板内置方法:urldecode
// Build-in template function: urldecode
func (view *View) funcUrlDecode(url interface{}) string {
if content, err := gurl.Decode(gconv.String(url)); err == nil {
return content
@ -383,7 +406,7 @@ func (view *View) funcUrlDecode(url interface{}) string {
}
}
// 模板内置方法:date
// Build-in template function: date
func (view *View) funcDate(format string, timestamp...interface{}) string {
t := int64(0)
if len(timestamp) > 0 {
@ -395,42 +418,42 @@ func (view *View) funcDate(format string, timestamp...interface{}) string {
return gtime.NewFromTimeStamp(t).Format(format)
}
// 模板内置方法:compare
// Build-in template function: compare
func (view *View) funcCompare(value1, value2 interface{}) int {
return strings.Compare(gconv.String(value1), gconv.String(value2))
}
// 模板内置方法:substr
// Build-in template function: substr
func (view *View) funcSubStr(start, end int, str interface{}) string {
return gstr.SubStr(gconv.String(str), start, end)
}
// 模板内置方法:strlimit
// Build-in template function: strlimit
func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
return gstr.StrLimit(gconv.String(str), length, suffix)
}
// 模板内置方法:highlight
// Build-in template function: highlight
func (view *View) funcHighlight(key string, color string, str interface{}) string {
return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`<span style="color:%s;">%s</span>`, color, key))
}
// 模板内置方法:hidestr
// Build-in template function: hidestr
func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
return gstr.HideStr(gconv.String(str), percent, hide)
}
// 模板内置方法:toupper
// Build-in template function: toupper
func (view *View) funcToUpper(str interface{}) string {
return gstr.ToUpper(gconv.String(str))
}
// 模板内置方法:toupper
// Build-in template function: toupper
func (view *View) funcToLower(str interface{}) string {
return gstr.ToLower(gconv.String(str))
}
// 模板内置方法:nl2br
// Build-in template function: nl2br
func (view *View) funcNl2Br(str interface{}) string {
return gstr.Nl2Br(gconv.String(str))
}

View File

@ -32,12 +32,9 @@ func Case(t *testing.T, f func()) {
// Assert checks <value> and <expect> EQUAL.
func Assert(value, expect interface{}) {
rvValue := reflect.ValueOf(value)
rvExpect := reflect.ValueOf(expect)
if rvValue.Kind() == reflect.Ptr {
if rvValue.IsNil() {
value = nil
}
if isNil(value) {
value = nil
}
if rvExpect.Kind() == reflect.Map {
if err := compareMap(value, expect); err != nil {
@ -53,13 +50,10 @@ func Assert(value, expect interface{}) {
// AssertEQ checks <value> and <expect> EQUAL, including their TYPES.
func AssertEQ(value, expect interface{}) {
// Value assert.
rvValue := reflect.ValueOf(value)
rvExpect := reflect.ValueOf(expect)
if rvValue.Kind() == reflect.Ptr {
if rvValue.IsNil() {
value = nil
}
}
if isNil(value) {
value = nil
}
if rvExpect.Kind() == reflect.Map {
if err := compareMap(value, expect); err != nil {
panic(err)
@ -79,13 +73,10 @@ func AssertEQ(value, expect interface{}) {
// AssertNE checks <value> and <expect> NOT EQUAL.
func AssertNE(value, expect interface{}) {
rvValue := reflect.ValueOf(value)
rvExpect := reflect.ValueOf(expect)
if rvValue.Kind() == reflect.Ptr {
if rvValue.IsNil() {
value = nil
}
}
if isNil(value) {
value = nil
}
if rvExpect.Kind() == reflect.Map {
if err := compareMap(value, expect); err == nil {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v != %v`, value, expect))
@ -259,11 +250,9 @@ func Fatal(message...interface{}) {
func compareMap(value, expect interface{}) error {
rvValue := reflect.ValueOf(value)
rvExpect := reflect.ValueOf(expect)
if rvValue.Kind() == reflect.Ptr {
if rvValue.IsNil() {
value = nil
}
}
if isNil(value) {
value = nil
}
if rvExpect.Kind() == reflect.Map {
if rvValue.Kind() == reflect.Map {
if rvExpect.Len() == rvValue.Len() {
@ -336,4 +325,15 @@ func getBacktrace(skip...int) string {
}
}
return backtrace
}
// isNil checks whether <value> is nil.
func isNil(value interface{}) bool {
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr, reflect.Func:
return rv.IsNil()
default:
return value == nil
}
}

View File

@ -594,4 +594,21 @@ func QuoteMeta(str string) string {
buf.WriteRune(char)
}
return buf.String()
}
// SearchArray searches string <s> in string slice <a> case-sensitively,
// returns its index in <a>.
// If <s> is not found in <a>, it returns -1.
func SearchArray (a []string, s string) int {
for i, v := range a {
if s == v {
return i
}
}
return -1
}
// InArray checks whether string <s> in slice <a>.
func InArray (a []string, s string) bool {
return SearchArray(a, s) != -1
}

View File

@ -383,3 +383,22 @@ func Test_ContainsAny(t *testing.T) {
})
}
func Test_SearchArray(t *testing.T) {
gtest.Case(t, func() {
a := g.SliceStr{"a", "b", "c"}
gtest.AssertEQ(gstr.SearchArray(a, "a"), 0)
gtest.AssertEQ(gstr.SearchArray(a, "b"), 1)
gtest.AssertEQ(gstr.SearchArray(a, "c"), 2)
gtest.AssertEQ(gstr.SearchArray(a, "d"), -1)
})
}
func Test_InArray(t *testing.T) {
gtest.Case(t, func() {
a := g.SliceStr{"a", "b", "c"}
gtest.AssertEQ(gstr.InArray(a, "a"), true)
gtest.AssertEQ(gstr.InArray(a, "b"), true)
gtest.AssertEQ(gstr.InArray(a, "c"), true)
gtest.AssertEQ(gstr.InArray(a, "d"), false)
})
}

View File

@ -7,8 +7,9 @@
package grand
import (
"crypto/rand"
"encoding/binary"
"crypto/rand"
"encoding/binary"
"os"
)
const (
@ -27,6 +28,7 @@ func init() {
for {
if n, err := rand.Read(buffer); err != nil {
panic(err)
os.Exit(1)
} else {
// 使用缓冲区数据进行一次完整的随机数生成
for i := 0; i < n - 4; {
@ -34,10 +36,18 @@ func init() {
i ++
}
// 充分利用缓冲区数据,随机索引递增
step = int(buffer[0])%10
for i := 0; i < n; i++ {
step = int(buffer[0])%10
if step != 0 {
break
}
}
if step == 0 {
step = 2
}
for i := 0; i < n - 4; {
bufferChan <- binary.BigEndian.Uint32(buffer[i : i + 4])
i += step
bufferChan <- binary.BigEndian.Uint32(buffer[i : i + 4])
i += step
}
}
}

View File

@ -0,0 +1,17 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/gtimer"
"time"
)
func main() {
for i := 0; i < 100000; i++ {
gtimer.Add(time.Second, func() {
})
}
fmt.Println("start")
time.Sleep(48*time.Hour)
}

View File

@ -1,21 +0,0 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gview"
)
// 用于测试的内置函数
func funcTest() string {
return "test"
}
func main() {
view := g.View()
b, err := view.Parse("index.html", nil, gview.FuncMap{
"test": funcTest,
})
fmt.Println(err)
fmt.Println(string(b))
}

View File

@ -0,0 +1,23 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gview"
)
// 用于测试的内置函数
func funcTest() string {
return "test content"
}
func main() {
// 解析模板的时候传递模板函数映射Map仅会在当前模板解析生效
parsed, err := g.View().ParseContent(`call build-in function test: {{test}}`, nil, gview.FuncMap {
"test": funcTest,
})
if err != nil {
panic(err)
}
fmt.Println(string(parsed))
}

View File

@ -0,0 +1,31 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
)
// 用于测试的带参数的内置函数
func funcHello(name string) string {
return fmt.Sprintf(`Hello %s`, name)
}
func main() {
// 绑定全局的模板函数
g.View().BindFunc("hello", funcHello)
// 普通方式传参
parsed1, err := g.View().ParseContent(`{{hello "GoFrame"}}`, nil)
if err != nil {
panic(err)
}
fmt.Println(string(parsed1))
// 通过管道传参
parsed2, err := g.View().ParseContent(`{{"GoFrame" | hello}}`, nil)
if err != nil {
panic(err)
}
fmt.Println(string(parsed2))
}

View File

@ -2,9 +2,21 @@ package main
import (
"fmt"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/test/gtest"
)
func main() {
fmt.Println(gtime.Now().Format(`Y-m-j G:i:su`))
var add float32
add = 3.1415926
myF32 := gtype.NewFloat32(add)
myAdd := myF32.Add(float32(6.2951413))
add += float32(6.2951413)
fmt.Println(myF32.Val())
gtest.AssertEQ(myAdd, add)
}

2
go.mod
View File

@ -1 +1,3 @@
module github.com/gogf/gf
go 1.12

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.6.0"
const VERSION = "v1.6.2"
const AUTHORS = "john<john@goframe.org>"