diff --git a/README.MD b/README.MD
index ff188dd89..afe2d6fc0 100644
--- a/README.MD
+++ b/README.MD
@@ -1,10 +1,10 @@
# GoFrame
-
+
[](https://godoc.org/github.com/gogf/gf)
[](https://travis-ci.org/gogf/gf)
[](https://goreportcard.com/report/github.com/gogf/gf)
-[](https://gfer.me)
+[](https://goframe.org)
[](https://github.com/gogf/gf)
[](https://github.com/gogf/gf)
[](https://github.com/gogf/gf/releases)
@@ -36,7 +36,7 @@ golang version >= 1.9.2
# Architecture
-

+
# Quick Start
@@ -58,7 +58,7 @@ func main() {
}
```
-[View More..](https://gfer.me/start/index)
+[View More..](https://goframe.org/start/index)
# License
@@ -71,7 +71,7 @@ func main() {
-
+
@@ -84,7 +84,7 @@ func main() {
-
+
diff --git a/README_ZH.MD b/README_ZH.MD
index fb8d57425..be823d7ab 100644
--- a/README_ZH.MD
+++ b/README_ZH.MD
@@ -1,10 +1,10 @@
# GoFrame
-
+
[](https://godoc.org/github.com/gogf/gf)
[](https://travis-ci.org/gogf/gf)
[](https://goreportcard.com/report/github.com/gogf/gf)
-[](https://gfer.me)
+[](https://goframe.org)
[](https://github.com/gogf/gf)
[](https://github.com/gogf/gf)
[](https://github.com/gogf/gf/releases)
@@ -14,10 +14,8 @@
[](https://www.codetriage.com/gogf/gf)
-->
-`GF(Go Frame)`是一款模块化、松耦合、轻量级、高性能的Go应用开发框架。支持热重启、热更新、多域名、多端口、多服务、HTTP/HTTPS、动态路由等特性
-,并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎、数据校验、分页管理、数据库ORM等等等等,
-并且提供了数十个内置核心开发模块集,如:缓存、日志、时间、命令行、二进制、文件锁、内存锁、对象池、连接池、数据编码、进程管理、进程通信、文件监控、定时任务、TCP/UDP组件、
-并发安全容器等等等等等等。
+`GF(Go Frame)`是一款模块化、松耦合、生产级、实战化的Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
+并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
# 特点
@@ -45,7 +43,7 @@ golang版本 >= 1.9.2
# 架构
-

+
@@ -74,7 +72,7 @@ func main() {
}
```
-[更多..](https://gfer.me/start/index)
+[更多..](https://goframe.org/start/index)
# 协议
@@ -86,14 +84,14 @@ func main() {
-
+
-
+
diff --git a/RELEASE.MD b/RELEASE.MD
index 06c0b61ef..7c056eb3a 100644
--- a/RELEASE.MD
+++ b/RELEASE.MD
@@ -31,20 +31,20 @@
# `v1.3.8` (2018-12-26)
## 新特性
-1. 对`gform`完成重构,以提高扩展性,并修复部分细节问题、完善单元测试用例([https://gfer.me/database/orm/index](https://gfer.me/database/orm/index));
-1. `WebServer`路由注册新增分组路由特性([https://gfer.me/net/ghttp/group](https://gfer.me/net/ghttp/group));
-1. `WebServer`新增`Rewrite`路由重写特性([https://gfer.me/net/ghttp/static](https://gfer.me/net/ghttp/static));
+1. 对`gform`完成重构,以提高扩展性,并修复部分细节问题、完善单元测试用例([https://goframe.org/database/orm/index](https://goframe.org/database/orm/index));
+1. `WebServer`路由注册新增分组路由特性([https://goframe.org/net/ghttp/group](https://goframe.org/net/ghttp/group));
+1. `WebServer`新增`Rewrite`路由重写特性([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
1. 增加框架运行时对开发环境的自动识别;
1. 增加了`Travis CI`自动化构建/测试;
## 新功能
-1. 改进`WebServer`静态文件服务功能,增加`SetStaticPath`/`AddStaticPath`方法([https://gfer.me/net/ghttp/static](https://gfer.me/net/ghttp/static));
-1. `gform`新增`Filter`链式操作方法,用于过滤参数中的非表字段键值对([https://gfer.me/database/orm/linkop](https://gfer.me/database/orm/linkop));
+1. 改进`WebServer`静态文件服务功能,增加`SetStaticPath`/`AddStaticPath`方法([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
+1. `gform`新增`Filter`链式操作方法,用于过滤参数中的非表字段键值对([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
1. `gcache`新增`Data`方法,用以获取所有的缓存数据项;
1. `gredis`增加`GetConn`方法获取原生redis连接对象;
## 功能改进
-1. 改进`gform`的`Where`方法,支持`slice`类型的参数,并更方便地支持`in`操作查询([https://gfer.me/database/orm/linkop](https://gfer.me/database/orm/linkop));
+1. 改进`gform`的`Where`方法,支持`slice`类型的参数,并更方便地支持`in`操作查询([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
1. 改进`gproc`进程间通信数据结构,将`pid`字段从`16bit`扩展为`24bit`;
1. 改进`gconv`/`gmap`/`garray`,增加若干操作方法;
1. 改进`gview`模板引擎中的`date`内置函数,当给定的时间戳为空时打印当前的系统时间;
@@ -67,20 +67,20 @@
# `v1.2.11` (2018-11-26)
## 新特性
-1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://gfer.me/database/orm/database](https://gfer.me/database/orm/database));
-1. 完成`gvalid`模块校验结果的顺序特性([https://gfer.me/util/gvalid/checkmap](https://gfer.me/util/gvalid/checkmap));
-1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://gfer.me/net/ghttp/service/object](https://gfer.me/net/ghttp/service/object));
-1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://gfer.me/os/gview/funcs](https://gfer.me/os/gview/funcs));
-1. 模板引擎新增内置变量`Config` ([https://gfer.me/os/gview/vars](https://gfer.me/os/gview/vars));
+1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://goframe.org/database/orm/database](https://goframe.org/database/orm/database));
+1. 完成`gvalid`模块校验结果的顺序特性([https://goframe.org/util/gvalid/checkmap](https://goframe.org/util/gvalid/checkmap));
+1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object));
+1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://goframe.org/os/gview/funcs](https://goframe.org/os/gview/funcs));
+1. 模板引擎新增内置变量`Config` ([https://goframe.org/os/gview/vars](https://goframe.org/os/gview/vars));
1. 改进`gconv.Struct`转换默认规则,支持不区分大小写的键名与属性名称匹配;
-1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://gfer.me/database/orm/config](https://gfer.me/database/orm/config));
-1. `gfsnotify`模块增加对特定回调的取消注册功能([https://gfer.me/os/gfsnotify/index](https://gfer.me/os/gfsnotify/index));
+1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://goframe.org/database/orm/config](https://goframe.org/database/orm/config));
+1. `gfsnotify`模块增加对特定回调的取消注册功能([https://goframe.org/os/gfsnotify/index](https://goframe.org/os/gfsnotify/index));
## 新功能
-1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://gfer.me/net/ghttp/request](https://gfer.me/net/ghttp/request));
-1. 改进`ghttp.Response`,增加`ServeFileDownload`方法,用于WebServer引导客户端下载文件([https://gfer.me/net/ghttp/response](https://gfer.me/net/ghttp/response));
+1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://goframe.org/net/ghttp/request](https://goframe.org/net/ghttp/request));
+1. 改进`ghttp.Response`,增加`ServeFileDownload`方法,用于WebServer引导客户端下载文件([https://goframe.org/net/ghttp/response](https://goframe.org/net/ghttp/response));
1. `gvar`模块新增`gvar.VarRead`只读接口,用于控制对外只暴露数据读取功能;
1. 增加`g.Throw`抛异常方法,`g.TryCatch`异常捕获方法封装;
1. 改进`gcron`模块,增加自定义的Cron管理对象,增加`New/Start/Stop`方法;
@@ -88,9 +88,9 @@
## 功能改进
1. WebServer添加`RouterCacheExpire`配置参数,用于设置路由检索缓存过期时间;
-1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://gfer.me/net/ghttp/service/hook](https://gfer.me/net/ghttp/service/hook));
+1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://goframe.org/net/ghttp/service/hook](https://goframe.org/net/ghttp/service/hook));
1. 当前工作目录为系统临时目录时,`gcfg`/`gview`/`ghttp`模块默认不添加工作目录到搜索路径;
-1. 改进`WebSocket`默认支持跨域请求([https://gfer.me/net/ghttp/websocket](https://gfer.me/net/ghttp/websocket));
+1. 改进`WebSocket`默认支持跨域请求([https://goframe.org/net/ghttp/websocket](https://goframe.org/net/ghttp/websocket));
1. 改进`gtime.Format`支持中文;
1. 改进`gfsnotify`,支持编辑器对文件非执行标准编辑时(RENAME+CHMOD)的热更新问题;
1. 改进`gtype.Set`方法,增加Set原子操作返回旧的变量值;
@@ -99,7 +99,7 @@
1. `gstr`模块增加对中文截取方法;
1. 改进`gtime.StrToTime`对常用时间格式匹配模式,新增`gtime.ParseTimeFromContent`方法;
1. 修改配置管理、模板引擎、调试模式的环境变量名称为大写下划线标准格式;
-1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://gfer.me/util/grand/index](https://gfer.me/util/grand/index));
+1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://goframe.org/util/grand/index](https://goframe.org/util/grand/index));
## 问题修复
1. 修复`gspath`模块在`windows`下搜索失效问题;
diff --git a/TODO.MD b/TODO.MD
index 98de17c6f..c34ae28f8 100644
--- a/TODO.MD
+++ b/TODO.MD
@@ -47,6 +47,11 @@
1. 增加jumplist的数据结构容器;
1. DelayQueue/PriorityQueue;
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag;
+1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
+
+
+
+
# DONE
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
diff --git a/g/container/garray/garray.go b/g/container/garray/garray.go
index 2f87a97b9..7c3f044e5 100644
--- a/g/container/garray/garray.go
+++ b/g/container/garray/garray.go
@@ -6,7 +6,7 @@
// Package garray provides kinds of concurrent-safe(alternative) arrays.
//
-// 并发安全的数组.
+// 并发安全数组.
package garray
func New(size int, cap int, unsafe...bool) *Array {
diff --git a/g/container/garray/garray_func.go b/g/container/garray/garray_func.go
new file mode 100644
index 000000000..9d7a14508
--- /dev/null
+++ b/g/container/garray/garray_func.go
@@ -0,0 +1,8 @@
+// Copyright 2019 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+package garray
+
diff --git a/g/container/garray/garray_int.go b/g/container/garray/garray_int.go
deleted file mode 100644
index a58919018..000000000
--- a/g/container/garray/garray_int.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
-
-package garray
-
-import (
- "gitee.com/johng/gf/g/internal/rwmutex"
-)
-
-type IntArray struct {
- mu *rwmutex.RWMutex // 互斥锁
- cap int // 初始化设置的数组容量
- size int // 初始化设置的数组大小
- array []int // 底层数组
-}
-
-func NewIntArray(size int, cap int, unsafe...bool) *IntArray {
- a := &IntArray{
- mu : rwmutex.New(unsafe...),
- }
- a.size = size
- if cap > 0 {
- a.cap = cap
- a.array = make([]int, size, cap)
- } else {
- a.array = make([]int, size)
- }
- return a
-}
-
-// 获取指定索引的数据项, 调用方注意判断数组边界
-func (a *IntArray) Get(index int) int {
- a.mu.RLock()
- defer a.mu.RUnlock()
- value := a.array[index]
- return value
-}
-
-// 设置指定索引的数据项, 调用方注意判断数组边界
-func (a *IntArray) Set(index int, value int) {
- a.mu.Lock()
- defer a.mu.Unlock()
- a.array[index] = value
-}
-
-// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
-func (a *IntArray) InsertBefore(index int, value int) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]int{}, a.array[index : ]...)
- a.array = append(a.array[0 : index], value)
- a.array = append(a.array, rear...)
-}
-
-// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
-func (a *IntArray) InsertAfter(index int, value int) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]int{}, a.array[index + 1:]...)
- a.array = append(a.array[0 : index + 1], value)
- a.array = append(a.array, rear...)
-
-}
-
-// 删除指定索引的数据项, 调用方注意判断数组边界
-func (a *IntArray) Remove(index int) int {
- a.mu.Lock()
- defer a.mu.Unlock()
- // 边界删除判断,以提高删除效率
- if index == 0 {
- value := a.array[0]
- a.array = a.array[1 : ]
- return value
- } else if index == len(a.array) - 1 {
- value := a.array[index]
- a.array = a.array[: index]
- return value
- }
- // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
- value := a.array[index]
- a.array = append(a.array[ : index], a.array[index + 1 : ]...)
- return value
-}
-
-// 追加数据项
-func (a *IntArray) Append(value...int) {
- a.mu.Lock()
- a.array = append(a.array, value...)
- a.mu.Unlock()
-}
-
-// 数组长度
-func (a *IntArray) Len() int {
- a.mu.RLock()
- length := len(a.array)
- a.mu.RUnlock()
- return length
-}
-
-// 返回原始数据数组
-func (a *IntArray) Slice() []int {
- array := ([]int)(nil)
- if a.mu.IsSafe() {
- a.mu.RLock()
- array = make([]int, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
- } else {
- array = a.array
- }
- return array
-}
-
-// 清空数据数组
-func (a *IntArray) Clear() {
- a.mu.Lock()
- if len(a.array) > 0 {
- if a.cap > 0 {
- a.array = make([]int, a.size, a.cap)
- } else {
- a.array = make([]int, a.size)
- }
- }
- a.mu.Unlock()
-}
-
-// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
-func (a *IntArray) Search(value int) int {
- if len(a.array) == 0 {
- return -1
- }
- a.mu.RLock()
- result := -1
- for index, v := range a.array {
- if v == value {
- result = index
- break
- }
- }
- a.mu.RUnlock()
-
- return result
-}
-
-// 清理数组中重复的元素项
-func (a *IntArray) Unique() *IntArray {
- a.mu.Lock()
- for i := 0; i < len(a.array) - 1; i++ {
- for j := i + 1; j < len(a.array); j++ {
- if a.array[i] == a.array[j] {
- a.array = append(a.array[ : j], a.array[j + 1 : ]...)
- }
- }
- }
- a.mu.Unlock()
- return a
-}
-
-// 使用自定义方法执行加锁修改操作
-func (a *IntArray) LockFunc(f func(array []int)) {
- a.mu.Lock(true)
- defer a.mu.Unlock(true)
- f(a.array)
-}
-
-// 使用自定义方法执行加锁读取操作
-func (a *IntArray) RLockFunc(f func(array []int)) {
- a.mu.RLock(true)
- defer a.mu.RUnlock(true)
- f(a.array)
-}
diff --git a/g/container/garray/garray_interface.go b/g/container/garray/garray_interface.go
deleted file mode 100644
index ba5e927fe..000000000
--- a/g/container/garray/garray_interface.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
-
-package garray
-
-import (
- "gitee.com/johng/gf/g/internal/rwmutex"
-)
-
-type Array struct {
- mu *rwmutex.RWMutex // 互斥锁
- cap int // 初始化设置的数组容量
- size int // 初始化设置的数组大小
- array []interface{} // 底层数组
-}
-
-func NewArray(size int, cap int, unsafe...bool) *Array {
- a := &Array{
- mu : rwmutex.New(unsafe...),
- }
- a.size = size
- if cap > 0 {
- a.cap = cap
- a.array = make([]interface{}, size, cap)
- } else {
- a.array = make([]interface{}, size)
- }
- return a
-}
-
-// 获取指定索引的数据项, 调用方注意判断数组边界
-func (a *Array) Get(index int) interface{} {
- a.mu.RLock()
- defer a.mu.RUnlock()
- value := a.array[index]
- return value
-}
-
-// 设置指定索引的数据项, 调用方注意判断数组边界
-func (a *Array) Set(index int, value interface{}) {
- a.mu.Lock()
- defer a.mu.Unlock()
- a.array[index] = value
-}
-
-// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
-func (a *Array) InsertBefore(index int, value interface{}) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]interface{}{}, a.array[index : ]...)
- a.array = append(a.array[0 : index], value)
- a.array = append(a.array, rear...)
-}
-
-// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
-func (a *Array) InsertAfter(index int, value interface{}) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]interface{}{}, a.array[index + 1 : ]...)
- a.array = append(a.array[0 : index + 1], value)
- a.array = append(a.array, rear...)
-}
-
-// 删除指定索引的数据项, 调用方注意判断数组边界
-func (a *Array) Remove(index int) interface{} {
- a.mu.Lock()
- defer a.mu.Unlock()
- // 边界删除判断,以提高删除效率
- if index == 0 {
- value := a.array[0]
- a.array = a.array[1 : ]
- return value
- } else if index == len(a.array) - 1 {
- value := a.array[index]
- a.array = a.array[: index]
- return value
- }
- // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
- value := a.array[index]
- a.array = append(a.array[ : index], a.array[index + 1 : ]...)
- return value
-}
-
-// 将最左端(索引为0)的数据项移出数组,并返回该数据项
-func (a *Array) PopLeft() interface{} {
- a.mu.Lock()
- defer a.mu.Unlock()
- value := a.array[0]
- a.array = a.array[1 : ]
- return value
-}
-
-// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
-func (a *Array) PopRight() interface{} {
- a.mu.Lock()
- defer a.mu.Unlock()
- index := len(a.array) - 1
- value := a.array[index]
- a.array = a.array[: index]
- return value
-}
-
-// 追加数据项
-func (a *Array) Append(value...interface{}) {
- a.mu.Lock()
- a.array = append(a.array, value...)
- a.mu.Unlock()
-}
-
-// 数组长度
-func (a *Array) Len() int {
- a.mu.RLock()
- length := len(a.array)
- a.mu.RUnlock()
- return length
-}
-
-// 返回原始数据数组
-func (a *Array) Slice() []interface{} {
- array := ([]interface{})(nil)
- if a.mu.IsSafe() {
- a.mu.RLock()
- array = make([]interface{}, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
- } else {
- array = a.array
- }
- return array
-}
-
-// 清空数据数组
-func (a *Array) Clear() {
- a.mu.Lock()
- if len(a.array) > 0 {
- if a.cap > 0 {
- a.array = make([]interface{}, a.size, a.cap)
- } else {
- a.array = make([]interface{}, a.size)
- }
- }
- a.mu.Unlock()
-}
-
-// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
-func (a *Array) Search(value interface{}) int {
- if len(a.array) == 0 {
- return -1
- }
- a.mu.RLock()
- result := -1
- for index, v := range a.array {
- if v == value {
- result = index
- break
- }
- }
- a.mu.RUnlock()
-
- return result
-}
-
-// 清理数组中重复的元素项
-func (a *Array) Unique() *Array {
- a.mu.Lock()
- for i := 0; i < len(a.array) - 1; i++ {
- for j := i + 1; j < len(a.array); j++ {
- if a.array[i] == a.array[j] {
- a.array = append(a.array[ : j], a.array[j + 1 : ]...)
- }
- }
- }
- a.mu.Unlock()
- return a
-}
-
-// 使用自定义方法执行加锁修改操作
-func (a *Array) LockFunc(f func(array []interface{})) {
- a.mu.Lock(true)
- defer a.mu.Unlock(true)
- f(a.array)
-}
-
-// 使用自定义方法执行加锁读取操作
-func (a *Array) RLockFunc(f func(array []interface{})) {
- a.mu.RLock(true)
- defer a.mu.RUnlock(true)
- f(a.array)
-}
diff --git a/g/container/garray/garray_normal_int.go b/g/container/garray/garray_normal_int.go
new file mode 100644
index 000000000..740386e80
--- /dev/null
+++ b/g/container/garray/garray_normal_int.go
@@ -0,0 +1,394 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+package garray
+
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
+)
+
+type IntArray struct {
+ mu *rwmutex.RWMutex // 互斥锁
+ cap int // 初始化设置的数组容量
+ size int // 初始化设置的数组大小
+ array []int // 底层数组
+}
+
+func NewIntArray(size int, cap int, unsafe...bool) *IntArray {
+ a := &IntArray{
+ mu : rwmutex.New(unsafe...),
+ }
+ a.size = size
+ if cap > 0 {
+ a.cap = cap
+ a.array = make([]int, size, cap)
+ } else {
+ a.array = make([]int, size)
+ }
+ return a
+}
+
+func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
+ return &IntArray{
+ mu : rwmutex.New(unsafe...),
+ array : array,
+ }
+}
+
+// 获取指定索引的数据项, 调用方注意判断数组边界
+func (a *IntArray) Get(index int) int {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ value := a.array[index]
+ return value
+}
+
+// 设置指定索引的数据项, 调用方注意判断数组边界
+func (a *IntArray) Set(index int, value int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array[index] = value
+ return a
+}
+
+// 设置底层数组变量.
+func (a *IntArray) SetArray(array []int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *IntArray) Sort() *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Ints(a.array)
+ return a
+}
+
+// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
+func (a *IntArray) InsertBefore(index int, value int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]int{}, a.array[index : ]...)
+ a.array = append(a.array[0 : index], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
+func (a *IntArray) InsertAfter(index int, value int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]int{}, a.array[index + 1:]...)
+ a.array = append(a.array[0 : index + 1], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 删除指定索引的数据项, 调用方注意判断数组边界
+func (a *IntArray) Remove(index int) int {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ // 边界删除判断,以提高删除效率
+ if index == 0 {
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+ } else if index == len(a.array) - 1 {
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+ }
+ // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
+ value := a.array[index]
+ a.array = append(a.array[ : index], a.array[index + 1 : ]...)
+ return value
+}
+
+// 将数据项添加到数组的最左端(索引为0)
+func (a *IntArray) PushLeft(value...int) *IntArray {
+ a.mu.Lock()
+ a.array = append(value, a.array...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append
+func (a *IntArray) PushRight(value...int) *IntArray {
+ a.mu.Lock()
+ a.array = append(a.array, value...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将最左端(索引为0)的数据项移出数组,并返回该数据项
+func (a *IntArray) PopLeft() int {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+}
+
+// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
+func (a *IntArray) PopRight() int {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ index := len(a.array) - 1
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+}
+
+// 随机将一个数据项移出数组,并返回该数据项
+func (a *IntArray) PopRand() int {
+ return a.Remove(grand.Intn(len(a.array)))
+}
+
+// 追加数据项
+func (a *IntArray) Append(value...int) *IntArray {
+ a.mu.Lock()
+ a.array = append(a.array, value...)
+ a.mu.Unlock()
+ return a
+}
+
+// 数组长度
+func (a *IntArray) Len() int {
+ a.mu.RLock()
+ length := len(a.array)
+ a.mu.RUnlock()
+ return length
+}
+
+// 返回原始数据数组
+func (a *IntArray) Slice() []int {
+ array := ([]int)(nil)
+ if a.mu.IsSafe() {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ array = make([]int, len(a.array))
+ copy(array, a.array)
+ } else {
+ array = a.array
+ }
+ return array
+}
+
+// 清空数据数组
+func (a *IntArray) Clear() *IntArray {
+ a.mu.Lock()
+ if len(a.array) > 0 {
+ if a.cap > 0 {
+ a.array = make([]int, a.size, a.cap)
+ } else {
+ a.array = make([]int, a.size)
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 查找指定数值是否存在
+func (a *IntArray) Contains(value int) bool {
+ return a.Search(value) != -1
+}
+
+// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
+func (a *IntArray) Search(value int) int {
+ if len(a.array) == 0 {
+ return -1
+ }
+ a.mu.RLock()
+ result := -1
+ for index, v := range a.array {
+ if v == value {
+ result = index
+ break
+ }
+ }
+ a.mu.RUnlock()
+
+ return result
+}
+
+// 清理数组中重复的元素项
+func (a *IntArray) Unique() *IntArray {
+ a.mu.Lock()
+ for i := 0; i < len(a.array) - 1; i++ {
+ for j := i + 1; j < len(a.array); j++ {
+ if a.array[i] == a.array[j] {
+ a.array = append(a.array[ : j], a.array[j + 1 : ]...)
+ }
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 使用自定义方法执行加锁修改操作
+func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
+ a.mu.Lock(true)
+ defer a.mu.Unlock(true)
+ f(a.array)
+ return a
+}
+
+// 使用自定义方法执行加锁读取操作
+func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
+ a.mu.RLock(true)
+ defer a.mu.RUnlock(true)
+ f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *IntArray) Merge(array *IntArray) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ return a
+}
+
+// Fills an array with num entries of the value of the value parameter, keys starting at the startIndex parameter.
+func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if startIndex < 0 {
+ startIndex = 0
+ }
+ for i := startIndex; i < startIndex + num; i++ {
+ if i > len(a.array) - 1 {
+ a.array = append(a.array, value)
+ } else {
+ a.array[i] = value
+ }
+ }
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *IntArray) Chunk(size int) [][]int {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]int
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Pad array to the specified length with a value.
+// If size is positive then the array is padded on the right, or negative on the left.
+// If the absolute value of size is less than or equal to the length of the array
+// then no padding takes place.
+func (a *IntArray) Pad(size int, value int) *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+ return a
+ }
+ n := size
+ if size < 0 {
+ n = -size
+ }
+ n -= len(a.array)
+ tmp := make([]int, n)
+ for i := 0; i < n; i++ {
+ tmp[i] = value
+ }
+ if size > 0 {
+ a.array = append(a.array, tmp...)
+ } else {
+ a.array = append(tmp, a.array...)
+ }
+ return a
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *IntArray) SubSlice(offset, size int) []int {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]int, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *IntArray) Rand(size int) []int {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]int, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Randomly shuffles the array.
+func (a *IntArray) Shuffle() *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, v := range grand.Perm(len(a.array)) {
+ a.array[i], a.array[v] = a.array[v], a.array[i]
+ }
+ return a
+}
+
+// Make array with elements in reverse order.
+func (a *IntArray) Reverse() *IntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
+ a.array[i], a.array[j] = a.array[j], a.array[i]
+ }
+ return a
+}
+
+// Join array elements with a string.
+func (a *IntArray) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(gconv.Strings(a.array), glue)
+}
\ No newline at end of file
diff --git a/g/container/garray/garray_normal_interface.go b/g/container/garray/garray_normal_interface.go
new file mode 100644
index 000000000..3425c4b2c
--- /dev/null
+++ b/g/container/garray/garray_normal_interface.go
@@ -0,0 +1,393 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+package garray
+
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
+)
+
+type Array struct {
+ mu *rwmutex.RWMutex // 互斥锁
+ cap int // 初始化设置的数组容量
+ size int // 初始化设置的数组大小
+ array []interface{} // 底层数组
+}
+
+func NewArray(size int, cap int, unsafe...bool) *Array {
+ a := &Array{
+ mu : rwmutex.New(unsafe...),
+ }
+ a.size = size
+ if cap > 0 {
+ a.cap = cap
+ a.array = make([]interface{}, size, cap)
+ } else {
+ a.array = make([]interface{}, size)
+ }
+ return a
+}
+
+func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
+ return &Array{
+ mu : rwmutex.New(unsafe...),
+ array : array,
+ }
+}
+
+// 获取指定索引的数据项, 调用方注意判断数组边界
+func (a *Array) Get(index int) interface{} {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ value := a.array[index]
+ return value
+}
+
+// 设置指定索引的数据项, 调用方注意判断数组边界
+func (a *Array) Set(index int, value interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array[index] = value
+ return a
+}
+
+// 设置底层数组变量.
+func (a *Array) SetArray(array []interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *Array) Sort(less func(i, j int) bool) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Slice(a.array, less)
+ return a
+}
+
+// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
+func (a *Array) InsertBefore(index int, value interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]interface{}{}, a.array[index : ]...)
+ a.array = append(a.array[0 : index], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
+func (a *Array) InsertAfter(index int, value interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]interface{}{}, a.array[index + 1 : ]...)
+ a.array = append(a.array[0 : index + 1], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 删除指定索引的数据项, 调用方注意判断数组边界
+func (a *Array) Remove(index int) interface{} {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ // 边界删除判断,以提高删除效率
+ if index == 0 {
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+ } else if index == len(a.array) - 1 {
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+ }
+ // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
+ value := a.array[index]
+ a.array = append(a.array[ : index], a.array[index + 1 : ]...)
+ return value
+}
+
+// 将数据项添加到数组的最左端(索引为0)
+func (a *Array) PushLeft(value...interface{}) *Array {
+ a.mu.Lock()
+ a.array = append(value, a.array...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append
+func (a *Array) PushRight(value...interface{}) *Array {
+ a.mu.Lock()
+ a.array = append(a.array, value...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将最左端(索引为0)的数据项移出数组,并返回该数据项
+func (a *Array) PopLeft() interface{} {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+}
+
+// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
+func (a *Array) PopRight() interface{} {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ index := len(a.array) - 1
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+}
+
+// 随机将一个数据项移出数组,并返回该数据项
+func (a *Array) PopRand() interface{} {
+ return a.Remove(grand.Intn(len(a.array)))
+}
+
+// 追加数据项, 等于: PushRight
+func (a *Array) Append(value...interface{}) *Array {
+ a.PushRight(value...)
+ return a
+}
+
+// 数组长度
+func (a *Array) Len() int {
+ a.mu.RLock()
+ length := len(a.array)
+ a.mu.RUnlock()
+ return length
+}
+
+// 返回原始数据数组
+func (a *Array) Slice() []interface{} {
+ array := ([]interface{})(nil)
+ if a.mu.IsSafe() {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ array = make([]interface{}, len(a.array))
+ copy(array, a.array)
+ } else {
+ array = a.array
+ }
+ return array
+}
+
+// 清空数据数组
+func (a *Array) Clear() *Array {
+ a.mu.Lock()
+ if len(a.array) > 0 {
+ if a.cap > 0 {
+ a.array = make([]interface{}, a.size, a.cap)
+ } else {
+ a.array = make([]interface{}, a.size)
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 查找指定数值是否存在
+func (a *Array) Contains(value interface{}) bool {
+ return a.Search(value) != -1
+}
+
+// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
+func (a *Array) Search(value interface{}) int {
+ if len(a.array) == 0 {
+ return -1
+ }
+ a.mu.RLock()
+ result := -1
+ for index, v := range a.array {
+ if v == value {
+ result = index
+ break
+ }
+ }
+ a.mu.RUnlock()
+
+ return result
+}
+
+// 清理数组中重复的元素项
+func (a *Array) Unique() *Array {
+ a.mu.Lock()
+ for i := 0; i < len(a.array) - 1; i++ {
+ for j := i + 1; j < len(a.array); j++ {
+ if a.array[i] == a.array[j] {
+ a.array = append(a.array[ : j], a.array[j + 1 : ]...)
+ }
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 使用自定义方法执行加锁修改操作
+func (a *Array) LockFunc(f func(array []interface{})) *Array {
+ a.mu.Lock(true)
+ defer a.mu.Unlock(true)
+ f(a.array)
+ return a
+}
+
+// 使用自定义方法执行加锁读取操作
+func (a *Array) RLockFunc(f func(array []interface{})) *Array {
+ a.mu.RLock(true)
+ defer a.mu.RUnlock(true)
+ f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *Array) Merge(array *Array) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ return a
+}
+
+// Fills an array with num entries of the value of the value parameter, keys starting at the start_index parameter.
+func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if startIndex < 0 {
+ startIndex = 0
+ }
+ for i := startIndex; i < startIndex + num; i++ {
+ if i > len(a.array) - 1 {
+ a.array = append(a.array, value)
+ } else {
+ a.array[i] = value
+ }
+ }
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *Array) Chunk(size int) [][]interface{} {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]interface{}
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Pad array to the specified length with a value.
+// If size is positive then the array is padded on the right,
+// if it's negative then on the left.
+// If the absolute value of size is less than or equal to the length of the array
+// then no padding takes place.
+func (a *Array) Pad(size int, val interface{}) *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+ return a
+ }
+ n := size
+ if size < 0 {
+ n = -size
+ }
+ n -= len(a.array)
+ tmp := make([]interface{}, n)
+ for i := 0; i < n; i++ {
+ tmp[i] = val
+ }
+ if size > 0 {
+ a.array = append(a.array, tmp...)
+ } else {
+ a.array = append(tmp, a.array...)
+ }
+ return a
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *Array) SubSlice(offset, size int) []interface{} {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]interface{}, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *Array) Rand(size int) []interface{} {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]interface{}, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Randomly shuffles the array.
+func (a *Array) Shuffle() *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, v := range grand.Perm(len(a.array)) {
+ a.array[i], a.array[v] = a.array[v], a.array[i]
+ }
+ return a
+}
+
+// Make array with elements in reverse order.
+func (a *Array) Reverse() *Array {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
+ a.array[i], a.array[j] = a.array[j], a.array[i]
+ }
+ return a
+}
+
+// Join array elements with a string.
+func (a *Array) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(gconv.Strings(a.array), glue)
+}
\ No newline at end of file
diff --git a/g/container/garray/garray_normal_string.go b/g/container/garray/garray_normal_string.go
new file mode 100644
index 000000000..920c02605
--- /dev/null
+++ b/g/container/garray/garray_normal_string.go
@@ -0,0 +1,394 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+package garray
+
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
+)
+
+type StringArray struct {
+ mu *rwmutex.RWMutex // 互斥锁
+ cap int // 初始化设置的数组容量
+ size int // 初始化设置的数组大小
+ array []string // 底层数组
+}
+
+func NewStringArray(size int, cap int, unsafe...bool) *StringArray {
+ a := &StringArray{
+ mu : rwmutex.New(unsafe...),
+ }
+ a.size = size
+ if cap > 0 {
+ a.cap = cap
+ a.array = make([]string, size, cap)
+ } else {
+ a.array = make([]string, size)
+ }
+ return a
+}
+
+func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
+ return &StringArray {
+ mu : rwmutex.New(unsafe...),
+ array : array,
+ }
+}
+
+// 获取指定索引的数据项, 调用方注意判断数组边界
+func (a *StringArray) Get(index int) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ value := a.array[index]
+ return value
+}
+
+// 设置指定索引的数据项, 调用方注意判断数组边界
+func (a *StringArray) Set(index int, value string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array[index] = value
+ return a
+}
+
+// 设置底层数组变量.
+func (a *StringArray) SetArray(array []string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *StringArray) Sort() *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Strings(a.array)
+ return a
+}
+
+// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
+func (a *StringArray) InsertBefore(index int, value string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]string{}, a.array[index : ]...)
+ a.array = append(a.array[0 : index], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
+func (a *StringArray) InsertAfter(index int, value string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ rear := append([]string{}, a.array[index + 1:]...)
+ a.array = append(a.array[ 0: index + 1], value)
+ a.array = append(a.array, rear...)
+ return a
+}
+
+// 删除指定索引的数据项, 调用方注意判断数组边界
+func (a *StringArray) Remove(index int) string {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ // 边界删除判断,以提高删除效率
+ if index == 0 {
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+ } else if index == len(a.array) - 1 {
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+ }
+ // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
+ value := a.array[index]
+ a.array = append(a.array[ : index], a.array[index + 1 : ]...)
+ return value
+}
+
+// 将数据项添加到数组的最左端(索引为0)
+func (a *StringArray) PushLeft(value...string) *StringArray {
+ a.mu.Lock()
+ a.array = append(value, a.array...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append
+func (a *StringArray) PushRight(value...string) *StringArray {
+ a.mu.Lock()
+ a.array = append(a.array, value...)
+ a.mu.Unlock()
+ return a
+}
+
+// 将最左端(索引为0)的数据项移出数组,并返回该数据项
+func (a *StringArray) PopLeft() string {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ value := a.array[0]
+ a.array = a.array[1 : ]
+ return value
+}
+
+// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
+func (a *StringArray) PopRight() string {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ index := len(a.array) - 1
+ value := a.array[index]
+ a.array = a.array[: index]
+ return value
+}
+
+// 随机将一个数据项移出数组,并返回该数据项
+func (a *StringArray) PopRand() string {
+ return a.Remove(grand.Intn(len(a.array)))
+}
+
+// 追加数据项
+func (a *StringArray) Append(value...string) *StringArray {
+ a.mu.Lock()
+ a.array = append(a.array, value...)
+ a.mu.Unlock()
+ return a
+}
+
+// 数组长度
+func (a *StringArray) Len() int {
+ a.mu.RLock()
+ length := len(a.array)
+ a.mu.RUnlock()
+ return length
+}
+
+// 返回原始数据数组
+func (a *StringArray) Slice() []string {
+ array := ([]string)(nil)
+ if a.mu.IsSafe() {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ array = make([]string, len(a.array))
+ copy(array, a.array)
+ } else {
+ array = a.array
+ }
+ return array
+}
+
+// 清空数据数组
+func (a *StringArray) Clear() *StringArray {
+ a.mu.Lock()
+ if len(a.array) > 0 {
+ if a.cap > 0 {
+ a.array = make([]string, a.size, a.cap)
+ } else {
+ a.array = make([]string, a.size)
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 查找指定数值是否存在
+func (a *StringArray) Contains(value string) bool {
+ return a.Search(value) != -1
+}
+
+// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
+func (a *StringArray) Search(value string) int {
+ if len(a.array) == 0 {
+ return -1
+ }
+ a.mu.RLock()
+ result := -1
+ for index, v := range a.array {
+ if strings.Compare(v, value) == 0 {
+ result = index
+ break
+ }
+ }
+ a.mu.RUnlock()
+ return result
+}
+
+// 清理数组中重复的元素项
+func (a *StringArray) Unique() *StringArray {
+ a.mu.Lock()
+ for i := 0; i < len(a.array) - 1; i++ {
+ for j := i + 1; j < len(a.array); j++ {
+ if a.array[i] == a.array[j] {
+ a.array = append(a.array[ : j], a.array[j + 1 : ]...)
+ }
+ }
+ }
+ a.mu.Unlock()
+ return a
+}
+
+// 使用自定义方法执行加锁修改操作
+func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
+ a.mu.Lock(true)
+ defer a.mu.Unlock(true)
+ f(a.array)
+ return a
+}
+
+// 使用自定义方法执行加锁读取操作
+func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
+ a.mu.RLock(true)
+ defer a.mu.RUnlock(true)
+ f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *StringArray) Merge(array *StringArray) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ return a
+}
+
+// Fills an array with num entries of the value of the value parameter, keys starting at the start_index parameter.
+func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if startIndex < 0 {
+ startIndex = 0
+ }
+ for i := startIndex; i < startIndex + num; i++ {
+ if i > len(a.array) - 1 {
+ a.array = append(a.array, value)
+ } else {
+ a.array[i] = value
+ }
+ }
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *StringArray) Chunk(size int) [][]string {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]string
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Pad array to the specified length with a value.
+// If size is positive then the array is padded on the right,
+// if it's negative then on the left.
+// If the absolute value of size is less than or equal to the length of the array
+// then no padding takes place.
+func (a *StringArray) Pad(size int, value string) *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+ return a
+ }
+ n := size
+ if size < 0 {
+ n = -size
+ }
+ n -= len(a.array)
+ tmp := make([]string, n)
+ for i := 0; i < n; i++ {
+ tmp[i] = value
+ }
+ if size > 0 {
+ a.array = append(a.array, tmp...)
+ } else {
+ a.array = append(tmp, a.array...)
+ }
+ return a
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *StringArray) SubSlice(offset, size int) []string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]string, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *StringArray) Rand(size int) []string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]string, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Randomly shuffles the array.
+func (a *StringArray) Shuffle() *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, v := range grand.Perm(len(a.array)) {
+ a.array[i], a.array[v] = a.array[v], a.array[i]
+ }
+ return a
+}
+
+// Make array with elements in reverse order.
+func (a *StringArray) Reverse() *StringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
+ a.array[i], a.array[j] = a.array[j], a.array[i]
+ }
+ return a
+}
+
+// Join array elements with a string.
+func (a *StringArray) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(a.array, glue)
+}
+
diff --git a/g/container/garray/garray_sorted_int.go b/g/container/garray/garray_sorted_int.go
index 21082698f..d2c6ec0ee 100644
--- a/g/container/garray/garray_sorted_int.go
+++ b/g/container/garray/garray_sorted_int.go
@@ -9,9 +9,14 @@ package garray
import (
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
)
-// 默认按照从低到高进行排序
+// 默认按照从小到大进行排序
type SortedIntArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
@@ -38,10 +43,34 @@ func NewSortedIntArray(cap int, unsafe...bool) *SortedIntArray {
}
}
+func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
+ a := NewSortedIntArray(0, unsafe...)
+ a.array = array
+ sort.Ints(a.array)
+ return a
+}
+
+// 设置底层数组变量.
+func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ sort.Ints(a.array)
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *SortedIntArray) Sort() *SortedIntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Ints(a.array)
+ return a
+}
+
// 添加加数据项
-func (a *SortedIntArray) Add(values...int) {
+func (a *SortedIntArray) Add(values...int) *SortedIntArray {
if len(values) == 0 {
- return
+ return a
}
a.mu.Lock()
defer a.mu.Unlock()
@@ -62,6 +91,7 @@ func (a *SortedIntArray) Add(values...int) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
+ return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
@@ -124,17 +154,21 @@ func (a *SortedIntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
+ defer a.mu.RUnlock()
array = make([]int, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
+ copy(array, a.array)
} else {
array = a.array
}
return array
}
+// 查找指定数值是否存在
+func (a *SortedIntArray) Contains(value int) bool {
+ _, r := a.Search(value)
+ return r == 0
+}
+
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedIntArray) Search(value int) (index int, result int) {
@@ -167,16 +201,17 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
}
// 设置是否允许数组唯一
-func (a *SortedIntArray) SetUnique(unique bool) {
+func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
- a.doUnique()
+ a.Unique()
}
+ return a
}
// 清理数组中重复的元素项
-func (a *SortedIntArray) doUnique() {
+func (a *SortedIntArray) Unique() *SortedIntArray {
a.mu.Lock()
i := 0
for {
@@ -190,27 +225,109 @@ func (a *SortedIntArray) doUnique() {
}
}
a.mu.Unlock()
+ return a
}
// 清空数据数组
-func (a *SortedIntArray) Clear() {
+func (a *SortedIntArray) Clear() *SortedIntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0, a.cap)
}
a.mu.Unlock()
+ return a
}
// 使用自定义方法执行加锁修改操作
-func (a *SortedIntArray) LockFunc(f func(array []int)) {
+func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
+ return a
}
// 使用自定义方法执行加锁读取操作
-func (a *SortedIntArray) RLockFunc(f func(array []int)) {
+func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *SortedIntArray) Merge(array *SortedIntArray) *SortedIntArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ sort.Ints(a.array)
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *SortedIntArray) Chunk(size int) [][]int {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]int
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *SortedIntArray) SubSlice(offset, size int) []int {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]int, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *SortedIntArray) Rand(size int) []int {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]int, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Join array elements with a string.
+func (a *SortedIntArray) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(gconv.Strings(a.array), glue)
}
\ No newline at end of file
diff --git a/g/container/garray/garray_sorted_interface.go b/g/container/garray/garray_sorted_interface.go
index 27ab6cdcb..98efacd89 100644
--- a/g/container/garray/garray_sorted_interface.go
+++ b/g/container/garray/garray_sorted_interface.go
@@ -9,9 +9,14 @@ package garray
import (
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
)
-// 默认按照从低到高进行排序
+// 默认按照从小到大进行排序
type SortedArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
@@ -29,10 +34,40 @@ func NewSortedArray(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...
}
}
+func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
+ a := NewSortedArray(0, compareFunc, unsafe...)
+ a.array = array
+ sort.Slice(a.array, func(i, j int) bool {
+ return a.compareFunc(a.array[i], a.array[j]) < 0
+ })
+ return a
+}
+
+// 设置底层数组变量.
+func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ sort.Slice(a.array, func(i, j int) bool {
+ return a.compareFunc(a.array[i], a.array[j]) < 0
+ })
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *SortedArray) Sort() *SortedArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Slice(a.array, func(i, j int) bool {
+ return a.compareFunc(a.array[i], a.array[j]) < 0
+ })
+ return a
+}
+
// 添加加数据项
-func (a *SortedArray) Add(values...interface{}) {
+func (a *SortedArray) Add(values...interface{}) *SortedArray {
if len(values) == 0 {
- return
+ return a
}
a.mu.Lock()
defer a.mu.Unlock()
@@ -53,6 +88,7 @@ func (a *SortedArray) Add(values...interface{}) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
+ return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
@@ -115,17 +151,21 @@ func (a *SortedArray) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
+ defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
+ copy(array, a.array)
} else {
array = a.array
}
return array
}
+// 查找指定数值是否存在
+func (a *SortedArray) Contains(value interface{}) bool {
+ _, r := a.Search(value)
+ return r == 0
+}
+
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedArray) Search(value interface{}) (index int, result int) {
@@ -160,16 +200,17 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
}
// 设置是否允许数组唯一
-func (a *SortedArray) SetUnique(unique bool) {
+func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
- a.doUnique()
+ a.Unique()
}
+ return a
}
// 清理数组中重复的元素项
-func (a *SortedArray) doUnique() {
+func (a *SortedArray) Unique() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
i := 0
@@ -183,27 +224,111 @@ func (a *SortedArray) doUnique() {
i++
}
}
+ return a
}
// 清空数据数组
-func (a *SortedArray) Clear() {
+func (a *SortedArray) Clear() *SortedArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0, a.cap)
}
a.mu.Unlock()
+ return a
}
// 使用自定义方法执行加锁修改操作
-func (a *SortedArray) LockFunc(f func(array []interface{})) {
+func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
+ return a
}
// 使用自定义方法执行加锁读取操作
-func (a *SortedArray) RLockFunc(f func(array []interface{})) {
+func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *SortedArray) Merge(array *SortedArray) *SortedArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ sort.Slice(a.array, func(i, j int) bool {
+ return a.compareFunc(a.array[i], a.array[j]) < 0
+ })
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *SortedArray) Chunk(size int) [][]interface{} {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]interface{}
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *SortedArray) SubSlice(offset, size int) []interface{} {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]interface{}, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *SortedArray) Rand(size int) []interface{} {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]interface{}, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Join array elements with a string.
+func (a *SortedArray) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(gconv.Strings(a.array), glue)
}
\ No newline at end of file
diff --git a/g/container/garray/garray_sorted_string.go b/g/container/garray/garray_sorted_string.go
index 7cae7e73d..bdb7e5517 100644
--- a/g/container/garray/garray_sorted_string.go
+++ b/g/container/garray/garray_sorted_string.go
@@ -8,11 +8,14 @@ package garray
import (
"gitee.com/johng/gf/g/container/gtype"
- "strings"
"gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "sort"
+ "strings"
)
-// 默认按照从低到高进行排序
+// 默认按照从小到大进行排序
type SortedStringArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
@@ -32,10 +35,34 @@ func NewSortedStringArray(cap int, unsafe...bool) *SortedStringArray {
}
}
+func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
+ a := NewSortedStringArray(0, unsafe...)
+ a.array = array
+ sort.Strings(a.array)
+ return a
+}
+
+// 设置底层数组变量.
+func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.array = array
+ sort.Strings(a.array)
+ return a
+}
+
+// 将数组重新排序(从小到大).
+func (a *SortedStringArray) Sort() *SortedStringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ sort.Strings(a.array)
+ return a
+}
+
// 添加加数据项
-func (a *SortedStringArray) Add(values...string) {
+func (a *SortedStringArray) Add(values...string) *SortedStringArray {
if len(values) == 0 {
- return
+ return a
}
a.mu.Lock()
defer a.mu.Unlock()
@@ -56,6 +83,7 @@ func (a *SortedStringArray) Add(values...string) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
+ return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
@@ -118,17 +146,21 @@ func (a *SortedStringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
+ defer a.mu.RUnlock()
array = make([]string, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
+ copy(array, a.array)
} else {
array = a.array
}
return array
}
+// 查找指定数值是否存在
+func (a *SortedStringArray) Contains(value string) bool {
+ _, r := a.Search(value)
+ return r == 0
+}
+
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedStringArray) Search(value string) (index int, result int) {
@@ -161,16 +193,17 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
}
// 设置是否允许数组唯一
-func (a *SortedStringArray) SetUnique(unique bool) {
+func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
- a.doUnique()
+ a.Unique()
}
+ return a
}
// 清理数组中重复的元素项
-func (a *SortedStringArray) doUnique() {
+func (a *SortedStringArray) Unique() *SortedStringArray {
a.mu.Lock()
i := 0
for {
@@ -184,27 +217,109 @@ func (a *SortedStringArray) doUnique() {
}
}
a.mu.Unlock()
+ return a
}
// 清空数据数组
-func (a *SortedStringArray) Clear() {
+func (a *SortedStringArray) Clear() *SortedStringArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0, a.cap)
}
a.mu.Unlock()
+ return a
}
// 使用自定义方法执行加锁修改操作
-func (a *SortedStringArray) LockFunc(f func(array []string)) {
+func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
+ return a
}
// 使用自定义方法执行加锁读取操作
-func (a *SortedStringArray) RLockFunc(f func(array []string)) {
+func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
+ return a
+}
+
+// 合并两个数组.
+func (a *SortedStringArray) Merge(array *SortedStringArray) *SortedStringArray {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ if a != array {
+ array.mu.RLock()
+ defer array.mu.RUnlock()
+ }
+ a.array = append(a.array, array.array...)
+ sort.Strings(a.array)
+ return a
+}
+
+// Chunks an array into arrays with size elements. The last chunk may contain less than size elements.
+func (a *SortedStringArray) Chunk(size int) [][]string {
+ if size < 1 {
+ panic("size: cannot be less than 1")
+ }
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ length := len(a.array)
+ chunks := int(math.Ceil(float64(length) / float64(size)))
+ var n [][]string
+ for i, end := 0, 0; chunks > 0; chunks-- {
+ end = (i + 1) * size
+ if end > length {
+ end = length
+ }
+ n = append(n, a.array[i*size : end])
+ i++
+ }
+ return n
+}
+
+// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
+// It returns the sequence of elements from the array array as specified by the offset and length parameters.
+func (a *SortedStringArray) SubSlice(offset, size int) []string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if offset > len(a.array) {
+ return nil
+ }
+ if offset + size > len(a.array) {
+ size = len(a.array) - offset
+ }
+ if a.mu.IsSafe() {
+ s := make([]string, size)
+ copy(s, a.array[offset:])
+ return s
+ } else {
+ return a.array[offset:]
+ }
+}
+
+// Picks one or more random entries out of an array(a copy), and returns the key (or keys) of the random entries.
+func (a *SortedStringArray) Rand(size int) []string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ if size > len(a.array) {
+ size = len(a.array)
+ }
+ n := make([]string, size)
+ for i, v := range grand.Perm(len(a.array)) {
+ n[i] = a.array[v]
+ if i == size - 1 {
+ break
+ }
+ }
+ return n
+}
+
+// Join array elements with a string.
+func (a *SortedStringArray) Join(glue string) string {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return strings.Join(a.array, glue)
}
\ No newline at end of file
diff --git a/g/container/garray/garray_string.go b/g/container/garray/garray_string.go
deleted file mode 100644
index b6298a207..000000000
--- a/g/container/garray/garray_string.go
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
-
-package garray
-
-import (
- "strings"
- "gitee.com/johng/gf/g/internal/rwmutex"
-)
-
-type StringArray struct {
- mu *rwmutex.RWMutex // 互斥锁
- cap int // 初始化设置的数组容量
- size int // 初始化设置的数组大小
- array []string // 底层数组
-}
-
-func NewStringArray(size int, cap int, unsafe...bool) *StringArray {
- a := &StringArray{
- mu : rwmutex.New(unsafe...),
- }
- a.size = size
- if cap > 0 {
- a.cap = cap
- a.array = make([]string, size, cap)
- } else {
- a.array = make([]string, size)
- }
- return a
-}
-
-// 获取指定索引的数据项, 调用方注意判断数组边界
-func (a *StringArray) Get(index int) string {
- a.mu.RLock()
- defer a.mu.RUnlock()
- value := a.array[index]
- return value
-}
-
-// 设置指定索引的数据项, 调用方注意判断数组边界
-func (a *StringArray) Set(index int, value string) {
- a.mu.Lock()
- defer a.mu.Unlock()
- a.array[index] = value
-}
-
-// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
-func (a *StringArray) InsertBefore(index int, value string) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]string{}, a.array[index : ]...)
- a.array = append(a.array[0 : index], value)
- a.array = append(a.array, rear...)
-}
-
-// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
-func (a *StringArray) InsertAfter(index int, value string) {
- a.mu.Lock()
- defer a.mu.Unlock()
- rear := append([]string{}, a.array[index + 1:]...)
- a.array = append(a.array[ 0: index + 1], value)
- a.array = append(a.array, rear...)
-}
-
-// 删除指定索引的数据项, 调用方注意判断数组边界
-func (a *StringArray) Remove(index int) string {
- a.mu.Lock()
- defer a.mu.Unlock()
- // 边界删除判断,以提高删除效率
- if index == 0 {
- value := a.array[0]
- a.array = a.array[1 : ]
- return value
- } else if index == len(a.array) - 1 {
- value := a.array[index]
- a.array = a.array[: index]
- return value
- }
- // 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
- value := a.array[index]
- a.array = append(a.array[ : index], a.array[index + 1 : ]...)
- return value
-}
-
-// 追加数据项
-func (a *StringArray) Append(value...string) {
- a.mu.Lock()
- a.array = append(a.array, value...)
- a.mu.Unlock()
-}
-
-// 数组长度
-func (a *StringArray) Len() int {
- a.mu.RLock()
- length := len(a.array)
- a.mu.RUnlock()
- return length
-}
-
-// 返回原始数据数组
-func (a *StringArray) Slice() []string {
- array := ([]string)(nil)
- if a.mu.IsSafe() {
- a.mu.RLock()
- array = make([]string, len(a.array))
- for k, v := range a.array {
- array[k] = v
- }
- a.mu.RUnlock()
- } else {
- array = a.array
- }
- return array
-}
-
-// 清空数据数组
-func (a *StringArray) Clear() {
- a.mu.Lock()
- if len(a.array) > 0 {
- if a.cap > 0 {
- a.array = make([]string, a.size, a.cap)
- } else {
- a.array = make([]string, a.size)
- }
- }
- a.mu.Unlock()
-}
-
-// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
-func (a *StringArray) Search(value string) int {
- if len(a.array) == 0 {
- return -1
- }
- a.mu.RLock()
- result := -1
- for index, v := range a.array {
- if strings.Compare(v, value) == 0 {
- result = index
- break
- }
- }
- a.mu.RUnlock()
- return result
-}
-
-// 清理数组中重复的元素项
-func (a *StringArray) Unique() *StringArray {
- a.mu.Lock()
- for i := 0; i < len(a.array) - 1; i++ {
- for j := i + 1; j < len(a.array); j++ {
- if a.array[i] == a.array[j] {
- a.array = append(a.array[ : j], a.array[j + 1 : ]...)
- }
- }
- }
- a.mu.Unlock()
- return a
-}
-
-// 使用自定义方法执行加锁修改操作
-func (a *StringArray) LockFunc(f func(array []string)) {
- a.mu.Lock(true)
- defer a.mu.Unlock(true)
- f(a.array)
-}
-
-// 使用自定义方法执行加锁读取操作
-func (a *StringArray) RLockFunc(f func(array []string)) {
- a.mu.RLock(true)
- defer a.mu.RUnlock(true)
- f(a.array)
-}
diff --git a/g/container/garray/garray_z_unit_test.go b/g/container/garray/garray_z_unit_basic_test.go
similarity index 98%
rename from g/container/garray/garray_z_unit_test.go
rename to g/container/garray/garray_z_unit_basic_test.go
index ddbed30a2..23c07b631 100644
--- a/g/container/garray/garray_z_unit_test.go
+++ b/g/container/garray/garray_z_unit_basic_test.go
@@ -10,13 +10,12 @@ package garray_test
import (
"gitee.com/johng/gf/g/container/garray"
+ "gitee.com/johng/gf/g/test/gtest"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
"strings"
"testing"
)
-
func Test_IntArray_Unique(t *testing.T) {
expect := []int{1, 2, 3, 4, 5, 6}
array := garray.NewIntArray(0, 0)
diff --git a/g/container/garray/garray_z_unit_int_test.go b/g/container/garray/garray_z_unit_int_test.go
new file mode 100644
index 000000000..63c11ea22
--- /dev/null
+++ b/g/container/garray/garray_z_unit_int_test.go
@@ -0,0 +1,161 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+// go test *.go
+
+package garray_test
+
+import (
+ "gitee.com/johng/gf/g/container/garray"
+ "gitee.com/johng/gf/g/test/gtest"
+ "testing"
+)
+
+func Test_IntArray_Basic(t *testing.T) {
+ gtest.Case(t, func() {
+ expect := []int{0, 1, 2, 3}
+ array := garray.NewIntArrayFrom(expect)
+ gtest.Assert(array.Slice(), expect)
+ array.Set(0, 100)
+ gtest.Assert(array.Get(0), 100)
+ gtest.Assert(array.Get(1), 1)
+ gtest.Assert(array.Search(100), 0)
+ gtest.Assert(array.Contains(100), true)
+ gtest.Assert(array.Remove(0), 100)
+ gtest.Assert(array.Contains(100), false)
+ array.Append(4)
+ gtest.Assert(array.Len(), 4)
+ array.InsertBefore(0, 100)
+ array.InsertAfter(0, 200)
+ gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
+ array.InsertBefore(5, 300)
+ array.InsertAfter(6, 400)
+ gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
+ gtest.Assert(array.Clear().Len(), 0)
+ })
+}
+
+func TestIntArray_Sort(t *testing.T) {
+ gtest.Case(t, func() {
+ expect := []int{0, 1, 2, 3}
+ array := garray.NewIntArray(0, 0)
+ for i := 3; i >= 0; i-- {
+ array.Append(i)
+ }
+ array.Sort()
+ gtest.Assert(array.Slice(), expect)
+ })
+}
+
+func TestIntArray_Unique(t *testing.T) {
+ gtest.Case(t, func() {
+ expect := []int{1, 1, 2, 3}
+ array := garray.NewIntArrayFrom(expect)
+ gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
+ })
+}
+
+func TestIntArray_PushAndPop(t *testing.T) {
+ gtest.Case(t, func() {
+ expect := []int{0, 1, 2, 3}
+ array := garray.NewIntArrayFrom(expect)
+ gtest.Assert(array.Slice(), expect)
+ gtest.Assert(array.PopLeft(), 0)
+ gtest.Assert(array.PopRight(), 3)
+ gtest.AssertIN(array.PopRand(), []int{1, 2})
+ gtest.AssertIN(array.PopRand(), []int{1, 2})
+ gtest.Assert(array.Len(), 0)
+ array.PushLeft(1).PushRight(2)
+ gtest.Assert(array.Slice(), []int{1, 2})
+ })
+}
+
+func TestIntArray_Merge(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0, 1, 2, 3}
+ a2 := []int{4, 5, 6, 7}
+ array1 := garray.NewIntArrayFrom(a1)
+ array2 := garray.NewIntArrayFrom(a2)
+ gtest.Assert(array1.Merge(array2).Slice(), []int{0,1,2,3,4,5,6,7})
+ })
+}
+
+func TestIntArray_Fill(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0}
+ a2 := []int{0}
+ array1 := garray.NewIntArrayFrom(a1)
+ array2 := garray.NewIntArrayFrom(a2)
+ gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0,100,100})
+ gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100,100})
+ })
+}
+
+func TestIntArray_Chunk(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{1,2,3,4,5}
+ array1 := garray.NewIntArrayFrom(a1)
+ chunks := array1.Chunk(2)
+ gtest.Assert(len(chunks), 3)
+ gtest.Assert(chunks[0], []int{1,2})
+ gtest.Assert(chunks[1], []int{3,4})
+ gtest.Assert(chunks[2], []int{5})
+ })
+}
+
+func TestIntArray_Pad(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(array1.Pad(3, 1).Slice(), []int{0,1,1})
+ gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1,0,1,1})
+ gtest.Assert(array1.Pad(3, 1).Slice(), []int{1,0,1,1})
+ })
+}
+
+func TestIntArray_SubSlice(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0,1,2,3,4,5,6}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(array1.SubSlice(0, 2), []int{0,1})
+ gtest.Assert(array1.SubSlice(2, 2), []int{2,3})
+ gtest.Assert(array1.SubSlice(5, 8), []int{5,6})
+ })
+}
+
+func TestIntArray_Rand(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0,1,2,3,4,5,6}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(len(array1.Rand(2)), 2)
+ gtest.Assert(len(array1.Rand(10)), 7)
+ gtest.AssertIN(array1.Rand(1)[0], a1)
+ })
+}
+
+func TestIntArray_Shuffle(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0,1,2,3,4,5,6}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(array1.Shuffle().Len(), 7)
+ })
+}
+
+func TestIntArray_Reverse(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0,1,2,3,4,5,6}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(array1.Reverse().Slice(), []int{6,5,4,3,2,1,0})
+ })
+}
+
+func TestIntArray_Join(t *testing.T) {
+ gtest.Case(t, func() {
+ a1 := []int{0,1,2,3,4,5,6}
+ array1 := garray.NewIntArrayFrom(a1)
+ gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
+ })
+}
\ No newline at end of file
diff --git a/g/container/glist/glist.go b/g/container/glist/glist.go
index d27843091..0659047f1 100644
--- a/g/container/glist/glist.go
+++ b/g/container/glist/glist.go
@@ -7,7 +7,7 @@
// Package glist provides a concurrent-safe(alternative) doubly linked list.
//
-// 并发安全的双向链表.
+// 并发安全双向链表.
package glist
import (
diff --git a/g/container/gmap/gmap.go b/g/container/gmap/gmap.go
index e149f35ec..b12f88f96 100644
--- a/g/container/gmap/gmap.go
+++ b/g/container/gmap/gmap.go
@@ -6,7 +6,7 @@
// Package gmap provides kinds of concurrent-safe(alternative) maps.
//
-// 并发安全的哈希MAP.
+// 并发安全MAP.
package gmap
// 默认的Map对象其实就是InterfaceInterfaceMap的别名。
diff --git a/g/container/gmap/gmap_func.go b/g/container/gmap/gmap_func.go
new file mode 100644
index 000000000..15ebd4650
--- /dev/null
+++ b/g/container/gmap/gmap_func.go
@@ -0,0 +1,8 @@
+// Copyright 2019 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+package gmap
+
diff --git a/g/container/gmap/gmap_int_bool_map.go b/g/container/gmap/gmap_int_bool_map.go
index 199e22480..bda848ca4 100644
--- a/g/container/gmap/gmap_int_bool_map.go
+++ b/g/container/gmap/gmap_int_bool_map.go
@@ -220,4 +220,17 @@ func (this *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
this.mu.RLock(true)
defer this.mu.RUnlock(true)
f(this.m)
+}
+
+// 合并两个Map.
+func (this *IntBoolMap) Merge(m *IntBoolMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_int_int_map.go b/g/container/gmap/gmap_int_int_map.go
index 94f042f67..4cfeb6a5e 100644
--- a/g/container/gmap/gmap_int_int_map.go
+++ b/g/container/gmap/gmap_int_int_map.go
@@ -221,3 +221,27 @@ func (this *IntIntMap) RLockFunc(f func(m map[int]int)) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *IntIntMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[int]int, len(this.m))
+ for k, v := range this.m {
+ n[v] = k
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *IntIntMap) Merge(m *IntIntMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
diff --git a/g/container/gmap/gmap_int_interface_map.go b/g/container/gmap/gmap_int_interface_map.go
index dd147e958..e6d0a5bb1 100644
--- a/g/container/gmap/gmap_int_interface_map.go
+++ b/g/container/gmap/gmap_int_interface_map.go
@@ -7,7 +7,10 @@
package gmap
-import "gitee.com/johng/gf/g/internal/rwmutex"
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+)
type IntInterfaceMap struct {
mu *rwmutex.RWMutex
@@ -204,3 +207,27 @@ func (this *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *IntInterfaceMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[int]interface{}, len(this.m))
+ for k, v := range this.m {
+ n[gconv.Int(v)] = k
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *IntInterfaceMap) Merge(m *IntInterfaceMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_int_string_map.go b/g/container/gmap/gmap_int_string_map.go
index 0411a3032..9f6cc4cf0 100644
--- a/g/container/gmap/gmap_int_string_map.go
+++ b/g/container/gmap/gmap_int_string_map.go
@@ -9,6 +9,7 @@ package gmap
import (
"gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
)
type IntStringMap struct {
@@ -221,3 +222,27 @@ func (this *IntStringMap) RLockFunc(f func(m map[int]string)) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *IntStringMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[int]string, len(this.m))
+ for k, v := range this.m {
+ n[gconv.Int(v)] = gconv.String(k)
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *IntStringMap) Merge(m *IntStringMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_interface_interface_map.go b/g/container/gmap/gmap_interface_interface_map.go
index c9ed697c7..725f28f4b 100644
--- a/g/container/gmap/gmap_interface_interface_map.go
+++ b/g/container/gmap/gmap_interface_interface_map.go
@@ -206,3 +206,27 @@ func (this *InterfaceInterfaceMap) RLockFunc(f func(m map[interface{}]interface{
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *InterfaceInterfaceMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[interface{}]interface{}, len(this.m))
+ for i, v := range this.m {
+ n[v] = i
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *InterfaceInterfaceMap) Merge(m *Map) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
diff --git a/g/container/gmap/gmap_string_bool_map.go b/g/container/gmap/gmap_string_bool_map.go
index 2f07f0250..80c9707c8 100644
--- a/g/container/gmap/gmap_string_bool_map.go
+++ b/g/container/gmap/gmap_string_bool_map.go
@@ -221,3 +221,16 @@ func (this *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 合并两个Map.
+func (this *StringBoolMap) Merge(m *StringBoolMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_string_int_map.go b/g/container/gmap/gmap_string_int_map.go
index 939f853f6..77a805e68 100644
--- a/g/container/gmap/gmap_string_int_map.go
+++ b/g/container/gmap/gmap_string_int_map.go
@@ -7,7 +7,10 @@
package gmap
-import "gitee.com/johng/gf/g/internal/rwmutex"
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
+)
type StringIntMap struct {
mu *rwmutex.RWMutex
@@ -219,3 +222,27 @@ func (this *StringIntMap) RLockFunc(f func(m map[string]int)) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *StringIntMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[string]int, len(this.m))
+ for k, v := range this.m {
+ n[gconv.String(v)] = gconv.Int(k)
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *StringIntMap) Merge(m *StringIntMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_string_interface_map.go b/g/container/gmap/gmap_string_interface_map.go
index b7ab9a5a9..bc033013f 100644
--- a/g/container/gmap/gmap_string_interface_map.go
+++ b/g/container/gmap/gmap_string_interface_map.go
@@ -9,6 +9,7 @@ package gmap
import (
"gitee.com/johng/gf/g/internal/rwmutex"
+ "gitee.com/johng/gf/g/util/gconv"
)
type StringInterfaceMap struct {
@@ -206,3 +207,27 @@ func (this *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *StringInterfaceMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[string]interface{}, len(this.m))
+ for k, v := range this.m {
+ n[gconv.String(v)] = k
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *StringInterfaceMap) Merge(m *StringInterfaceMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
\ No newline at end of file
diff --git a/g/container/gmap/gmap_string_string_map.go b/g/container/gmap/gmap_string_string_map.go
index fc18893e6..c303d5dff 100644
--- a/g/container/gmap/gmap_string_string_map.go
+++ b/g/container/gmap/gmap_string_string_map.go
@@ -7,7 +7,9 @@
package gmap
-import "gitee.com/johng/gf/g/internal/rwmutex"
+import (
+ "gitee.com/johng/gf/g/internal/rwmutex"
+)
type StringStringMap struct {
mu *rwmutex.RWMutex
@@ -219,3 +221,27 @@ func (this *StringStringMap) RLockFunc(f func(m map[string]string)) {
defer this.mu.RUnlock(true)
f(this.m)
}
+
+// 交换Map中的键和值.
+func (this *StringStringMap) Flip() {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ n := make(map[string]string, len(this.m))
+ for k, v := range this.m {
+ n[v] = k
+ }
+ this.m = n
+}
+
+// 合并两个Map.
+func (this *StringStringMap) Merge(m *StringStringMap) {
+ this.mu.Lock()
+ defer this.mu.Unlock()
+ if m != this {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ }
+ for k, v := range m.m {
+ this.m[k] = v
+ }
+}
diff --git a/g/container/gmap/gmap_bench_safe_test.go b/g/container/gmap/gmap_z_bench_safe_test.go
similarity index 100%
rename from g/container/gmap/gmap_bench_safe_test.go
rename to g/container/gmap/gmap_z_bench_safe_test.go
diff --git a/g/container/gmap/gmap_bench_syncmap_test.go b/g/container/gmap/gmap_z_bench_syncmap_test.go
similarity index 100%
rename from g/container/gmap/gmap_bench_syncmap_test.go
rename to g/container/gmap/gmap_z_bench_syncmap_test.go
diff --git a/g/container/gmap/gmap_bench_unsafe_test.go b/g/container/gmap/gmap_z_bench_unsafe_test.go
similarity index 100%
rename from g/container/gmap/gmap_bench_unsafe_test.go
rename to g/container/gmap/gmap_z_bench_unsafe_test.go
diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go
index 257ce8235..dbb4128e5 100644
--- a/g/container/gqueue/gqueue.go
+++ b/g/container/gqueue/gqueue.go
@@ -6,7 +6,7 @@
// Package gqueue provides a dynamic/static concurrent-safe(alternative) queue.
//
-// 并发安全的动态队列.
+// 并发安全动态队列.
//
// 特点:
// 1. 动态队列初始化速度快;
diff --git a/g/container/gset/gset_int_set.go b/g/container/gset/gset_int_set.go
index c254369f2..44501b904 100644
--- a/g/container/gset/gset_int_set.go
+++ b/g/container/gset/gset_int_set.go
@@ -26,7 +26,7 @@ func NewIntSet(unsafe...bool) *IntSet {
}
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
-func (set *IntSet) Iterator(f func (v int) bool) {
+func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
@@ -34,6 +34,7 @@ func (set *IntSet) Iterator(f func (v int) bool) {
break
}
}
+ return set
}
// 设置键
@@ -62,11 +63,12 @@ func (set *IntSet) Contains(item int) bool {
return exists
}
-// 删除键值对
-func (set *IntSet) Remove(key int) {
+// 删除元素项
+func (set *IntSet) Remove(key int) *IntSet {
set.mu.Lock()
delete(set.m, key)
set.mu.Unlock()
+ return set
}
// 大小
@@ -78,10 +80,11 @@ func (set *IntSet) Size() int {
}
// 清空set
-func (set *IntSet) Clear() {
+func (set *IntSet) Clear() *IntSet {
set.mu.Lock()
set.m = make(map[int]struct{})
set.mu.Unlock()
+ return set
}
// 转换为数组
@@ -93,7 +96,6 @@ func (set *IntSet) Slice() []int {
ret[i] = item
i++
}
-
set.mu.RUnlock()
return ret
}
@@ -103,14 +105,127 @@ func (set *IntSet) String() string {
return fmt.Sprint(set.Slice())
}
-func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
+func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
set.mu.Lock(true)
defer set.mu.Unlock(true)
f(set.m)
+ return set
}
-func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
+func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
set.mu.RLock(true)
defer set.mu.RUnlock(true)
f(set.m)
+ return set
+}
+
+// 判断两个集合是否相等.
+func (set *IntSet) Equal(other *IntSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range set.m {
+ if _, ok := other.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 判断other集合是否为当前集合的子集.
+func (set *IntSet) IsSubset(other *IntSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range other.m {
+ if _, ok := set.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 并集, 返回新的集合:属于set或属于other的元素为元素的集合.
+func (set *IntSet) Union(other *IntSet) (newSet *IntSet) {
+ newSet = NewIntSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ newSet.m[k] = v
+ }
+ if set != other {
+ for k, v := range other.m {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 差集, 返回新的集合: 属于set且不属于other的元素为元素的集合.
+func (set *IntSet) Diff(other *IntSet) (newSet *IntSet) {
+ newSet = NewIntSet(true)
+ if set == other {
+ return newSet
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+
+ for k, v := range set.m {
+ if _, ok := other.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 交集, 返回新的集合: 属于set且属于other的元素为元素的集合.
+func (set *IntSet) Inter(other *IntSet) (newSet *IntSet) {
+ newSet = NewIntSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ if _, ok := other.m[k]; ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
+func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
+ newSet = NewIntSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ full.mu.RLock()
+ defer full.mu.RUnlock()
+ for k, v := range full.m {
+ if _, ok := set.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
}
\ No newline at end of file
diff --git a/g/container/gset/gset_interface_set.go b/g/container/gset/gset_interface_set.go
index 0950898f8..20089e29c 100644
--- a/g/container/gset/gset_interface_set.go
+++ b/g/container/gset/gset_interface_set.go
@@ -25,7 +25,7 @@ func NewInterfaceSet(unsafe...bool) *InterfaceSet {
}
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
-func (set *InterfaceSet) Iterator(f func (v interface{}) bool) {
+func (set *InterfaceSet) Iterator(f func (v interface{}) bool) *InterfaceSet {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
@@ -33,6 +33,7 @@ func (set *InterfaceSet) Iterator(f func (v interface{}) bool) {
break
}
}
+ return set
}
// 添加
@@ -62,10 +63,11 @@ func (set *InterfaceSet) Contains(item interface{}) bool {
}
// 删除键值对
-func (set *InterfaceSet) Remove(key interface{}) {
+func (set *InterfaceSet) Remove(key interface{}) *InterfaceSet {
set.mu.Lock()
delete(set.m, key)
set.mu.Unlock()
+ return set
}
// 大小
@@ -77,10 +79,11 @@ func (set *InterfaceSet) Size() int {
}
// 清空set
-func (set *InterfaceSet) Clear() {
+func (set *InterfaceSet) Clear() *InterfaceSet {
set.mu.Lock()
set.m = make(map[interface{}]struct{})
set.mu.Unlock()
+ return set
}
// 转换为数组
@@ -101,14 +104,127 @@ func (set *InterfaceSet) String() string {
return fmt.Sprint(set.Slice())
}
-func (set *InterfaceSet) LockFunc(f func(m map[interface{}]struct{})) {
+func (set *InterfaceSet) LockFunc(f func(m map[interface{}]struct{})) *InterfaceSet {
set.mu.Lock(true)
defer set.mu.Unlock(true)
f(set.m)
+ return set
}
-func (set *InterfaceSet) RLockFunc(f func(m map[interface{}]struct{})) {
+func (set *InterfaceSet) RLockFunc(f func(m map[interface{}]struct{})) *InterfaceSet {
set.mu.RLock(true)
defer set.mu.RUnlock(true)
f(set.m)
+ return set
}
+
+// 判断两个集合是否相等.
+func (set *InterfaceSet) Equal(other *InterfaceSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range set.m {
+ if _, ok := other.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 判断other集合是否为当前集合的子集.
+func (set *InterfaceSet) IsSubset(other *InterfaceSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range other.m {
+ if _, ok := set.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 并集, 返回新的集合:属于set或属于other的元素为元素的集合.
+func (set *InterfaceSet) Union(other *InterfaceSet) (newSet *InterfaceSet) {
+ newSet = NewInterfaceSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ newSet.m[k] = v
+ }
+ if set != other {
+ for k, v := range other.m {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 差集, 返回新的集合: 属于set且不属于other的元素为元素的集合.
+func (set *InterfaceSet) Diff(other *InterfaceSet) (newSet *InterfaceSet) {
+ newSet = NewInterfaceSet(true)
+ if set == other {
+ return newSet
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+
+ for k, v := range set.m {
+ if _, ok := other.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 交集, 返回新的集合: 属于set且属于other的元素为元素的集合.
+func (set *InterfaceSet) Inter(other *InterfaceSet) (newSet *InterfaceSet) {
+ newSet = NewInterfaceSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ if _, ok := other.m[k]; ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
+func (set *InterfaceSet) Complement(full *InterfaceSet) (newSet *InterfaceSet) {
+ newSet = NewInterfaceSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ full.mu.RLock()
+ defer full.mu.RUnlock()
+ for k, v := range full.m {
+ if _, ok := set.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
\ No newline at end of file
diff --git a/g/container/gset/gset_string_set.go b/g/container/gset/gset_string_set.go
index 6aa7cde7f..329e475e5 100644
--- a/g/container/gset/gset_string_set.go
+++ b/g/container/gset/gset_string_set.go
@@ -25,7 +25,7 @@ func NewStringSet(unsafe...bool) *StringSet {
}
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
-func (set *StringSet) Iterator(f func (v string) bool) {
+func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
@@ -33,6 +33,7 @@ func (set *StringSet) Iterator(f func (v string) bool) {
break
}
}
+ return set
}
// 设置键
@@ -62,10 +63,11 @@ func (set *StringSet) Contains(item string) bool {
}
// 删除键值对
-func (set *StringSet) Remove(key string) {
+func (set *StringSet) Remove(key string) *StringSet {
set.mu.Lock()
delete(set.m, key)
set.mu.Unlock()
+ return set
}
// 大小
@@ -77,10 +79,11 @@ func (set *StringSet) Size() int {
}
// 清空set
-func (set *StringSet) Clear() {
+func (set *StringSet) Clear() *StringSet {
set.mu.Lock()
set.m = make(map[string]struct{})
set.mu.Unlock()
+ return set
}
// 转换为数组
@@ -102,14 +105,127 @@ func (set *StringSet) String() string {
return fmt.Sprint(set.Slice())
}
-func (set *StringSet) LockFunc(f func(m map[string]struct{})) {
+func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
set.mu.Lock(true)
defer set.mu.Unlock(true)
f(set.m)
+ return set
}
-func (set *StringSet) RLockFunc(f func(m map[string]struct{})) {
+func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
set.mu.RLock(true)
defer set.mu.RUnlock(true)
f(set.m)
+ return set
}
+
+// 判断两个集合是否相等.
+func (set *StringSet) Equal(other *StringSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range set.m {
+ if _, ok := other.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 判断other集合是否为当前集合的子集.
+func (set *StringSet) IsSubset(other *StringSet) bool {
+ if set == other {
+ return true
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ if len(set.m) != len(other.m) {
+ return false
+ }
+ for key := range other.m {
+ if _, ok := set.m[key]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// 并集, 返回新的集合:属于set或属于other的元素为元素的集合.
+func (set *StringSet) Union(other *StringSet) (newSet *StringSet) {
+ newSet = NewStringSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ newSet.m[k] = v
+ }
+ if set != other {
+ for k, v := range other.m {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 差集, 返回新的集合: 属于set且不属于other的元素为元素的集合.
+func (set *StringSet) Diff(other *StringSet) (newSet *StringSet) {
+ newSet = NewStringSet(true)
+ if set == other {
+ return newSet
+ }
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+
+ for k, v := range set.m {
+ if _, ok := other.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 交集, 返回新的集合: 属于set且属于other的元素为元素的集合.
+func (set *StringSet) Inter(other *StringSet) (newSet *StringSet) {
+ newSet = NewStringSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ if set != other {
+ other.mu.RLock()
+ defer other.mu.RUnlock()
+ }
+ for k, v := range set.m {
+ if _, ok := other.m[k]; ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
+
+// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
+func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
+ newSet = NewStringSet(true)
+ set.mu.RLock()
+ defer set.mu.RUnlock()
+ full.mu.RLock()
+ defer full.mu.RUnlock()
+ for k, v := range full.m {
+ if _, ok := set.m[k]; !ok {
+ newSet.m[k] = v
+ }
+ }
+ return
+}
\ No newline at end of file
diff --git a/g/container/gset/gset_test.go b/g/container/gset/gset_z_bench_test.go
similarity index 100%
rename from g/container/gset/gset_test.go
rename to g/container/gset/gset_z_bench_test.go
diff --git a/g/container/gset/gset_unsafe_test.go b/g/container/gset/gset_z_bench_unsafe_test.go
similarity index 100%
rename from g/container/gset/gset_unsafe_test.go
rename to g/container/gset/gset_z_bench_unsafe_test.go
diff --git a/g/container/gset/gset_z_unit_test.go b/g/container/gset/gset_z_unit_test.go
new file mode 100644
index 000000000..c6ea1eb8f
--- /dev/null
+++ b/g/container/gset/gset_z_unit_test.go
@@ -0,0 +1,25 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+// go test *.go
+
+package gset_test
+
+import (
+ "gitee.com/johng/gf/g/container/gset"
+ "gitee.com/johng/gf/g/test/gtest"
+ "testing"
+)
+
+func TestIntSet_Basic(t *testing.T) {
+ gtest.Case(t, func() {
+ s := gset.NewIntSet()
+ s.Add(1).Add(1).Add(2)
+ s.BatchAdd([]int{3,4})
+ gtest.Assert(s.Size(), 3)
+ gtest.Assert(s.Contains(4), true)
+ })
+}
diff --git a/g/crypto/gdes/gdes.go b/g/crypto/gdes/gdes.go
index e60645c4f..b50289342 100644
--- a/g/crypto/gdes/gdes.go
+++ b/g/crypto/gdes/gdes.go
@@ -262,14 +262,14 @@ func PKCS5Unpadding(text []byte) []byte{
//补位方法
func Padding(text []byte, padding int)([]byte, error) {
switch padding {
- case NOPADDING:
- if len(text) % 8 != 0 {
- return nil, errors.New("text length invalid")
- }
- case PKCS5PADDING:
- return PKCS5Padding(text, 8), nil
- default:
- return nil, errors.New("padding type error")
+ case NOPADDING:
+ if len(text) % 8 != 0 {
+ return nil, errors.New("text length invalid")
+ }
+ case PKCS5PADDING:
+ return PKCS5Padding(text, 8), nil
+ default:
+ return nil, errors.New("padding type error")
}
return text, nil
@@ -278,14 +278,14 @@ func Padding(text []byte, padding int)([]byte, error) {
//去除补位方法
func UnPadding(text []byte, padding int)([]byte, error) {
switch padding {
- case NOPADDING:
- if len(text) % 8 != 0 {
- return nil, errors.New("text length invalid")
- }
- case PKCS5PADDING:
- return PKCS5Unpadding(text), nil
- default:
- return nil, errors.New("padding type error.")
+ case NOPADDING:
+ if len(text) % 8 != 0 {
+ return nil, errors.New("text length invalid")
+ }
+ case PKCS5PADDING:
+ return PKCS5Unpadding(text), nil
+ default:
+ return nil, errors.New("padding type error.")
}
return text, nil
}
\ No newline at end of file
diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go
index 4dc3cbde1..b233fdb77 100644
--- a/g/crypto/gsha1/gsha1.go
+++ b/g/crypto/gsha1/gsha1.go
@@ -34,6 +34,7 @@ func EncryptFile(path string) string {
if e != nil {
return ""
}
+ defer f.Close()
h := sha1.New()
_, e = io.Copy(h, f)
if e != nil {
diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go
index f5d78e19c..641755fcd 100644
--- a/g/database/gdb/gdb_base.go
+++ b/g/database/gdb/gdb_base.go
@@ -15,7 +15,7 @@ import (
"gitee.com/johng/gf/g/os/gcache"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"reflect"
"strings"
)
diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go
index 9a200b50a..16638e549 100644
--- a/g/database/gdb/gdb_func.go
+++ b/g/database/gdb/gdb_func.go
@@ -13,8 +13,8 @@ import (
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gregex"
+ "gitee.com/johng/gf/g/string/gstr"
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
"reflect"
"strings"
diff --git a/g/database/gdb/gdb_mssql.go b/g/database/gdb/gdb_mssql.go
index 70187a585..5c6e732d2 100644
--- a/g/database/gdb/gdb_mssql.go
+++ b/g/database/gdb/gdb_mssql.go
@@ -16,7 +16,7 @@ package gdb
import (
"database/sql"
"fmt"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strconv"
"strings"
)
diff --git a/g/database/gdb/gdb_oracle.go b/g/database/gdb/gdb_oracle.go
index d6d9873ec..2a5312c7c 100644
--- a/g/database/gdb/gdb_oracle.go
+++ b/g/database/gdb/gdb_oracle.go
@@ -16,7 +16,7 @@ package gdb
import (
"database/sql"
"fmt"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strconv"
"strings"
)
diff --git a/g/database/gdb/gdb_structure.go b/g/database/gdb/gdb_structure.go
index d5c9356b7..265b213cf 100644
--- a/g/database/gdb/gdb_structure.go
+++ b/g/database/gdb/gdb_structure.go
@@ -10,7 +10,7 @@ package gdb
import (
"fmt"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strings"
)
diff --git a/g/database/gdb/gdb_transaction.go b/g/database/gdb/gdb_transaction.go
index a1d28cfe4..b7d9ef7cc 100644
--- a/g/database/gdb/gdb_transaction.go
+++ b/g/database/gdb/gdb_transaction.go
@@ -8,7 +8,7 @@ package gdb
import (
"database/sql"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
)
diff --git a/g/database/gdb/gdb_unit_0_test.go b/g/database/gdb/gdb_unit_0_test.go
index 017a6633f..878250091 100644
--- a/g/database/gdb/gdb_unit_0_test.go
+++ b/g/database/gdb/gdb_unit_0_test.go
@@ -2,7 +2,7 @@ package gdb_test
import (
"gitee.com/johng/gf/g/database/gdb"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
)
var (
diff --git a/g/database/gdb/gdb_unit_1_test.go b/g/database/gdb/gdb_unit_1_test.go
index 8e3bd979f..88f99e12e 100644
--- a/g/database/gdb/gdb_unit_1_test.go
+++ b/g/database/gdb/gdb_unit_1_test.go
@@ -3,7 +3,7 @@ package gdb_test
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/database/gdb/gdb_unit_2_test.go b/g/database/gdb/gdb_unit_2_test.go
index c63f3626b..d8ad54be9 100644
--- a/g/database/gdb/gdb_unit_2_test.go
+++ b/g/database/gdb/gdb_unit_2_test.go
@@ -3,7 +3,7 @@ package gdb_test
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/database/gdb/gdb_unit_3_test.go b/g/database/gdb/gdb_unit_3_test.go
index 3180c906d..4526163e0 100644
--- a/g/database/gdb/gdb_unit_3_test.go
+++ b/g/database/gdb/gdb_unit_3_test.go
@@ -3,7 +3,7 @@ package gdb_test
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go
index dabb85548..b2fb5e7e1 100644
--- a/g/encoding/gjson/gjson.go
+++ b/g/encoding/gjson/gjson.go
@@ -9,7 +9,7 @@ package gjson
import (
"errors"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strings"
"strconv"
"io/ioutil"
@@ -19,7 +19,7 @@ import (
"gitee.com/johng/gf/g/encoding/gxml"
"gitee.com/johng/gf/g/encoding/gyaml"
"gitee.com/johng/gf/g/encoding/gtoml"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"time"
"gitee.com/johng/gf/g/internal/rwmutex"
"fmt"
diff --git a/g/encoding/gurl/url.go b/g/encoding/gurl/url.go
index 65bf2eea4..929c4b97a 100644
--- a/g/encoding/gurl/url.go
+++ b/g/encoding/gurl/url.go
@@ -7,7 +7,10 @@
// Package gurl provides useful API for URL handling.
package gurl
-import "net/url"
+import (
+ "net/url"
+ "strings"
+)
// url encode string, is + not %20
func Encode(str string) string {
@@ -18,3 +21,60 @@ func Encode(str string) string {
func Decode(str string) (string, error) {
return url.QueryUnescape(str)
}
+
+// URL-encode according to RFC 3986.
+// See http://php.net/manual/en/function.rawurlencode.php.
+func RawEncode(str string) string {
+ return strings.Replace(url.QueryEscape(str), "+", "%20", -1)
+}
+
+// Decode URL-encoded strings.
+// See http://php.net/manual/en/function.rawurldecode.php.
+func RawDecode(str string) (string, error) {
+ return url.QueryUnescape(strings.Replace(str, "%20", "+", -1))
+}
+
+// Generate URL-encoded query string.
+// See http://php.net/manual/en/function.http-build-query.php.
+func BuildQuery(queryData url.Values) string {
+ return queryData.Encode()
+}
+
+// Parse a URL and return its components.
+// -1: all; 1: scheme; 2: host; 4: port; 8: user; 16: pass; 32: path; 64: query; 128: fragment.
+// See http://php.net/manual/en/function.parse-url.php.
+func ParseURL(str string, component int) (map[string]string, error) {
+ u, err := url.Parse(str)
+ if err != nil {
+ return nil, err
+ }
+ if component == -1 {
+ component = 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128
+ }
+ var components = make(map[string]string)
+ if (component & 1) == 1 {
+ components["scheme"] = u.Scheme
+ }
+ if (component & 2) == 2 {
+ components["host"] = u.Hostname()
+ }
+ if (component & 4) == 4 {
+ components["port"] = u.Port()
+ }
+ if (component & 8) == 8 {
+ components["user"] = u.User.Username()
+ }
+ if (component & 16) == 16 {
+ components["pass"], _ = u.User.Password()
+ }
+ if (component & 32) == 32 {
+ components["path"] = u.Path
+ }
+ if (component & 64) == 64 {
+ components["query"] = u.RawQuery
+ }
+ if (component & 128) == 128 {
+ components["fragment"] = u.Fragment
+ }
+ return components, nil
+}
diff --git a/g/encoding/gxml/gxml.go b/g/encoding/gxml/gxml.go
index 351aa838f..39d547206 100644
--- a/g/encoding/gxml/gxml.go
+++ b/g/encoding/gxml/gxml.go
@@ -11,7 +11,7 @@ import (
"gitee.com/johng/gf/third/github.com/clbanning/mxj"
"encoding/xml"
"io"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"gitee.com/johng/gf/third/github.com/axgle/mahonia"
"errors"
"fmt"
diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go
index 45247785b..6c9b275fb 100644
--- a/g/frame/gins/gins.go
+++ b/g/frame/gins/gins.go
@@ -22,7 +22,7 @@ import (
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
)
const (
diff --git a/g/net/ghttp/ghttp_client_request_client.go b/g/net/ghttp/ghttp_client_request_client.go
index 410392fdd..607cf75ed 100644
--- a/g/net/ghttp/ghttp_client_request_client.go
+++ b/g/net/ghttp/ghttp_client_request_client.go
@@ -9,7 +9,7 @@
package ghttp
import (
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"time"
"bytes"
"strings"
diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go
index 2ce2368e4..552890ac1 100644
--- a/g/net/ghttp/ghttp_request.go
+++ b/g/net/ghttp/ghttp_request.go
@@ -10,7 +10,7 @@ import (
"gitee.com/johng/gf/g/container/gvar"
"gitee.com/johng/gf/g/encoding/gjson"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"gitee.com/johng/gf/third/github.com/fatih/structs"
"io/ioutil"
"net/http"
diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go
index 311fefe9d..6b6938e22 100644
--- a/g/net/ghttp/ghttp_server.go
+++ b/g/net/ghttp/ghttp_server.go
@@ -20,7 +20,7 @@ import (
"gitee.com/johng/gf/g/os/gproc"
"gitee.com/johng/gf/g/os/gtimer"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"gitee.com/johng/gf/third/github.com/gorilla/websocket"
"gitee.com/johng/gf/third/github.com/olekukonko/tablewriter"
"net/http"
diff --git a/g/net/ghttp/ghttp_server_router.go b/g/net/ghttp/ghttp_server_router.go
index 5e3cc5e9b..b2405b9ce 100644
--- a/g/net/ghttp/ghttp_server_router.go
+++ b/g/net/ghttp/ghttp_server_router.go
@@ -12,8 +12,8 @@ import (
"errors"
"fmt"
"gitee.com/johng/gf/g/os/glog"
- "gitee.com/johng/gf/g/util/gregex"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gregex"
+ "gitee.com/johng/gf/g/string/gstr"
"runtime"
"strings"
)
diff --git a/g/net/ghttp/ghttp_server_router_hook.go b/g/net/ghttp/ghttp_server_router_hook.go
index dac1cd2d3..bf9d333a3 100644
--- a/g/net/ghttp/ghttp_server_router_hook.go
+++ b/g/net/ghttp/ghttp_server_router_hook.go
@@ -11,7 +11,7 @@ import (
"container/list"
"fmt"
"gitee.com/johng/gf/g/container/gset"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"reflect"
"runtime"
"strings"
diff --git a/g/net/ghttp/ghttp_server_router_serve.go b/g/net/ghttp/ghttp_server_router_serve.go
index 4e746a952..2445aab2d 100644
--- a/g/net/ghttp/ghttp_server_router_serve.go
+++ b/g/net/ghttp/ghttp_server_router_serve.go
@@ -10,7 +10,7 @@ package ghttp
import (
"strings"
"container/list"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
)
// 查询请求处理方法.
diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go
index e20fddb94..5e6ceedb8 100644
--- a/g/net/ghttp/ghttp_server_service_controller.go
+++ b/g/net/ghttp/ghttp_server_service_controller.go
@@ -14,7 +14,7 @@ import (
"reflect"
"fmt"
"gitee.com/johng/gf/g/os/gfile"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
)
// 绑定控制器,控制器需要实现gmvc.Controller接口
diff --git a/g/net/ghttp/ghttp_server_service_handler.go b/g/net/ghttp/ghttp_server_service_handler.go
index 2fd1ff3f5..c45b8beb9 100644
--- a/g/net/ghttp/ghttp_server_service_handler.go
+++ b/g/net/ghttp/ghttp_server_service_handler.go
@@ -10,7 +10,7 @@ package ghttp
import (
"errors"
"strings"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"bytes"
"runtime"
"reflect"
diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go
index ef64df4a1..ce6eb6451 100644
--- a/g/net/ghttp/ghttp_server_service_object.go
+++ b/g/net/ghttp/ghttp_server_service_object.go
@@ -13,7 +13,7 @@ import (
"strings"
"reflect"
"fmt"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"gitee.com/johng/gf/g/os/gfile"
)
diff --git a/g/net/ghttp/ghttp_unit_1_test.go b/g/net/ghttp/ghttp_unit_1_test.go
index e8251e717..de6b66a05 100644
--- a/g/net/ghttp/ghttp_unit_1_test.go
+++ b/g/net/ghttp/ghttp_unit_1_test.go
@@ -11,7 +11,7 @@ import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/net/ghttp/ghttp_unit_2_test.go b/g/net/ghttp/ghttp_unit_2_test.go
index 76afbca89..eafdfee04 100644
--- a/g/net/ghttp/ghttp_unit_2_test.go
+++ b/g/net/ghttp/ghttp_unit_2_test.go
@@ -12,7 +12,7 @@ import (
"gitee.com/johng/gf/g/frame/gmvc"
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/net/ghttp/ghttp_unit_3_test.go b/g/net/ghttp/ghttp_unit_3_test.go
index 3543c5d97..3ad93d7b3 100644
--- a/g/net/ghttp/ghttp_unit_3_test.go
+++ b/g/net/ghttp/ghttp_unit_3_test.go
@@ -11,7 +11,7 @@ import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/net/gipv4/gipv4.go b/g/net/gipv4/gipv4.go
index 4aeeb70e1..302502f6b 100644
--- a/g/net/gipv4/gipv4.go
+++ b/g/net/gipv4/gipv4.go
@@ -9,12 +9,13 @@
package gipv4
import (
+ "encoding/binary"
"net"
"strconv"
"strings"
"regexp"
"fmt"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
)
// 判断所给地址是否是一个IPv4地址
@@ -22,33 +23,58 @@ func Validate(ip string) bool {
return gregex.IsMatchString(`^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$`, ip)
}
-// ip字符串转为整形
-func Ip2long(ipstr string) (ip uint32) {
- reg, _ := regexp.Compile(`^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`)
- ips := reg.FindStringSubmatch(ipstr)
- if ips == nil {
- return
+// Get the IPv4 address corresponding to a given Internet host name.
+func GetHostByName(hostname string) (string, error) {
+ ips, err := net.LookupIP(hostname)
+ if ips != nil {
+ for _, v := range ips {
+ if v.To4() != nil {
+ return v.String(), nil
+ }
+ }
+ return "", nil
}
+ return "", err
+}
- ip1, _ := strconv.Atoi(ips[1])
- ip2, _ := strconv.Atoi(ips[2])
- ip3, _ := strconv.Atoi(ips[3])
- ip4, _ := strconv.Atoi(ips[4])
-
- if ip1>255 || ip2>255 || ip3>255 || ip4 > 255 {
- return
+// Get a list of IPv4 addresses corresponding to a given Internet host name.
+func GetHostsByName(hostname string) ([]string, error) {
+ ips, err := net.LookupIP(hostname)
+ if ips != nil {
+ var ipStrs []string
+ for _, v := range ips {
+ if v.To4() != nil {
+ ipStrs = append(ipStrs, v.String())
+ }
+ }
+ return ipStrs, nil
}
+ return nil, err
+}
- ip += uint32(ip1 * 0x1000000)
- ip += uint32(ip2 * 0x10000)
- ip += uint32(ip3 * 0x100)
- ip += uint32(ip4)
- return
+// Get the Internet host name corresponding to a given IP address.
+func GetNameByAddr(ipAddress string) (string, error) {
+ names, err := net.LookupAddr(ipAddress)
+ if names != nil {
+ return strings.TrimRight(names[0], "."), nil
+ }
+ return "", err
+}
+
+// IP字符串转为整形.
+func Ip2long(ipAddress string) uint32 {
+ ip := net.ParseIP(ipAddress)
+ if ip == nil {
+ return 0
+ }
+ return binary.BigEndian.Uint32(ip.To4())
}
// ip整形转为字符串
-func Long2ip(ip uint32) string {
- return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
+func Long2ip(properAddress uint32) string {
+ ipByte := make([]byte, 4)
+ binary.BigEndian.PutUint32(ipByte, properAddress)
+ return net.IP(ipByte).String()
}
// 获得ip的网段,例如:192.168.2.102 -> 192.168.2
diff --git a/g/net/gipv6/gipv6.go b/g/net/gipv6/gipv6.go
index 58bab6af3..055f5e027 100644
--- a/g/net/gipv6/gipv6.go
+++ b/g/net/gipv6/gipv6.go
@@ -7,7 +7,7 @@
// Package gipv4 provides useful API for IPv6 address handling.
package gipv6
-import "gitee.com/johng/gf/g/util/gregex"
+import "gitee.com/johng/gf/g/string/gregex"
// 判断所给地址是否是一个IPv6地址
func Validate(ip string) bool {
diff --git a/g/os/gcache/gcache_z_unit_1_test.go b/g/os/gcache/gcache_z_unit_1_test.go
index 74de36619..f978a2939 100644
--- a/g/os/gcache/gcache_z_unit_1_test.go
+++ b/g/os/gcache/gcache_z_unit_1_test.go
@@ -10,7 +10,7 @@ package gcache_test
import (
"gitee.com/johng/gf/g/os/gcache"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gcron/gcron_schedule.go b/g/os/gcron/gcron_schedule.go
index a9f3abc54..35abe2b64 100644
--- a/g/os/gcron/gcron_schedule.go
+++ b/g/os/gcron/gcron_schedule.go
@@ -9,7 +9,7 @@ package gcron
import (
"errors"
"fmt"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strconv"
"strings"
"time"
diff --git a/g/os/gcron/gcron_unit_1_test.go b/g/os/gcron/gcron_unit_1_test.go
index 5191ba604..8a43e5aa9 100644
--- a/g/os/gcron/gcron_unit_1_test.go
+++ b/g/os/gcron/gcron_unit_1_test.go
@@ -10,7 +10,7 @@ package gcron_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gcron"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gcron/gcron_unit_2_test.go b/g/os/gcron/gcron_unit_2_test.go
index 0bdef20ad..01d5f07ff 100644
--- a/g/os/gcron/gcron_unit_2_test.go
+++ b/g/os/gcron/gcron_unit_2_test.go
@@ -11,7 +11,7 @@ import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gcron"
"gitee.com/johng/gf/g/os/glog"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go
index e98354552..fba5dd3a6 100644
--- a/g/os/gfile/gfile.go
+++ b/g/os/gfile/gfile.go
@@ -15,8 +15,8 @@ import (
"fmt"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gregex"
+ "gitee.com/johng/gf/g/string/gstr"
"io"
"os"
"os/exec"
@@ -202,7 +202,7 @@ func Remove(path string) error {
return os.RemoveAll(path)
}
-// 文件是否可读
+// 文件是否可读(支持文件/目录)
func IsReadable(path string) bool {
result := true
file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM)
@@ -213,17 +213,17 @@ func IsReadable(path string) bool {
return result
}
-// 文件是否可写
+// 文件是否可写(支持文件/目录)
func IsWritable(path string) bool {
result := true
if IsDir(path) {
// 如果是目录,那么创建一个临时文件进行写入测试
- tfile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
- err := Create(tfile)
- if err != nil || !Exists(tfile){
+ tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
+ err := Create(tmpFile)
+ if err != nil || !Exists(tmpFile){
result = false
} else {
- Remove(tfile)
+ Remove(tmpFile)
}
} else {
// 如果是文件,那么判断文件是否可打开
@@ -421,4 +421,4 @@ func MainPkgPath() string {
// 系统临时目录
func TempDir() string {
return os.TempDir()
-}
\ No newline at end of file
+}
diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go
index 2d8e4fe37..2e273b3c5 100644
--- a/g/os/glog/glog_logger.go
+++ b/g/os/glog/glog_logger.go
@@ -14,7 +14,7 @@ import (
"gitee.com/johng/gf/g/os/gfpool"
"gitee.com/johng/gf/g/os/gmlock"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"io"
"os"
"runtime"
diff --git a/g/os/gmlock/gmlock_unit_lock_test.go b/g/os/gmlock/gmlock_unit_lock_test.go
index ced3a201a..553061de7 100644
--- a/g/os/gmlock/gmlock_unit_lock_test.go
+++ b/g/os/gmlock/gmlock_unit_lock_test.go
@@ -9,7 +9,7 @@ package gmlock_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gmlock"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gmlock/gmlock_unit_rlock_test.go b/g/os/gmlock/gmlock_unit_rlock_test.go
index ffd210868..1d8bfe905 100644
--- a/g/os/gmlock/gmlock_unit_rlock_test.go
+++ b/g/os/gmlock/gmlock_unit_rlock_test.go
@@ -9,7 +9,7 @@ package gmlock_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gmlock"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gspath/gspath.go b/g/os/gspath/gspath.go
index da5ffff01..fa228acfe 100644
--- a/g/os/gspath/gspath.go
+++ b/g/os/gspath/gspath.go
@@ -17,7 +17,7 @@ import (
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gfsnotify"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"runtime"
"sort"
"strings"
diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go
index 4b7cef60e..5d6df5486 100644
--- a/g/os/gtime/gtime.go
+++ b/g/os/gtime/gtime.go
@@ -11,8 +11,8 @@ package gtime
import (
"errors"
- "gitee.com/johng/gf/g/util/gregex"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gregex"
+ "gitee.com/johng/gf/g/string/gstr"
"regexp"
"strconv"
"strings"
diff --git a/g/os/gtime/gtime_format.go b/g/os/gtime/gtime_format.go
index a0c9a9366..4be19ca0f 100644
--- a/g/os/gtime/gtime_format.go
+++ b/g/os/gtime/gtime_format.go
@@ -8,7 +8,7 @@ package gtime
import (
"bytes"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strings"
)
diff --git a/g/os/gtimer/gtimer_z_unit_0_test.go b/g/os/gtimer/gtimer_z_unit_0_test.go
index 09aa7c0f8..d7db1db2e 100644
--- a/g/os/gtimer/gtimer_z_unit_0_test.go
+++ b/g/os/gtimer/gtimer_z_unit_0_test.go
@@ -11,7 +11,7 @@ package gtimer_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gtimer"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gtimer/gtimer_z_unit_1_test.go b/g/os/gtimer/gtimer_z_unit_1_test.go
index 15779a4cd..e992ba22a 100644
--- a/g/os/gtimer/gtimer_z_unit_1_test.go
+++ b/g/os/gtimer/gtimer_z_unit_1_test.go
@@ -11,7 +11,7 @@ package gtimer_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gtimer"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gtimer/gtimer_z_unit_2_test.go b/g/os/gtimer/gtimer_z_unit_2_test.go
index 41a9393bb..f625f6c40 100644
--- a/g/os/gtimer/gtimer_z_unit_2_test.go
+++ b/g/os/gtimer/gtimer_z_unit_2_test.go
@@ -11,7 +11,7 @@ package gtimer_test
import (
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gtimer"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go
index 0b379cc13..8affbb46c 100644
--- a/g/os/gview/gview.go
+++ b/g/os/gview/gview.go
@@ -25,7 +25,7 @@ import (
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/os/gview/internal/text/template"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"strings"
"sync"
)
diff --git a/g/util/gregex/gregex.go b/g/string/gregex/gregex.go
similarity index 100%
rename from g/util/gregex/gregex.go
rename to g/string/gregex/gregex.go
diff --git a/g/util/gregex/gregex_test.go b/g/string/gregex/gregex_z_bench_test.go
similarity index 100%
rename from g/util/gregex/gregex_test.go
rename to g/string/gregex/gregex_z_bench_test.go
diff --git a/g/string/gstr/gstr.go b/g/string/gstr/gstr.go
new file mode 100644
index 000000000..432b2a202
--- /dev/null
+++ b/g/string/gstr/gstr.go
@@ -0,0 +1,576 @@
+// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
+
+// Package gstr provides useful API for string handling.
+//
+// 字符串操作.
+package gstr
+
+import (
+ "bytes"
+ "fmt"
+ "gitee.com/johng/gf/g/util/grand"
+ "math"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// 字符串替换(大小写敏感)
+func Replace(origin, search, replace string, count...int) string {
+ n := -1
+ if len(count) > 0 {
+ n = count[0]
+ }
+ return strings.Replace(origin, search, replace, n)
+}
+
+// 使用map进行字符串替换(大小写敏感)
+func ReplaceByMap(origin string, replaces map[string]string) string {
+ result := origin
+ for k, v := range replaces {
+ result = strings.Replace(result, k, v, -1)
+ }
+ return result
+}
+
+// 字符串转换为小写
+func ToLower(s string) string {
+ return strings.ToLower(s)
+}
+
+// 字符串转换为大写
+func ToUpper(s string) string {
+ return strings.ToUpper(s)
+}
+
+// 字符串首字母转换为大写
+func UcFirst(s string) string {
+ if len(s) == 0 {
+ return s
+ }
+ if IsLetterLower(s[0]) {
+ return string(s[0] - 32) + s[1 :]
+ }
+ return s
+}
+
+// 字符串首字母转换为小写
+func LcFirst(s string) string {
+ if len(s) == 0 {
+ return s
+ }
+ if IsLetterUpper(s[0]) {
+ return string(s[0] + 32) + s[1 :]
+ }
+ return s
+}
+
+// Uppercase the first character of each word in a string.
+func UcWords(str string) string {
+ return strings.Title(str)
+}
+
+// 便利数组查找字符串索引位置,如果不存在则返回-1,使用完整遍历查找
+func SearchArray (a []string, s string) int {
+ for i, v := range a {
+ if s == v {
+ return i
+ }
+ }
+ return -1
+}
+
+// 判断字符串是否在数组中
+func InArray (a []string, s string) bool {
+ return SearchArray(a, s) != -1
+}
+
+// 判断给定字符是否小写
+func IsLetterLower(b byte) bool {
+ if b >= byte('a') && b <= byte('z') {
+ return true
+ }
+ return false
+}
+
+// 判断给定字符是否大写
+func IsLetterUpper(b byte) bool {
+ if b >= byte('A') && b <= byte('Z') {
+ return true
+ }
+ return false
+}
+
+// 判断锁给字符串是否为数字
+func IsNumeric(s string) bool {
+ length := len(s)
+ if length == 0 {
+ return false
+ }
+ for i := 0; i < len(s); i++ {
+ if s[i] < byte('0') || s[i] > byte('9') {
+ return false
+ }
+ }
+ return true
+}
+
+// 字符串截取,支持中文
+func SubStr(str string, start int, length...int) (substr string) {
+ // 将字符串的转换成[]rune
+ rs := []rune(str)
+ lth := len(rs)
+ // 简单的越界判断
+ if start < 0 {
+ start = 0
+ }
+ if start >= lth {
+ start = lth
+ }
+ end := lth
+ if len(length) > 0 {
+ end = start + length[0]
+ if end < start {
+ end = lth
+ }
+ }
+ if end > lth {
+ end = lth
+ }
+ // 返回子串
+ return string(rs[start : end])
+}
+
+// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文
+func StrLimit(str string, length int, suffix...string) (string) {
+ rs := []rune(str)
+ if len(str) < length {
+ return str
+ }
+ addStr := "..."
+ if len(suffix) > 0 {
+ addStr = suffix[0]
+ }
+ return string(rs[0 : length]) + addStr
+}
+
+// Reverse a string.
+func Reverse(str string) string {
+ runes := []rune(str)
+ for i, j := 0, len(runes) - 1; i < j; i, j = i + 1, j - 1 {
+ runes[i], runes[j] = runes[j], runes[i]
+ }
+ return string(runes)
+}
+
+// Format a number with grouped thousands.
+// decimals: Sets the number of decimal points.
+// decPoint: Sets the separator for the decimal point.
+// thousandsSep: Sets the thousands separator.
+func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string {
+ neg := false
+ if number < 0 {
+ number = -number
+ neg = true
+ }
+ // Will round off
+ str := fmt.Sprintf("%."+strconv.Itoa(decimals)+"F", number)
+ prefix, suffix := "", ""
+ if decimals > 0 {
+ prefix = str[ : len(str) - (decimals + 1)]
+ suffix = str[len(str) - decimals : ]
+ } else {
+ prefix = str
+ }
+ sep := []byte(thousandsSep)
+ n, l1, l2 := 0, len(prefix), len(sep)
+ // thousands sep num
+ c := (l1 - 1) / 3
+ tmp := make([]byte, l2*c+l1)
+ pos := len(tmp) - 1
+ for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 {
+ if l2 > 0 && n > 0 && n%3 == 0 {
+ for j := range sep {
+ tmp[pos] = sep[l2-j-1]
+ pos--
+ }
+ }
+ tmp[pos] = prefix[i]
+ }
+ s := string(tmp)
+ if decimals > 0 {
+ s += decPoint + suffix
+ }
+ if neg {
+ s = "-" + s
+ }
+
+ return s
+}
+
+// Split a string into smaller chunks.
+func ChunkSplit(body string, chunkLen uint, end string) string {
+ if end == "" {
+ end = "\r\n"
+ }
+ runes, endRunes := []rune(body), []rune(end)
+ l := uint(len(runes))
+ if l <= 1 || l < chunkLen {
+ return body + end
+ }
+ ns := make([]rune, 0, len(runes) + len(endRunes))
+ var i uint
+ for i = 0; i < l; i += chunkLen {
+ if i + chunkLen > l {
+ ns = append(ns, runes[i : ]...)
+ } else {
+ ns = append(ns, runes[i : i + chunkLen]...)
+ }
+ ns = append(ns, endRunes...)
+ }
+ return string(ns)
+}
+
+// Return information about words used in a string.
+func WordCount(str string) []string {
+ return strings.Fields(str)
+}
+
+// Wraps a string to a given number of characters.
+func WordWrap(str string, width uint, br string) string {
+ if br == "" {
+ br = "\n"
+ }
+ init := make([]byte, 0, len(str))
+ buf := bytes.NewBuffer(init)
+ var current uint
+ var wordBuf, spaceBuf bytes.Buffer
+ for _, char := range str {
+ if char == '\n' {
+ if wordBuf.Len() == 0 {
+ if current+uint(spaceBuf.Len()) > width {
+ current = 0
+ } else {
+ current += uint(spaceBuf.Len())
+ spaceBuf.WriteTo(buf)
+ }
+ spaceBuf.Reset()
+ } else {
+ current += uint(spaceBuf.Len() + wordBuf.Len())
+ spaceBuf.WriteTo(buf)
+ spaceBuf.Reset()
+ wordBuf.WriteTo(buf)
+ wordBuf.Reset()
+ }
+ buf.WriteRune(char)
+ current = 0
+ } else if unicode.IsSpace(char) {
+ if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
+ current += uint(spaceBuf.Len() + wordBuf.Len())
+ spaceBuf.WriteTo(buf)
+ spaceBuf.Reset()
+ wordBuf.WriteTo(buf)
+ wordBuf.Reset()
+ }
+ spaceBuf.WriteRune(char)
+ } else {
+ wordBuf.WriteRune(char)
+ if current+uint(spaceBuf.Len()+wordBuf.Len()) > width && uint(wordBuf.Len()) < width {
+ buf.WriteString(br)
+ current = 0
+ spaceBuf.Reset()
+ }
+ }
+ }
+
+ if wordBuf.Len() == 0 {
+ if current+uint(spaceBuf.Len()) <= width {
+ spaceBuf.WriteTo(buf)
+ }
+ } else {
+ spaceBuf.WriteTo(buf)
+ wordBuf.WriteTo(buf)
+ }
+ return buf.String()
+}
+
+// Get string length of unicode.
+func RuneLen(str string) int {
+ return utf8.RuneCountInString(str)
+}
+
+// Repeat a string.
+func Repeat(input string, multiplier int) string {
+ return strings.Repeat(input, multiplier)
+}
+
+// Returns part of haystack string starting from and including the first occurrence of needle to the end of haystack.
+// See http://php.net/manual/en/function.strstr.php.
+func Str(haystack string, needle string) string {
+ if needle == "" {
+ return ""
+ }
+ idx := strings.Index(haystack, needle)
+ if idx == -1 {
+ return ""
+ }
+ return haystack[idx + len([]byte(needle)) - 1 : ]
+}
+
+// Translate characters or replace substrings.
+//
+// If the params length is 1, type is: map[string]string
+// Tr("baab", map[string]string{"ab": "01"}) will return "ba01".
+// If the params length is 2, type is: string, string
+// Tr("baab", "ab", "01") will return "1001", a => 0; b => 1.
+func Tr(haystack string, params ...interface{}) string {
+ ac := len(params)
+ if ac == 1 {
+ pairs := params[0].(map[string]string)
+ length := len(pairs)
+ if length == 0 {
+ return haystack
+ }
+ oldnew := make([]string, length*2)
+ for o, n := range pairs {
+ if o == "" {
+ return haystack
+ }
+ oldnew = append(oldnew, o, n)
+ }
+ return strings.NewReplacer(oldnew...).Replace(haystack)
+ } else if ac == 2 {
+ from := params[0].(string)
+ to := params[1].(string)
+ trlen, lt := len(from), len(to)
+ if trlen > lt {
+ trlen = lt
+ }
+ if trlen == 0 {
+ return haystack
+ }
+
+ str := make([]uint8, len(haystack))
+ var xlat [256]uint8
+ var i int
+ var j uint8
+ if trlen == 1 {
+ for i = 0; i < len(haystack); i++ {
+ if haystack[i] == from[0] {
+ str[i] = to[0]
+ } else {
+ str[i] = haystack[i]
+ }
+ }
+ return string(str)
+ }
+ // trlen != 1
+ for {
+ xlat[j] = j
+ if j++; j == 0 {
+ break
+ }
+ }
+ for i = 0; i < trlen; i++ {
+ xlat[from[i]] = to[i]
+ }
+ for i = 0; i < len(haystack); i++ {
+ str[i] = xlat[haystack[i]]
+ }
+ return string(str)
+ }
+
+ return haystack
+}
+
+// Randomly shuffles a string.
+func Shuffle(str string) string {
+ runes := []rune(str)
+ s := make([]rune, len(runes))
+ for i, v := range grand.Perm(len(runes)) {
+ s[i] = runes[v]
+ }
+ return string(s)
+}
+
+// Strip whitespace (or other characters) from the beginning and end of a string.
+func Trim(str string, characterMask ...string) string {
+ mask := ""
+ if len(characterMask) == 0 {
+ mask = " \\t\\n\\r\\0\\x0B"
+ } else {
+ mask = characterMask[0]
+ }
+ return strings.Trim(str, mask)
+}
+
+// Strip whitespace (or other characters) from the beginning of a string.
+func TrimLeft(str string, characterMask ...string) string {
+ mask := ""
+ if len(characterMask) == 0 {
+ mask = " \\t\\n\\r\\0\\x0B"
+ } else {
+ mask = characterMask[0]
+ }
+ return strings.TrimLeft(str, mask)
+}
+
+// Strip whitespace (or other characters) from the end of a string.
+func TrimRight(str string, characterMask ...string) string {
+ mask := ""
+ if len(characterMask) == 0 {
+ mask = " \\t\\n\\r\\0\\x0B"
+ } else {
+ mask = characterMask[0]
+ }
+ return strings.TrimRight(str, mask)
+}
+
+// Split a string by a string.
+func Split(str, delimiter string) []string {
+ return strings.Split(str, delimiter)
+}
+
+// Join concatenates the elements of a to create a single string. The separator string
+// sep is placed between elements in the resulting string.
+func Join(array []string, sep string) string {
+ return strings.Join(array, sep)
+}
+
+// Split a string by a string.
+func Explode(delimiter, str string) []string {
+ return Split(str, delimiter)
+}
+
+// Join array elements with a string.
+func Implode(glue string, pieces []string) string {
+ var buf bytes.Buffer
+ l := len(pieces)
+ for _, str := range pieces {
+ buf.WriteString(str)
+ if l--; l > 0 {
+ buf.WriteString(glue)
+ }
+ }
+ return buf.String()
+}
+
+// Generate a single-byte string from a number.
+func Chr(ascii int) string {
+ return string(ascii)
+}
+
+// Convert the first byte of a string to a value between 0 and 255.
+func Ord(char string) int {
+ r, _ := utf8.DecodeRune([]byte(char))
+ return int(r)
+}
+
+// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。
+func HideStr(str string, percent int, hide string) string {
+ array := strings.Split(str, "@")
+ if len(array) > 1 {
+ str = array[0]
+ }
+ rs := []rune(str)
+ length := len(rs)
+ mid := math.Floor(float64(length/2))
+ hideLen := int(math.Floor(float64(length) * (float64(percent)/100)))
+ start := int(mid - math.Floor(float64(hideLen) / 2))
+ hideStr := []rune("")
+ hideRune := []rune(hide)
+ for i := 0; i < int(hideLen); i++ {
+ hideStr = append(hideStr, hideRune...)
+ }
+ buffer := bytes.NewBuffer(nil)
+ buffer.WriteString(string(rs[0 : start]))
+ buffer.WriteString(string(hideStr))
+ buffer.WriteString(string(rs[start + hideLen : ]))
+ if len(array) > 1 {
+ buffer.WriteString(array[1])
+ }
+ return buffer.String()
+}
+
+// Inserts HTML line breaks before all newlines in a string.
+// \n\r, \r\n, \r, \n
+func Nl2Br(str string, isXhtml...bool) string {
+ r, n, runes := '\r', '\n', []rune(str)
+ var br []byte
+ if len(isXhtml) > 0 && isXhtml[0] {
+ br = []byte("
")
+ } else {
+ br = []byte("
")
+ }
+ skip := false
+ length := len(runes)
+ var buf bytes.Buffer
+ for i, v := range runes {
+ if skip {
+ skip = false
+ continue
+ }
+ switch v {
+ case n, r:
+ if (i+1 < length) && (v == r && runes[i+1] == n) || (v == n && runes[i+1] == r) {
+ buf.Write(br)
+ skip = true
+ continue
+ }
+ buf.Write(br)
+ default:
+ buf.WriteRune(v)
+ }
+ }
+ return buf.String()
+}
+
+// Quote string with slashes.
+func AddSlashes(str string) string {
+ var buf bytes.Buffer
+ for _, char := range str {
+ switch char {
+ case '\'', '"', '\\':
+ buf.WriteRune('\\')
+ }
+ buf.WriteRune(char)
+ }
+ return buf.String()
+}
+
+// Un-quotes a quoted string.
+func StripSlashes(str string) string {
+ var buf bytes.Buffer
+ l, skip := len(str), false
+ for i, char := range str {
+ if skip {
+ skip = false
+ } else if char == '\\' {
+ if i+1 < l && str[i+1] == '\\' {
+ skip = true
+ }
+ continue
+ }
+ buf.WriteRune(char)
+ }
+ return buf.String()
+}
+
+// Returns a version of str with a backslash character (\) before every character that is among:
+// .\+*?[^]($)
+func QuoteMeta(str string) string {
+ var buf bytes.Buffer
+ for _, char := range str {
+ switch char {
+ case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?':
+ buf.WriteRune('\\')
+ }
+ buf.WriteRune(char)
+ }
+ return buf.String()
+}
\ No newline at end of file
diff --git a/g/string/gstr/gstr_levenshtein.go b/g/string/gstr/gstr_levenshtein.go
new file mode 100644
index 000000000..10e6cc39c
--- /dev/null
+++ b/g/string/gstr/gstr_levenshtein.go
@@ -0,0 +1,55 @@
+package gstr
+
+// Calculate Levenshtein distance between two strings.
+// costIns: Defines the cost of insertion.
+// costRep: Defines the cost of replacement.
+// costDel: Defines the cost of deletion.
+// See http://php.net/manual/en/function.levenshtein.php.
+func Levenshtein(str1, str2 string, costIns, costRep, costDel int) int {
+ var maxLen = 255
+ l1 := len(str1)
+ l2 := len(str2)
+ if l1 == 0 {
+ return l2 * costIns
+ }
+ if l2 == 0 {
+ return l1 * costDel
+ }
+ if l1 > maxLen || l2 > maxLen {
+ return -1
+ }
+
+ tmp := make([]int, l2+1)
+ p1 := make([]int, l2+1)
+ p2 := make([]int, l2+1)
+ var c0, c1, c2 int
+ var i1, i2 int
+ for i2 := 0; i2 <= l2; i2++ {
+ p1[i2] = i2 * costIns
+ }
+ for i1 = 0; i1 < l1; i1++ {
+ p2[0] = p1[0] + costDel
+ for i2 = 0; i2 < l2; i2++ {
+ if str1[i1] == str2[i2] {
+ c0 = p1[i2]
+ } else {
+ c0 = p1[i2] + costRep
+ }
+ c1 = p1[i2+1] + costDel
+ if c1 < c0 {
+ c0 = c1
+ }
+ c2 = p2[i2] + costIns
+ if c2 < c0 {
+ c0 = c2
+ }
+ p2[i2+1] = c0
+ }
+ tmp = p1
+ p1 = p2
+ p2 = tmp
+ }
+ c0 = p1[l2]
+
+ return c0
+}
\ No newline at end of file
diff --git a/g/string/gstr/gstr_parse.go b/g/string/gstr/gstr_parse.go
new file mode 100644
index 000000000..f2e0b24a5
--- /dev/null
+++ b/g/string/gstr/gstr_parse.go
@@ -0,0 +1,157 @@
+package gstr
+
+import (
+ "fmt"
+ "net/url"
+ "strings"
+)
+
+// Parses the string into variables.
+// f1=m&f2=n -> map[f1:m f2:n]
+// f[a]=m&f[b]=n -> map[f:map[a:m b:n]]
+// f[a][a]=m&f[a][b]=n -> map[f:map[a:map[a:m b:n]]]
+// f[]=m&f[]=n -> map[f:[m n]]
+// f[a][]=m&f[a][]=n -> map[f:map[a:[m n]]]
+// f[][]=m&f[][]=n -> map[f:[map[]]] // Currently does not support nested slice.
+// f=m&f[a]=n -> error // This is not the same as PHP.
+// a .[[b=c -> map[a___[b:c]
+func Parse(encodedString string, result map[string]interface{}) error {
+ // build nested map.
+ var build func(map[string]interface{}, []string, interface{}) error
+ build = func(result map[string]interface{}, keys []string, value interface{}) error {
+ length := len(keys)
+ // trim ',"
+ key := strings.Trim(keys[0], "'\"")
+ if length == 1 {
+ result[key] = value
+ return nil
+ }
+
+ // The end is slice. like f[], f[a][]
+ if keys[1] == "" && length == 2 {
+ // todo nested slice
+ if key == "" {
+ return nil
+ }
+ val, ok := result[key]
+ if !ok {
+ result[key] = []interface{}{value}
+ return nil
+ }
+ children, ok := val.([]interface{})
+ if !ok {
+ return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
+ }
+ result[key] = append(children, value)
+ return nil
+ }
+
+ // The end is slice + map. like f[][a]
+ if keys[1] == "" && length > 2 && keys[2] != "" {
+ val, ok := result[key]
+ if !ok {
+ result[key] = []interface{}{}
+ val = result[key]
+ }
+ children, ok := val.([]interface{})
+ if !ok {
+ return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
+ }
+ if l := len(children); l > 0 {
+ if child, ok := children[l-1].(map[string]interface{}); ok {
+ if _, ok := child[keys[2]]; !ok {
+ build(child, keys[2:], value)
+ return nil
+ }
+ }
+ }
+ child := map[string]interface{}{}
+ build(child, keys[2:], value)
+ result[key] = append(children, child)
+
+ return nil
+ }
+
+ // map. like f[a], f[a][b]
+ val, ok := result[key]
+ if !ok {
+ result[key] = map[string]interface{}{}
+ val = result[key]
+ }
+ children, ok := val.(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("expected type 'map[string]interface{}' for key '%s', but got '%T'", key, val)
+ }
+ if err := build(children, keys[1:], value); err != nil {
+ return err
+ }
+ return nil
+ }
+
+ // split encodedString.
+ parts := strings.Split(encodedString, "&")
+ for _, part := range parts {
+ pos := strings.Index(part, "=")
+ if pos <= 0 {
+ continue
+ }
+ key, err := url.QueryUnescape(part[:pos])
+ if err != nil {
+ return err
+ }
+ for key[0] == ' ' {
+ key = key[1:]
+ }
+ if key == "" || key[0] == '[' {
+ continue
+ }
+ value, err := url.QueryUnescape(part[pos+1:])
+ if err != nil {
+ return err
+ }
+
+ // split into multiple keys
+ var keys []string
+ left := 0
+ for i, k := range key {
+ if k == '[' && left == 0 {
+ left = i
+ } else if k == ']' {
+ if left > 0 {
+ if len(keys) == 0 {
+ keys = append(keys, key[:left])
+ }
+ keys = append(keys, key[left+1:i])
+ left = 0
+ if i+1 < len(key) && key[i+1] != '[' {
+ break
+ }
+ }
+ }
+ }
+ if len(keys) == 0 {
+ keys = append(keys, key)
+ }
+ // first key
+ first := ""
+ for i, chr := range keys[0] {
+ if chr == ' ' || chr == '.' || chr == '[' {
+ first += "_"
+ } else {
+ first += string(chr)
+ }
+ if chr == '[' {
+ first += keys[0][i+1:]
+ break
+ }
+ }
+ keys[0] = first
+
+ // build nested map
+ if err := build(result, keys, value); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
\ No newline at end of file
diff --git a/g/string/gstr/gstr_pos.go b/g/string/gstr/gstr_pos.go
new file mode 100644
index 000000000..237535c8c
--- /dev/null
+++ b/g/string/gstr/gstr_pos.go
@@ -0,0 +1,76 @@
+package gstr
+
+import "strings"
+
+// Find the position of the first occurrence of a substring in a string.
+func Pos(haystack, needle string, offset int) int {
+ length := len(haystack)
+ if length == 0 || offset > length || -offset > length {
+ return -1
+ }
+
+ if offset < 0 {
+ offset += length
+ }
+ pos := strings.Index(haystack[offset:], needle)
+ if pos == -1 {
+ return -1
+ }
+ return pos + offset
+}
+
+// Find the position of the first occurrence of a case-insensitive substring in a string.
+func PosI(haystack, needle string, offset int) int {
+ length := len(haystack)
+ if length == 0 || offset > length || -offset > length {
+ return -1
+ }
+
+ haystack = haystack[offset:]
+ if offset < 0 {
+ offset += length
+ }
+ pos := strings.Index(strings.ToLower(haystack), strings.ToLower(needle))
+ if pos == -1 {
+ return -1
+ }
+ return pos + offset
+}
+
+// Find the position of the last occurrence of a substring in a string.
+func PosR(haystack, needle string, offset int) int {
+ pos, length := 0, len(haystack)
+ if length == 0 || offset > length || -offset > length {
+ return -1
+ }
+
+ if offset < 0 {
+ haystack = haystack[:offset+length+1]
+ } else {
+ haystack = haystack[offset:]
+ }
+ pos = strings.LastIndex(haystack, needle)
+ if offset > 0 && pos != -1 {
+ pos += offset
+ }
+ return pos
+}
+
+// Find the position of the last occurrence of a case-insensitive substring in a string.
+func PosRI(haystack, needle string, offset int) int {
+ pos, length := 0, len(haystack)
+ if length == 0 || offset > length || -offset > length {
+ return -1
+ }
+
+ if offset < 0 {
+ haystack = haystack[:offset+length+1]
+ } else {
+ haystack = haystack[offset:]
+ }
+ pos = strings.LastIndex(strings.ToLower(haystack), strings.ToLower(needle))
+ if offset > 0 && pos != -1 {
+ pos += offset
+ }
+ return pos
+}
\ No newline at end of file
diff --git a/g/string/gstr/gstr_similartext.go b/g/string/gstr/gstr_similartext.go
new file mode 100644
index 000000000..108a2a32a
--- /dev/null
+++ b/g/string/gstr/gstr_similartext.go
@@ -0,0 +1,47 @@
+package gstr
+
+// Calculate the similarity between two strings.
+// See http://php.net/manual/en/function.similar-text.php.
+func SimilarText(first, second string, percent *float64) int {
+ var similarText func(string, string, int, int) int
+ similarText = func(str1, str2 string, len1, len2 int) int {
+ var sum, max int
+ pos1, pos2 := 0, 0
+
+ // Find the longest segment of the same section in two strings
+ for i := 0; i < len1; i++ {
+ for j := 0; j < len2; j++ {
+ for l := 0; (i+l < len1) && (j+l < len2) && (str1[i+l] == str2[j+l]); l++ {
+ if l+1 > max {
+ max = l + 1
+ pos1 = i
+ pos2 = j
+ }
+ }
+ }
+ }
+
+ if sum = max; sum > 0 {
+ if pos1 > 0 && pos2 > 0 {
+ sum += similarText(str1, str2, pos1, pos2)
+ }
+ if (pos1+max < len1) && (pos2+max < len2) {
+ s1 := []byte(str1)
+ s2 := []byte(str2)
+ sum += similarText(string(s1[pos1+max:]), string(s2[pos2+max:]), len1-pos1-max, len2-pos2-max)
+ }
+ }
+
+ return sum
+ }
+
+ l1, l2 := len(first), len(second)
+ if l1+l2 == 0 {
+ return 0
+ }
+ sim := similarText(first, second, l1, l2)
+ if percent != nil {
+ *percent = float64(sim*200) / float64(l1+l2)
+ }
+ return sim
+}
diff --git a/g/string/gstr/gstr_soundex.go b/g/string/gstr/gstr_soundex.go
new file mode 100644
index 000000000..cd7ec1ca0
--- /dev/null
+++ b/g/string/gstr/gstr_soundex.go
@@ -0,0 +1,52 @@
+package gstr
+
+// Calculate the soundex key of a string.
+// See http://php.net/manual/en/function.soundex.php.
+func Soundex(str string) string {
+ if str == "" {
+ panic("str: cannot be an empty string")
+ }
+ table := [26]rune{
+ '0', '1', '2', '3', // A, B, C, D
+ '0', '1', '2', // E, F, G
+ '0', // H
+ '0', '2', '2', '4', '5', '5', // I, J, K, L, M, N
+ '0', '1', '2', '6', '2', '3', // O, P, Q, R, S, T
+ '0', '1', // U, V
+ '0', '2', // W, X
+ '0', '2', // Y, Z
+ }
+ last, code, small := -1, 0, 0
+ sd := make([]rune, 4)
+ // build soundex string
+ for i := 0; i < len(str) && small < 4; i++ {
+ // ToUpper
+ char := str[i]
+ if char < '\u007F' && 'a' <= char && char <= 'z' {
+ code = int(char - 'a' + 'A')
+ } else {
+ code = int(char)
+ }
+ if code >= 'A' && code <= 'Z' {
+ if small == 0 {
+ sd[small] = rune(code)
+ small++
+ last = int(table[code-'A'])
+ } else {
+ code = int(table[code-'A'])
+ if code != last {
+ if code != 0 {
+ sd[small] = rune(code)
+ small++
+ }
+ last = code
+ }
+ }
+ }
+ }
+ // pad with "0"
+ for ; small < 4; small++ {
+ sd[small] = '0'
+ }
+ return string(sd)
+}
diff --git a/g/util/gstr/gstr_test.go b/g/string/gstr/gstr_z_bench_test.go
similarity index 100%
rename from g/util/gstr/gstr_test.go
rename to g/string/gstr/gstr_z_bench_test.go
diff --git a/g/util/gtest/gtest.go b/g/test/gtest/gtest.go
similarity index 92%
rename from g/util/gtest/gtest.go
rename to g/test/gtest/gtest.go
index ae4b5f674..1ee4667e2 100644
--- a/g/util/gtest/gtest.go
+++ b/g/test/gtest/gtest.go
@@ -33,12 +33,24 @@ func Case(t *testing.T, f func()) {
// 断言判断, 相等
func Assert(value, expect interface{}) {
- rv := reflect.ValueOf(value)
- if rv.Kind() == reflect.Ptr {
- if rv.IsNil() {
+ rvValue := reflect.ValueOf(value)
+ rvExpect := reflect.ValueOf(expect)
+ if rvValue.Kind() == reflect.Ptr {
+ if rvValue.IsNil() {
value = nil
}
}
+ //if rvExpect.Kind() == reflect.Map {
+ // if rvValue.Kind() == reflect.Map {
+ // ksExpect := rvExpect.MapKeys()
+ // for _, k := range ksExpect {
+ // rvValue.M.MapIndex()
+ // m[String(k.Interface())] = rv.MapIndex(k).Interface()
+ // }
+ // } else {
+ // panic(fmt.Sprint(`[ASSERT] EXPECT VALUE TO BE A MAP`))
+ // }
+ //}
if fmt.Sprintf("%v", value) != fmt.Sprintf("%v", expect) {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, value, expect))
}
@@ -233,7 +245,7 @@ func getBacktrace(skip...int) string {
if reg, _ := regexp.Compile(``); reg.MatchString(file) {
continue
}
- if reg, _ := regexp.Compile("/g/util/gtest/.+$"); reg.MatchString(file) {
+ if reg, _ := regexp.Compile("/g/test/gtest/.+$"); reg.MatchString(file) {
continue
}
if goRoot != "" {
diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go
index 140fc1364..9ea0a8cc5 100644
--- a/g/util/gconv/gconv.go
+++ b/g/util/gconv/gconv.go
@@ -11,9 +11,9 @@
package gconv
import (
- "strconv"
"encoding/json"
"gitee.com/johng/gf/g/encoding/gbinary"
+ "strconv"
"strings"
)
@@ -116,27 +116,10 @@ func Int(i interface{}) int {
if i == nil {
return 0
}
- switch value := i.(type) {
- case int: return value
- case int8: return int(value)
- case int16: return int(value)
- case int32: return int(value)
- case int64: return int(value)
- case uint: return int(value)
- case uint8: return int(value)
- case uint16: return int(value)
- case uint32: return int(value)
- case uint64: return int(value)
- case float32: return int(value)
- case float64: return int(value)
- case bool:
- if value {
- return 1
- }
- return 0
- default:
- return int(Float64(value))
+ if v, ok := i.(int); ok {
+ return v
}
+ return int(Int64(i))
}
func Int8(i interface{}) int8 {
@@ -146,7 +129,7 @@ func Int8(i interface{}) int8 {
if v, ok := i.(int8); ok {
return v
}
- return int8(Int(i))
+ return int8(Int64(i))
}
func Int16(i interface{}) int16 {
@@ -156,7 +139,7 @@ func Int16(i interface{}) int16 {
if v, ok := i.(int16); ok {
return v
}
- return int16(Int(i))
+ return int16(Int64(i))
}
func Int32(i interface{}) int32 {
@@ -166,7 +149,7 @@ func Int32(i interface{}) int32 {
if v, ok := i.(int32); ok {
return v
}
- return int32(Int(i))
+ return int32(Int64(i))
}
func Int64(i interface{}) int64 {
@@ -176,60 +159,57 @@ func Int64(i interface{}) int64 {
if v, ok := i.(int64); ok {
return v
}
- return int64(Int(i))
-}
-
-func Uint(i interface{}) uint {
- if i == nil {
- return 0
- }
switch value := i.(type) {
- case int:
- if value < 0 {
- value = -value
- }
- return uint(value)
- case int8:
- if value < 0 {
- value = -value
- }
- return uint(value)
- case int16:
- if value < 0 {
- value = -value
- }
- return uint(value)
- case int32:
- if value < 0 {
- value = -value
- }
- return uint(value)
- case int64:
- if value < 0 {
- value = -value
- }
- return uint(value)
- case uint: return value
- case uint8: return uint(value)
- case uint16: return uint(value)
- case uint32: return uint(value)
- case uint64: return uint(value)
- case float32: return uint(value)
- case float64: return uint(value)
+ case int: return int64(value)
+ case int8: return int64(value)
+ case int16: return int64(value)
+ case int32: return int64(value)
+ case int64: return value
+ case uint: return int64(value)
+ case uint8: return int64(value)
+ case uint16: return int64(value)
+ case uint32: return int64(value)
+ case uint64: return int64(value)
+ case float32: return int64(value)
+ case float64: return int64(value)
case bool:
if value {
return 1
}
return 0
default:
- v := Float64(value)
- if v < 0 {
- v = -v
+ s := String(value)
+ // 按照十六进制解析
+ if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
+ if v, e := strconv.ParseInt(s[2 : ], 16, 64); e == nil {
+ return v
+ }
}
- return uint(v)
+ // 按照八进制解析
+ if len(s) > 1 && s[0] == '0' {
+ if v, e := strconv.ParseInt(s[1 : ], 8, 64); e == nil {
+ return v
+ }
+ }
+ // 按照十进制解析
+ if v, e := strconv.ParseInt(s, 10, 64); e == nil {
+ return v
+ }
+ // 按照浮点数解析
+ return int64(Float64(value))
}
}
+func Uint(i interface{}) uint {
+ if i == nil {
+ return 0
+ }
+ if v, ok := i.(uint); ok {
+ return v
+ }
+ return uint(Uint64(i))
+}
+
func Uint8(i interface{}) uint8 {
if i == nil {
return 0
@@ -237,7 +217,7 @@ func Uint8(i interface{}) uint8 {
if v, ok := i.(uint8); ok {
return v
}
- return uint8(Uint(i))
+ return uint8(Uint64(i))
}
func Uint16(i interface{}) uint16 {
@@ -247,7 +227,7 @@ func Uint16(i interface{}) uint16 {
if v, ok := i.(uint16); ok {
return v
}
- return uint16(Uint(i))
+ return uint16(Uint64(i))
}
func Uint32(i interface{}) uint32 {
@@ -257,17 +237,52 @@ func Uint32(i interface{}) uint32 {
if v, ok := i.(uint32); ok {
return v
}
- return uint32(Uint(i))
+ return uint32(Uint64(i))
}
func Uint64(i interface{}) uint64 {
if i == nil {
return 0
}
- if v, ok := i.(uint64); ok {
- return v
+ switch value := i.(type) {
+ case int: return uint64(value)
+ case int8: return uint64(value)
+ case int16: return uint64(value)
+ case int32: return uint64(value)
+ case int64: return uint64(value)
+ case uint: return uint64(value)
+ case uint8: return uint64(value)
+ case uint16: return uint64(value)
+ case uint32: return uint64(value)
+ case uint64: return value
+ case float32: return uint64(value)
+ case float64: return uint64(value)
+ case bool:
+ if value {
+ return 1
+ }
+ return 0
+ default:
+ s := String(value)
+ // 按照十六进制解析
+ if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
+ if v, e := strconv.ParseUint(s[2 : ], 16, 64); e == nil {
+ return v
+ }
+ }
+ // 按照八进制解析
+ if len(s) > 1 && s[0] == '0' {
+ if v, e := strconv.ParseUint(s[1 : ], 8, 64); e == nil {
+ return v
+ }
+ }
+ // 按照十进制解析
+ if v, e := strconv.ParseUint(s, 10, 64); e == nil {
+ return v
+ }
+ // 按照浮点数解析
+ return uint64(Float64(value))
}
- return uint64(Uint(i))
}
func Float32 (i interface{}) float32 {
diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go
index f02389a93..8b1ff55e4 100644
--- a/g/util/gconv/gconv_struct.go
+++ b/g/util/gconv/gconv_struct.go
@@ -10,7 +10,7 @@ import (
"errors"
"fmt"
"gitee.com/johng/gf/g/container/gset"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"gitee.com/johng/gf/third/github.com/fatih/structs"
"reflect"
"strings"
diff --git a/g/util/gconv/gconv_time.go b/g/util/gconv/gconv_time.go
index 7211c368b..76289c62a 100644
--- a/g/util/gconv/gconv_time.go
+++ b/g/util/gconv/gconv_time.go
@@ -9,7 +9,7 @@ package gconv
import (
"time"
"gitee.com/johng/gf/g/os/gtime"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
)
// 将变量i转换为time.Time类型
diff --git a/g/util/gconv/gconv_z_unit_basic_test.go b/g/util/gconv/gconv_z_unit_basic_test.go
index 8c44f5fac..cca3b2c54 100644
--- a/g/util/gconv/gconv_z_unit_basic_test.go
+++ b/g/util/gconv/gconv_z_unit_basic_test.go
@@ -8,7 +8,7 @@ package gconv_test
import (
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/util/gconv/gconv_z_unit_map_test.go b/g/util/gconv/gconv_z_unit_map_test.go
index 2aaf8712c..530a0294f 100644
--- a/g/util/gconv/gconv_z_unit_map_test.go
+++ b/g/util/gconv/gconv_z_unit_map_test.go
@@ -8,7 +8,7 @@ package gconv_test
import (
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/util/gconv/gconv_z_unit_slice_test.go b/g/util/gconv/gconv_z_unit_slice_test.go
index e27062295..a334f8966 100644
--- a/g/util/gconv/gconv_z_unit_slice_test.go
+++ b/g/util/gconv/gconv_z_unit_slice_test.go
@@ -8,7 +8,7 @@ package gconv_test
import (
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go
index 31b87597b..0659ef336 100644
--- a/g/util/gconv/gconv_z_unit_struct_test.go
+++ b/g/util/gconv/gconv_z_unit_struct_test.go
@@ -9,7 +9,7 @@ package gconv_test
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
)
@@ -30,7 +30,7 @@ func Test_Struct_Basic1(t *testing.T) {
params1 := g.Map{
"uid" : 1,
"Name" : "john",
- "siteurl" : "https://gfer.me",
+ "siteurl" : "https://goframe.org",
"nick_name" : "johng",
"PASS1" : "123",
"PASS2" : "456",
@@ -41,7 +41,7 @@ func Test_Struct_Basic1(t *testing.T) {
gtest.Assert(user, &User{
Uid : 1,
Name : "john",
- Site_Url : "https://gfer.me",
+ Site_Url : "https://goframe.org",
NickName : "johng",
Pass1 : "123",
Pass2 : "456",
@@ -52,7 +52,7 @@ func Test_Struct_Basic1(t *testing.T) {
params2 := g.Map {
"uid" : 2,
"name" : "smith",
- "site-url" : "https://gfer.me",
+ "site-url" : "https://goframe.org",
"nick name" : "johng",
"password1" : "111",
"password2" : "222",
@@ -63,7 +63,7 @@ func Test_Struct_Basic1(t *testing.T) {
gtest.Assert(user, &User{
Uid : 2,
Name : "smith",
- Site_Url : "https://gfer.me",
+ Site_Url : "https://goframe.org",
NickName : "johng",
Pass1 : "111",
Pass2 : "222",
@@ -86,7 +86,7 @@ func Test_Struct_Basic2(t *testing.T) {
params := g.Map {
"uid" : 1,
"Name" : "john",
- "site_url" : "https://gfer.me",
+ "site_url" : "https://goframe.org",
"PASS1" : "123",
"PASS2" : "456",
}
@@ -96,7 +96,7 @@ func Test_Struct_Basic2(t *testing.T) {
gtest.Assert(user, &User{
Uid : 1,
Name : "john",
- SiteUrl : "https://gfer.me",
+ SiteUrl : "https://goframe.org",
Pass1 : "123",
Pass2 : "456",
})
diff --git a/g/util/gconv/gconv_z_unit_time_test.go b/g/util/gconv/gconv_z_unit_time_test.go
index 7234a1990..86c9bfc37 100644
--- a/g/util/gconv/gconv_z_unit_time_test.go
+++ b/g/util/gconv/gconv_z_unit_time_test.go
@@ -9,7 +9,7 @@ package gconv_test
import (
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"testing"
"time"
)
diff --git a/g/util/gpage/gpage.go b/g/util/gpage/gpage.go
index 3e45cfaaf..6b316ee35 100644
--- a/g/util/gpage/gpage.go
+++ b/g/util/gpage/gpage.go
@@ -15,8 +15,8 @@ import (
url2 "net/url"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/net/ghttp"
- "gitee.com/johng/gf/g/util/gregex"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gregex"
+ "gitee.com/johng/gf/g/string/gstr"
"strings"
)
diff --git a/g/util/grand/grand.go b/g/util/grand/grand.go
index d74cbc653..c085b73f5 100644
--- a/g/util/grand/grand.go
+++ b/g/util/grand/grand.go
@@ -24,23 +24,23 @@ func MeetProb(prob float32) bool {
return Rand(0, 1e7) <= int(prob*1e7)
}
-// Rand 别名
+// Rand 别名, 返回: [min, max]
func N (min, max int) int {
return Rand(min, max)
}
-// 获得一个 min, max 之间的随机数(min <= x <= max)
+// 获得一个 min, max 之间的随机数: [min, max]
func Rand (min, max int) int {
if min >= max {
return min
}
if min >= 0 {
// 数值往左平移,再使用底层随机方法获得随机数,随后将结果数值往右平移
- return intn(max - (min - 0) + 1) + (min - 0)
+ return Intn(max - (min - 0) + 1) + (min - 0)
}
if min < 0 {
// 数值往右平移,再使用底层随机方法获得随机数,随后将结果数值往左平移
- return intn(max + (0 - min) + 1) - (0 - min)
+ return Intn(max + (0 - min) + 1) - (0 - min)
}
return 0
}
@@ -54,10 +54,10 @@ func Str(n int) string {
func RandStr(n int) string {
b := make([]rune, n)
for i := range b {
- if intn(2) == 1 {
- b[i] = digits[intn(10)]
+ if Intn(2) == 1 {
+ b[i] = digits[Intn(10)]
} else {
- b[i] = letters[intn(52)]
+ b[i] = letters[Intn(52)]
}
}
return string(b)
@@ -72,7 +72,7 @@ func Digits(n int) string {
func RandDigits(n int) string {
b := make([]rune, n)
for i := range b {
- b[i] = digits[intn(10)]
+ b[i] = digits[Intn(10)]
}
return string(b)
}
@@ -86,7 +86,18 @@ func Letters(n int) string {
func RandLetters(n int) string {
b := make([]rune, n)
for i := range b {
- b[i] = letters[intn(52)]
+ b[i] = letters[Intn(52)]
}
return string(b)
}
+
+// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
+func Perm(n int) []int {
+ m := make([]int, n)
+ for i := 0; i < n; i++ {
+ j := Intn(i + 1)
+ m[i] = m[j]
+ m[j] = i
+ }
+ return m
+}
diff --git a/g/util/grand/grand_intn.go b/g/util/grand/grand_intn.go
index 2309b7b4b..06aa19c21 100644
--- a/g/util/grand/grand_intn.go
+++ b/g/util/grand/grand_intn.go
@@ -44,8 +44,8 @@ func init() {
}()
}
-// 自定义的 rand.Intn ,绝对随机
-func intn (max int) int {
+// 自定义的 rand.Intn ,绝对随机, 返回: [0, max)
+func Intn (max int) int {
n := int(<- bufferChan)%max
if n < 0 {
return -n
diff --git a/g/util/gstr/gstr.go b/g/util/gstr/gstr.go
deleted file mode 100644
index aec98176d..000000000
--- a/g/util/gstr/gstr.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
-
-// Package gstr provides useful API for string handling.
-//
-// 字符串操作.
-package gstr
-
-import (
- "bytes"
- "math"
- "strings"
-)
-
-// 字符串替换(大小写敏感)
-func Replace(origin, search, replace string, count...int) string {
- n := -1
- if len(count) > 0 {
- n = count[0]
- }
- return strings.Replace(origin, search, replace, n)
-}
-
-// 使用map进行字符串替换(大小写敏感)
-func ReplaceByMap(origin string, replaces map[string]string) string {
- result := origin
- for k, v := range replaces {
- result = strings.Replace(result, k, v, -1)
- }
- return result
-}
-
-// 字符串转换为小写
-func ToLower(s string) string {
- return strings.ToLower(s)
-}
-
-// 字符串转换为大写
-func ToUpper(s string) string {
- return strings.ToUpper(s)
-}
-
-// 字符串首字母转换为大写
-func UcFirst(s string) string {
- if len(s) == 0 {
- return s
- }
- if IsLetterLower(s[0]) {
- return string(s[0] - 32) + s[1 :]
- }
- return s
-}
-
-// 字符串首字母转换为小写
-func LcFirst(s string) string {
- if len(s) == 0 {
- return s
- }
- if IsLetterUpper(s[0]) {
- return string(s[0] + 32) + s[1 :]
- }
- return s
-}
-
-// 便利数组查找字符串索引位置,如果不存在则返回-1,使用完整遍历查找
-func SearchArray (a []string, s string) int {
- for i, v := range a {
- if s == v {
- return i
- }
- }
- return -1
-}
-
-// 判断字符串是否在数组中
-func InArray (a []string, s string) bool {
- return SearchArray(a, s) != -1
-}
-
-// 判断给定字符是否小写
-func IsLetterLower(b byte) bool {
- if b >= byte('a') && b <= byte('z') {
- return true
- }
- return false
-}
-
-// 判断给定字符是否大写
-func IsLetterUpper(b byte) bool {
- if b >= byte('A') && b <= byte('Z') {
- return true
- }
- return false
-}
-
-// 判断锁给字符串是否为数字
-func IsNumeric(s string) bool {
- length := len(s)
- if length == 0 {
- return false
- }
- for i := 0; i < len(s); i++ {
- if s[i] < byte('0') || s[i] > byte('9') {
- return false
- }
- }
- return true
-}
-
-// 字符串截取,支持中文
-func SubStr(str string, start int, length...int) (substr string) {
- // 将字符串的转换成[]rune
- rs := []rune(str)
- lth := len(rs)
- // 简单的越界判断
- if start < 0 {
- start = 0
- }
- if start >= lth {
- start = lth
- }
- end := lth
- if len(length) > 0 {
- end = start + length[0]
- if end < start {
- end = lth
- }
- }
- if end > lth {
- end = lth
- }
- // 返回子串
- return string(rs[start : end])
-}
-
-// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文
-func StrLimit(str string, length int, suffix...string) (string) {
- rs := []rune(str)
- if len(str) < length {
- return str
- }
- addstr := "..."
- if len(suffix) > 0 {
- addstr = suffix[0]
- }
- return string(rs[0 : length]) + addstr
-}
-
-// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。
-func HideStr(str string, percent int, hide string) string {
- array := strings.Split(str, "@")
- if len(array) > 1 {
- str = array[0]
- }
- rs := []rune(str)
- length := len(rs)
- mid := math.Floor(float64(length/2))
- hideLen := int(math.Floor(float64(length) * (float64(percent)/100)))
- start := int(mid - math.Floor(float64(hideLen) / 2))
- hideStr := []rune("")
- hideRune := []rune(hide)
- for i := 0; i < int(hideLen); i++ {
- hideStr = append(hideStr, hideRune...)
- }
- buffer := bytes.NewBuffer(nil)
- buffer.WriteString(string(rs[0 : start]))
- buffer.WriteString(string(hideStr))
- buffer.WriteString(string(rs[start + hideLen : ]))
- if len(array) > 1 {
- buffer.WriteString(array[1])
- }
- return buffer.String()
-}
-
-// 将\n\r替换为html中的
标签。
-func Nl2Br(str string) string {
- str = Replace(str, "\r\n", "\n")
- str = Replace(str, "\n\r", "\n")
- str = Replace(str, "\n", "
")
- return str
-}
diff --git a/g/util/gutil/gutil.go b/g/util/gutil/gutil.go
index f14ba645f..6dfb4c139 100644
--- a/g/util/gutil/gutil.go
+++ b/g/util/gutil/gutil.go
@@ -57,8 +57,8 @@ func PrintBacktrace() {
index := 1
buffer := bytes.NewBuffer(nil)
for i := 0; i < 10000; i++ {
- if _, cfile, cline, ok := runtime.Caller(i); ok {
- buffer.WriteString(fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, "\n"))
+ if _, path, line, ok := runtime.Caller(i); ok {
+ buffer.WriteString(fmt.Sprintf(`%d. %s:%d%s`, index, path, line, "\n"))
index++
} else {
break
@@ -84,3 +84,4 @@ func TryCatch(try func(), catch ... func(exception interface{})) {
try()
}
+
diff --git a/g/util/gvalid/gvalid.go b/g/util/gvalid/gvalid.go
index d94eaf42a..a169d0fac 100644
--- a/g/util/gvalid/gvalid.go
+++ b/g/util/gvalid/gvalid.go
@@ -10,7 +10,7 @@
package gvalid
import (
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"strings"
)
diff --git a/g/util/gvalid/gvalid_check.go b/g/util/gvalid/gvalid_check.go
index 6e06c779b..ce23a6128 100644
--- a/g/util/gvalid/gvalid_check.go
+++ b/g/util/gvalid/gvalid_check.go
@@ -13,7 +13,7 @@ import (
"gitee.com/johng/gf/g/net/gipv6"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g/util/gregex"
+ "gitee.com/johng/gf/g/string/gregex"
"regexp"
"strconv"
"strings"
diff --git a/g/util/gvalid/gvalid_unit_basic_all_test.go b/g/util/gvalid/gvalid_unit_basic_all_test.go
index 3627d30a5..b6e49ec23 100644
--- a/g/util/gvalid/gvalid_unit_basic_all_test.go
+++ b/g/util/gvalid/gvalid_unit_basic_all_test.go
@@ -8,7 +8,7 @@ package gvalid_test
import (
"gitee.com/johng/gf/g"
- "gitee.com/johng/gf/g/util/gtest"
+ "gitee.com/johng/gf/g/test/gtest"
"gitee.com/johng/gf/g/util/gvalid"
"testing"
)
diff --git a/geg/container/gpool/gpool.go b/geg/container/gpool/gpool.go
index 94acd6cba..4b195e5b0 100644
--- a/geg/container/gpool/gpool.go
+++ b/geg/container/gpool/gpool.go
@@ -8,7 +8,7 @@ import (
func main () {
// 创建一个对象池,过期时间为1000毫秒
- p := gpool.New(1000)
+ p := gpool.New(1000, nil)
// 从池中取一个对象,返回nil及错误信息
fmt.Println(p.Get())
diff --git a/geg/net/ghttp/server/hooks/same_route_multi_hook.go b/geg/net/ghttp/server/hooks/same_route_multi_hook.go
index 99a3dfc55..cb7a056ad 100644
--- a/geg/net/ghttp/server/hooks/same_route_multi_hook.go
+++ b/geg/net/ghttp/server/hooks/same_route_multi_hook.go
@@ -13,7 +13,7 @@ func beforeServeHook1(r *ghttp.Request) {
// 随后调用的HOOK
func beforeServeHook2(r *ghttp.Request) {
- r.SetParam("site", "https://gfer.me")
+ r.SetParam("site", "https://goframe.org")
r.Response.Writeln("set site")
}
diff --git a/geg/os/gview/build_in_funcs/build_in_funcs.go b/geg/os/gview/build_in_funcs/build_in_funcs.go
index 07460aef0..1c8cc7ea6 100644
--- a/geg/os/gview/build_in_funcs/build_in_funcs.go
+++ b/geg/os/gview/build_in_funcs/build_in_funcs.go
@@ -10,8 +10,8 @@ func main() {
{{"测试
"|text}}
{{"测试
"|html}}
{{"<div>测试</div>"|htmldecode}}
-{{"https://gfer.me"|url}}
-{{"https%3A%2F%2Fgfer.me"|urldecode}}
+{{"https://goframe.org"|url}}
+{{"https%3A%2F%2Fgoframe.org"|urldecode}}
{{1540822968 | date "Y-m-d"}}
{{"1540822968" | date "Y-m-d H:i:s"}}
{{date "Y-m-d H:i:s"}}
diff --git a/geg/other/test.go b/geg/other/test.go
index 5457752ea..d5cfc1631 100644
--- a/geg/other/test.go
+++ b/geg/other/test.go
@@ -1,25 +1,10 @@
package main
import (
- "gitee.com/johng/gf/g"
- "gitee.com/johng/gf/g/net/ghttp"
+ "fmt"
+ "gitee.com/johng/gf/g/string/gstr"
)
-func Handler(r *ghttp.Request) {
- if r.Request.Method == "OPTIONS" {
- return
- }
- r.Response.WriteJson(g.Map{"name" : "john"})
-}
-
func main() {
- s := g.Server()
- s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
- r.Response.SetAllowCrossDomainRequest("*", "PUT,GET,POST,DELETE,OPTIONS")
- r.Response.Header().Set("Access-Control-Allow-Credentials", "true")
- r.Response.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, token")
- })
- s.Group("/v1").ALL("*", Handler, ghttp.HOOK_BEFORE_SERVE)
- s.SetPort(6789)
- s.Run()
+ fmt.Println(gstr.PosI("abcdEfg", "eF", 0))
}
\ No newline at end of file
diff --git a/geg/util/gconv/gconv_struct1.go b/geg/util/gconv/gconv_struct1.go
index cee49b7c1..b75161583 100644
--- a/geg/util/gconv/gconv_struct1.go
+++ b/geg/util/gconv/gconv_struct1.go
@@ -22,7 +22,7 @@ func main() {
params1 := g.Map{
"uid" : 1,
"Name" : "john",
- "siteurl" : "https://gfer.me",
+ "siteurl" : "https://goframe.org",
"nick_name" : "johng",
"PASS1" : "123",
"PASS2" : "456",
@@ -36,7 +36,7 @@ func main() {
params2 := g.Map {
"uid" : 2,
"name" : "smith",
- "site-url" : "https://gfer.me",
+ "site-url" : "https://goframe.org",
"nick name" : "johng",
"password1" : "111",
"password2" : "222",
diff --git a/geg/util/gconv/gconv_struct2.go b/geg/util/gconv/gconv_struct2.go
index 17e15fdcf..89d83819b 100644
--- a/geg/util/gconv/gconv_struct2.go
+++ b/geg/util/gconv/gconv_struct2.go
@@ -21,7 +21,7 @@ func main() {
params := g.Map {
"uid" : 1,
"Name" : "john",
- "site_url" : "https://gfer.me",
+ "site_url" : "https://goframe.org",
"PASS1" : "123",
"PASS2" : "456",
}
diff --git a/geg/util/gpage/gpage_custom1.go b/geg/util/gpage/gpage_custom1.go
index 532a59758..34762e44d 100644
--- a/geg/util/gpage/gpage_custom1.go
+++ b/geg/util/gpage/gpage_custom1.go
@@ -3,7 +3,7 @@ package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/os/gview"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/util/gpage"
)
diff --git a/geg/util/gstr/gstr_hidestr.go b/geg/util/gstr/gstr_hidestr.go
index 3cc38d4f2..700b64be2 100644
--- a/geg/util/gstr/gstr_hidestr.go
+++ b/geg/util/gstr/gstr_hidestr.go
@@ -2,7 +2,7 @@ package main
import (
"fmt"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
)
func main() {
diff --git a/geg/util/gstr/gstr_substr.go b/geg/util/gstr/gstr_substr.go
index db59f4ad7..bed4c75c0 100644
--- a/geg/util/gstr/gstr_substr.go
+++ b/geg/util/gstr/gstr_substr.go
@@ -2,7 +2,7 @@ package main
import (
"fmt"
- "gitee.com/johng/gf/g/util/gstr"
+ "gitee.com/johng/gf/g/string/gstr"
)
func main() {
diff --git a/third/README.MD b/third/README.MD
index 7799e3372..bb95cfc4f 100644
--- a/third/README.MD
+++ b/third/README.MD
@@ -1 +1 @@
-`GF` self-maintains its thirdparty-packages, developers do not need worry about the dependences.
\ No newline at end of file
+`GF` self-maintains its thirdparty-packages, developers need no worry about the dependences.
\ No newline at end of file