Compare commits

..

8 Commits

66 changed files with 1506 additions and 707 deletions

View File

@ -1,25 +1,21 @@
package main
import (
"github.com/gogf/gf/crypto/gaes"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/gres"
"fmt"
)
var (
CryptoKey = []byte("x76cgqt36i9c863bzmotuf8626dxiwu0")
)
func Test() (int, int) {
return 1, 1
}
func Assert(v1, v2, v3 interface{}) {
fmt.Println(v1)
}
func F(v ...interface{}) []interface{} {
return v
}
func main() {
binContent, err := gres.Pack("public,config")
if err != nil {
panic(err)
}
binContent, err = gaes.Encrypt(binContent, CryptoKey)
if err != nil {
panic(err)
}
if err := gfile.PutBytes("data.bin", binContent); err != nil {
panic(err)
}
Assert(F(Test()), 2, 3)
}

View File

@ -1,3 +1,351 @@
# `v1.12.1` (2020-03-31)
大家好啊!久等啦!
由于自从上次版本的发布以来,越来越多小伙伴加入了`GF`的大家庭,并提供了许多不错的建议和反馈,这次版本对其中大部分反馈进行了处理,包括大部分的改进建议和部分新特性,因此这次的版本发布时隔了两个多月。`GF`非常注重代码质量以及可持续维护性,这次版本也进一步对框架大部分模块的示例、注释和单元测试用例进行了完善,目前单元测试用例数量约为`1991`例,代码覆盖率为`71%`,覆盖了所有模块的绝大部分主要功能。
`GF`框架提供了比较常用、高质量的基础开发模块,轻量级、模块化、高性能,推荐大家阅读框架源码了解更多细节。本次发布有个别的不兼容升级,往往批量替换即可,以下`change log`比较完善,建议升级前仔细阅读。
本次发布即意味下一版本开发计划的开启欢迎更多小伙伴参与开源贡献https://github.com/gogf/gf/projects/8
感谢大家支持Enjoy your `GF`
# GoFrame
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链提供了常用的基础开发模块缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
## 特点
* 模块化、松耦合设计;
* 模块丰富,开箱即用;
* 简便易用,易于维护;
* 社区活跃,大牛谦逊低调脾气好;
* 高代码质量、高单元测试覆盖率;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 更适合企业及团队使用;
## 地址
- 官网https://goframe.org
- 主库https://github.com/gogf/gf
- 码云https://gitee.com/johng/gf
# Change Log
从`GF v1.12`版本开始,框架要求的最低`Golang`运行版本为`v1.13`,由于`Golang`新版本都是向后兼容的,因此推荐大家更新使用`Golang`新版本https://golang.google.cn/dl/
> 本次版本也新增了`Swagger`的工具及插件支持,另行独立发布。
## `tool chain`
1. `gen model`命令新增对`pgsql/mssql/sqlite/oracle`的模型生成支持。
1. `gen model`命令生成模型新增公开包变量`Columns`用于获得表的字段名称。
## `net`
1. `ghttp`
- 注意:从该版本开始,`Server`默认关闭了平滑重启特性。开发者可以通过相应的配置选项打开。
- 改进`Client.Get`方法,增加可选的请求参数。
- 新增`Client`链式操作方法:`Header`, `HeaderRaw`, `Cookie`, `ContentType`, `ContentJson`, `ContentXml`, `Timeout`, `BasicAuth`, `Ctx`https://goframe.org/net/ghttp/client/chain
- 新增`Request.GetCtx/GetCtxVar/SetCtxVar/Context`上下文变量管理方法,用于请求内部的上下文变量特性:
- 自定义变量https://goframe.org/net/ghttp/request/custom
- 上下文变量https://goframe.org/net/ghttp/request/context
- 新增`Request.GetUploadFile/GetUploadFiles`方法,以及`UploadFile`类型极大简化文件上传处理逻辑https://goframe.org/net/ghttp/client/demo/upload
- 新增`Request.GetPage`方法,用于便捷地获得分页对象:
- 基本介绍https://goframe.org/util/gpage/index
- 动态分页https://goframe.org/util/gpage/dynamic
- 静态分页https://goframe.org/util/gpage/static
- Ajax分页https://goframe.org/util/gpage/ajax
- URL模板https://goframe.org/util/gpage/template
- 自定义分页https://goframe.org/util/gpage/custom
- 改进`Response.Redirect*`方法增加自定义的跳转HTTP状态码参数。
- 改进`CORS`特性,完善跨域功能处理,并完全遵守`W3C`关于`OPTIONS`请求方法的规范约定https://goframe.org/net/ghttp/cors
- 模板视图对象增加`Request`内置变量,用于模板获得请求参数。由于`GF`框架的模板引擎采用两级缓存设计,减少`IO`开销的同时提升了执行效率,即使增加了大量的内置变量以及内置方法,经过大规模的性能测试,性能比其他`WebServer`库相同逻辑下高出`50% - 200%`的效率。
- 新增`Server`实验性的`Plugin`特性。
- 改进`Server`底层路由存储、检索逻辑,优化代码,提升效率。
- 改进分组路由注册的源码位置记录功能,当路由注册冲突时能够精准定位及提示重复路由源码位置。
- 改进`Server`日志处理。
- 完善单元测试。
## `database`
1. `gdb`
- 代码重构改进,增加`Driver`驱动接口设计,方便开发者自定义驱动实现。新增`Register`包方法用于开发者注册自定义的数据库类型驱动https://goframe.org/database/gdb/driver
- 新增`GetArray`接口及实现用于获取指定字段列的数据构造成数组返回https://goframe.org/database/gdb/chaining/select
- 新增`InsertIgnore`接口及实现,用于写入时忽略写入冲突,仅对`mysql`数据库类型有效https://goframe.org/database/gdb/chaining/insert-save
- 新增`Schema`接口及实现用于动态切换并获取指定名称的数据库对象https://goframe.org/database/gdb/chaining/schema
- 新增`FieldsStr/FieldsExStr`模型方法用于获取表字段并构造成字符串返回hhttps://goframe.org/database/gdb/chaining/fields-retrieve
- 新增`LockUpdate/LockShared`模型链式操作方法用于实现悲观锁操作https://goframe.org/database/gdb/chaining/lock
- 改进`Where/Data`方法对更新参数输入方式的支持,提高灵活性:
- https://goframe.org/database/gdb/chaining/select
- https://goframe.org/database/gdb/chaining/update-delete
- 查询结果对象`Result`新增`Array`方法用于获得指定字段的数值构造成数组返回https://goframe.org/database/gdb/result
- 改进`OmitEmpty`方法对`Data`输入参数的过滤,当给定的`Data`参数为空时间对象时,将会被过滤。
- 增加默认的数据库连接池参数:`MaxIdleConns=10`。
- 其他一些改进。
- 完善单元测试。
1. `gredis`
- 增加/修改默认的数据库连接池参数:`MaxIdle=10`, `IdleTimeout=10s`, `MaxConnLifetime=30s`, `Wait=true`。
- 完善单元测试。
## `container`
1. 所有容器对象新增`UnmarshalValue(interface{}) error`接口方法实现,用于`gconv`转换时根据任意类型变量创建/设置对象内容,`GF`框架的所有容器对象均实现了该接口。
1. `garray`
- 新增`RemoveValue`方法,用于根据数值检索并删除元素项。
- 新增`FilterNil`方法,用于遍历并删除数组中的`nil`元素项。
- 新增`FilterEmpty`方法,用于遍历并删除数组中的空值元素项,空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
- 改进`Set/Fill/InsertBefore/InsertAfter`方法严谨性,将原有的链式操作返回值对象修改为`error`返回值。
- 改进`Get/Remove/PopRand/PopLeft/PopRight/Rand`方法严谨性,增加`found`的`bool`返回值,标识当前方法是否有数据返回,当空数组或者操作越界时`found`返回值为`false`。
- 改变`Rands`方法原有逻辑,保证按照给定大小返回随机数组。
- 完善单元测试。
1. `gpool`
- 调整缓存池过期时间参数类型,旧版本为`int`类型表示毫秒,新版本为`time.Duration`类型使得时间控制更灵活。
- 内部代码优化,性能改进。
- 完善单元测试。
- 完善注释。
## `os`
1. `glog`
- 增加`Rotation`日志滚动切分特性,新增按照文件大小或过期时间进行日志切分,并支持切分文件数量限制、对日志文件进行自动压缩、可自定义压缩级别(`1-9`、支持对切分文件过期时间清理https://goframe.org/os/glog/rotate
- 新增`LevelPrefixes`特性支持对日志级别的前缀名称进行自定义https://goframe.org/os/glog/level
- 新增`SetLevelStr`方法,并增加按照字符串进行日志级别配置的特性:
- https://goframe.org/os/glog/level
- https://goframe.org/os/glog/config
- 新增`SetDefaultLogger`包方法,用于设置全局默认的`Logger`对象。
1. `gres`
1. 改进资源内容编码设计,采用新的压缩算法,减少资源文件大小,例如原本`15MB`的网站静态资源文件(`css/js/html`等),资源文件打包后约为`4MB`左右https://goframe.org/os/gres/index
1. 注意:该改进与旧版本无法兼容,需要重新打包原有的资源文件。
1. 完善单元测试。
1. `gcfg`
- 去掉配置对象属性的原子并发安全控制,改为普通变量类型。由于配置管理是最常用模块之一,因此确保高效的设计及方法实现。
- 单例对象做较大调整:为方便多文件场景下的配置文件调用,简便使用并提高开发效率,因此当给定的单例名称对应的`toml`配置文件在配置目录中存在时,将自动设置该单例对象的默认配置文件为该文件。例如:`g.Cfg("redis")`获取到的单例对象将会默认去检索并设置默认的配置文件为`redis.toml`,当该文件不存在时,则使用默认的配置文件(`config.toml`https://goframe.org/net/ghttp/config
- 完善单元测试。
1. `gview`
- 新增`concat`内置字符串拼接方法https://goframe.org/os/gview/function/buildin
- 完善单元测试。
1. `gfile`
- 改进`SelfPath`获取当前执行文件路径方法,提高执行效率。
- 改进`Join`文件路径连接方法,防止多余的路径连接符号。
- 改建`GetContents`文件内容获取方法执行效率,降低内存占用。
- 新增`StrToSize`方法,用于将大小字符串转换为字节数字,大小字符串例如`10m`, `5KB`, `1.25Gib`等。
- 新增`ReadLines/ReadByteLines`方法,用于按行读取指定文件内容,并给定读取回调函数。
- 完善单元测试。
1. `gtime`
- 改进`gtime.Time`对象实现,统一字符串打印时间格式为`Y-m-d H:i:s`,如:`2020-01-01 12:00:00`。
1. `gcmd`
- 命令行解析方法增加`strict`参数,用于设置当前解析是否严格解析,严格解析下如果给定了非法的选项,将会停止解析并返回错误。默认情况下为非严格解析。
1. `genv`
- 改进`Remove`删除环境变量键值对方法,增加对多个键值对环境变量的删除。
1. `gfpool`
- 改进代码实现,提高效率。
- 完善单元测试。
1. `gfsnotify`
- 改进包初始化方法,当系统原因引起默认`Watcher`对象创建失败时,直接`panic`。
1. `gproc`
- 改进`SearchBinaryPath`方法。
- 改进`Process.Kill`方法在`windows`及`*niux`平台下不同表现的处理。
## `encoding`
1. `gjson`
- 代码改进、完善注释、新增大量代码示例。
- 文档更新:
- 基本介绍https://goframe.org/encoding/gjson/index
- 对象创建https://goframe.org/encoding/gjson/object
- 层级访问https://goframe.org/encoding/gjson/pattern
- Struct转换https://goframe.org/encoding/gjson/conversion-struct
- 动态创建修改https://goframe.org/encoding/gjson/dataset
- 数据格式转换https://goframe.org/encoding/gjson/conversion-format
## `frame`
1. `g`
- 新增`IsNil`方法,用于判断当前给定的变量是否为`nil`,该方法有可能会使用到反射来实现判断。
- 新增`IsEmpty`方法,用于判断当前给定的变量是否为`空`,该方法优先使用断言判断但有可能会使用到反射来实现判断。空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
- 标记废弃`SetServerGraceful`方法,转而统一交给`Server`的配置来管理。
1. `gins`
- 改进代码结构,方便维护。
- 改进、完善单元测试。
1. `gmvc`
- 新增`M`类型,为`*gdb.Model`的别名简称,用于工具链自动生成模型中的`M`属性。
## `text`
1. `gstr`
- 新增`HasPrefix/HasSuffix`方法。
- 新增`OctStr`方法,用于将八进制字符串转换为其对应的`unicode`字符串,例如转换为中文。常用于`gRPC`底层通信编码中。
- 完善单元测试。
## `debug`
1. `gdebug`
- 改进代码结构,方便维护。
- 新增`TestDataPath`方法,用于当前包单元测试中获得当前包中`testdata`目录的绝对路径。
## `util`
1. `gconv`
- 改进`String`字符串转换方法,增加对`time.Time/*time.Time/gtime.Time`类型的内置支持。
- 改进`Map/Struct`转换方法,增加对一些特殊场景的细节处理。经过大规模的使用,大量的反馈改进,不断完善了细节。
- 改进`Struct`转换方法,增加对`UnmarshalValue(interface{}) error`接口的支持。
- 完善单元测试。
1. `grand`
- 注意:不兼容调整,原有的`Str`方法更名为`S`方法,用以获取指定长度的随机字符串、数字,并增加`symbol`参数,指定是否可以随机返回特殊可见字符。
- 新增`Str`方法,用于从指定的字符串字符中随机获取指定长度的字符串。该方法同时支持`unicode`字符串例如中文https://goframe.org/util/grand/index
- 新增`Symbols`方法用于随机返回指定场孤独的特殊可见字符https://goframe.org/util/grand/index
- 完善单元测试。
1. `gvalid`
- 长度校验规则增加对`unicode`字符串的支持,例如:中文。
# Bug Fix
1. 修复`Server`的视图对象配置失效问题。
1. 修复`Server`中间件在中间件`panic`情况下,忽略`Middleware.Next`方法控制,导致鉴权中间件失效的问题。
1. 修复`gudp.Server`在请求包大小超过`64bytes`时的断包问题,并调整默认缓冲区大小为`1024byte`,开发者可自定义缓冲区大小。
1. 修复`gfile.MTimeMillisecond`方法返回错误的文件修改毫秒时间戳。
1. 修复`gconv.Int64`对负数转换的支持。
1. 其他一些修复。
1. 详见https://github.com/gogf/gf/issues?q=label%3Abug
# `v1.11.2` (2020-01-14)
`GF(Go Frame)` https://goframe.org 是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设包括常用的核心开发组件 如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、资源管理、数据校验、数据编码、文件监控、 定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、并发安全容器等等。 并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、配置管理、模板引擎等等 支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
`GF`有着丰富的基础模块、完善的工具链、详尽的开发文档。开源近两年以来,`GF`得到越来越多小伙伴的肯定和支持从寂寂无名到现在被广泛应用于微服务、物联网、区块链、电商系统、银行系统等企业级的生产项目中经历了百万级、千万级项目的考验2019年度被码云`gitee`评选为`GVP`最有价值开源项目。`GF`正在快速地成长中目前保持着1-2个月迭代版本的发布规律社区活跃欢迎加入`GF`大家庭。
最后祝大家2020新年快乐鼠年大吉
## 新特性
1. 新年新气象官网文档大量更新https://goframe.org/index
1. `GF`工具链更新https://goframe.org/toolchain/cli
- 新增`gf run`热编译运行命令;
- 新增`gf docker` Docker镜像编译命令
- 新增`gf gen model` 强大的模型自动生成命令;
- `gf build`命令增加对配置文件配置支持;
- 大量命令行工具改进工作;
- 新增自动代理设置特性;
1. 数据库`ORM`新特性:
- 增加`prefix`数据表前缀支持https://goframe.org/database/gdb/config
- 新增`Schema`数据库对象并改进数据库切换特性https://goframe.org/database/gdb/chaining/schema
- 新增`WherePri`方法用于自动识别主键的条件方法https://goframe.org/database/gdb/chaining/select
- 文档及示例大量更新覆盖95%以上的功能特性;
## 功能改进
### `container`
1. `garray`
- 新增`New*ArrayRange`方法,用于初始化创建指定数值范围的数组。
- 新增`Iterator*`方法,用于数组项元素回调遍历。
- 完善单元测试。
1. `gvar`
- 改进`MapStrStr`、`MapStrStrDeep`方法实现。
### `net`
1. `ghttp`
- 改进HTTP客户端增加对提交参数的自动`Content-Type`识别功能。
- `Request`对象增加`Parse`方法,用于快捷的对象转换即参数校验。
- `Request.GetPost*`方法全部标记为`deprecated`,统一客户端参数提交方式为`QueryString`, `Form`, `Body`。
- 去掉`Response`模板解析时的`Get`/`Post`内置变量,新增`Query`, `Form`, `Request`内置变量https://goframe.org/net/ghttp/response/template
- 改进`Response.WriteJson*`及`Response.WriteXml*`方法,增加对`string`, `[]byte`类型参数的支持。
- `Server`新增`GetRouterArray`方法,用于向应用层暴露并获取`Server`的路由列表。
- `Server`新增`Use`方法,该方法为`BindMiddlewareDefault`的别名,用以全局中间件的注册。
- `Server`新增`RouteOverWrite`配置项,用于控制是否在注册路由冲突时自动覆盖,默认关闭并提示。
- `Server`新增`Graceful`配置项,用于在单服务场景下控制平滑重启特性的开启/关闭,默认开启。
- 完善单元测试。
1. `gtcp`
- 改进简单协议下的数据包发送接收功能。
- 将连接池默认的缓存过期时间`30`秒修改为`10`秒。
- 完善单元测试。
### `database`
1. `gdb`
- 新增`As`数据表别名方法。
- 改进数据表、字段的安全字符自动识别添加功能。
- 新增`DB`数据库对象切换方法。
- 新增`TX`链式操作事务支持方法。
- 完善单元测试。
### `os`
1. `gcfg`
- 新增`GetMapStrStr`方法。
1. `gcmd`
- 增加参数解析的`strict`严格参数,默认严格解析,不存在指定参数/选项名称时则报错返回。
1. `genv`
- 改进`Remove`方法支持多个环境变量的删除。
1. `gfile`
- 改进`TempDir`临时目录获取方法,在`*nix`系统下默认为`/tmp`目录。
- 新增`ReadLines`, `ReadByteLines`方法,用以按行回调读取文件内容。
- 新增`Copy*`方法,用以文件/目录的拷贝,支持递归。
- 新增`Replace*`方法,用以目录下的文件内容替换,支持递归。
- 改进`Scan*`方法,用以检索并返回指定目录下的所有文件/目录,支持文件模式指定,支持递归。
- 完善单元测试。
1. `gproc`
- 改进命令行运行方法。
- 改进`Shell`命令文件检索逻辑。
- 改进实验性的进程间通信设计。
1. `gtime`
- 将包方法以及`Time`对象的时间戳方法`Second`, `Millisecond`, `Microsecond`, `Nanosecond`标记为废除,
并新增`Timestamp`, `TimestampMilli`, `TimestampMicro`, `TimestampNano`替换。
- 需要注意的是以上修改可能和老版本存在兼容性问题。
1. `gview`
- 解析功能、缓存设计改进。
- 新增`encode`, `decode`HTML编码/解码模板函数。
- 新增`concat`字符串拼接模板函数。
- 新增`dump`模板函数,功能类似于`g.Dump`方法。
- 新增`AutoEncode`配置项,用于自动转码输出的`HTML`内容,常用于防止`XSS`,默认关闭。需要注意的是该特性并不会影响`include`内置函数: https://goframe.org/os/gview/xss
- 单元测试完善。
### `crypto`
1. `gmd5`
- 增加`MustEncrypt`, `MustEncryptBytes`, `MustEncryptString`, `MustEncryptFile`方法。
1. `gsha1`
- 增加`MustEncryptFile`方法
### `encoding`
1. `gbase64`
- 新增`MustEncodeFile`, `MustEncodeFileToString`, `MustDecode`, `MustDecodeToString`方法。
1. `gjson`/`gparser`
- 新增`GetMapStrStr`方法。
- 新增`Must*`方法,用于指定数据格式的转换失败时产生`panic`错误,而不会返回`error`参数。
### `util`
1. `gconv`
- 改进`Convert`方法增加对`[]int32`, `[]int64`, `[]uint`, `[]uint32`, `[]uint64`, `[]float32`, `[]float64`数据类型的转换支持。
- 改进`String`字符串转换方法对指针参数的支持。
- 改进`Map*` Map转换方法的代码结构及性能。
- 新增`Floats`, `Float32s`, `Float64s`对`[]float32`, `[]float64`类型转换方法。
- 新增`Ints`, `Int32s`, `Int64s`对`[]int`, `[]int32`, `[]int64`类型转换方法。
- 新增`Uints`, `Uint32s`, `Uint64s`对`[]uint`, `[]uint32`, `[]uint64`类型转换方法。
- 完善单元测试。
### `frame`
1. `gins`
- 所有的单例对象在获取失败时产生`panic`错误。
## Bug Fix
1. 增加对常见错误路由格式例如`/user//index`的兼容支持。
1. 修复`gtcp`/`gudp`在数据接收时的间隔时间单位问题。
1. 修复`gfile`/`gspath`/`gfsnotify`包对文件的存在性判断不严谨问题。
1. 修复`gproc.Kill`方法在`windows`系统下的运行阻塞问题。
1. 修复`gstr.TrimLeftStr`/`gstr.TrimRightStr`在被替换字符串长度小于替换字符串长度时的数组溢出问题。
# `v1.10.0` (2019-12-05)
各位`gfer`久等了,较上一次发布时间过去已有两个多月了,这段时间`GF`也在不断地迭代改进,细节比较多,拟了个大概,以下是`release log`。

View File

@ -1,3 +1,5 @@
> This markdown is deprecated and will be removed in future. All TODO features are staged in the issue: https://github.com/gogf/gf/issues
# ON THE WAY
1. 增加图形验证码支持,至少支持数字和英文字母;
1. Cookie&Session数据池化处理

View File

@ -9,6 +9,7 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/text/gstr"
@ -20,6 +21,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// Array is a golang array with rich features.
type Array struct {
mu *rwmutex.RWMutex
array []interface{}
@ -94,21 +96,26 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *Array) Get(index int) interface{} {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *Array) Get(index int) (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return nil, false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *Array) Set(index int, value interface{}) *Array {
func (a *Array) Set(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -154,48 +161,60 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *Array) InsertBefore(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *Array) InsertAfter(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
}
// Remove removes an item by index.
func (a *Array) Remove(index int) interface{} {
func (a *Array) InsertBefore(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return nil
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
// Determine array boundaries when deleting to improve deletion efficiency。
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *Array) InsertAfter(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]interface{}{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return nil
}
// Remove removes an item by index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *Array) Remove(index int) (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
if index < 0 || index >= len(a.array) {
return nil, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
@ -226,64 +245,68 @@ func (a *Array) PushRight(value ...interface{}) *Array {
}
// PopRand randomly pops and return an item out of array.
func (a *Array) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *Array) PopRand() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *Array) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLeft pops and returns an item from the beginning of array.
func (a *Array) PopLeft() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *Array) PopLeft() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return nil
return nil, false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *Array) PopRight() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *Array) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return nil
return nil, false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *Array) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if length == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size > length {
size = length
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -294,12 +317,14 @@ func (a *Array) PopLefts(size int) []interface{} {
func (a *Array) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -529,11 +554,11 @@ func (a *Array) Merge(array interface{}) *Array {
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -542,7 +567,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -596,27 +621,27 @@ func (a *Array) Pad(size int, val interface{}) *Array {
}
// Rand randomly returns one item from array(no deleting).
func (a *Array) Rand() interface{} {
func (a *Array) Rand() (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return nil, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *Array) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.

View File

@ -9,6 +9,7 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math"
"sort"
@ -18,6 +19,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// IntArray is a golang int array with rich features.
type IntArray struct {
mu *rwmutex.RWMutex
array []int
@ -77,21 +79,26 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *IntArray) Get(index int) int {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *IntArray) Get(index int) (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return 0, false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *IntArray) Set(index int, value int) *IntArray {
func (a *IntArray) Set(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -127,8 +134,7 @@ func (a *IntArray) Sum() (sum int) {
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
// The parameter <reverse> controls whether sort in increasing order(default) or decreasing order.
func (a *IntArray) Sort(reverse ...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -156,57 +162,68 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
}
// Remove removes an item by index.
// Note that if the index is out of range of array, it returns 0.
func (a *IntArray) Remove(index int) int {
func (a *IntArray) InsertBefore(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return 0
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *IntArray) InsertAfter(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]int{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return nil
}
// Remove removes an item by index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *IntArray) Remove(index int) (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
if index < 0 || index >= len(a.array) {
return 0, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *IntArray) RemoveValue(value int) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
_, found := a.Remove(i)
return found
}
return false
}
@ -229,72 +246,72 @@ func (a *IntArray) PushRight(value ...int) *IntArray {
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, it returns 0.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *IntArray) PopLeft() int {
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopLeft() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return 0
return 0, false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, it returns 0.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *IntArray) PopRight() int {
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return 0
return 0, false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *IntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopRand() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if length == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size > length {
size = length
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -302,15 +319,19 @@ func (a *IntArray) PopLefts(size int) []int {
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -535,11 +556,11 @@ func (a *IntArray) Merge(array interface{}) *IntArray {
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
func (a *IntArray) Fill(startIndex int, num int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -548,7 +569,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -602,27 +623,27 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
}
// Rand randomly returns one item from array(no deleting).
func (a *IntArray) Rand() int {
func (a *IntArray) Rand() (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return 0, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *IntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]int, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.

View File

@ -9,6 +9,8 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -19,6 +21,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// StrArray is a golang string array with rich features.
type StrArray struct {
mu *rwmutex.RWMutex
array []string
@ -63,21 +66,26 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *StrArray) Get(index int) string {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *StrArray) Get(index int) (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return "", false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *StrArray) Set(index int, value string) *StrArray {
func (a *StrArray) Set(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -142,57 +150,68 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *StrArray) InsertBefore(index int, value string) *StrArray {
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
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *StrArray) InsertAfter(index int, value string) *StrArray {
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
}
// Remove removes an item by index.
// Note that if the index is out of range of array, it returns an empty string.
func (a *StrArray) Remove(index int) string {
func (a *StrArray) InsertBefore(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return ""
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
// Determine array boundaries when deleting to improve deletion efficiency。
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *StrArray) InsertAfter(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]string{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return nil
}
// Remove removes an item by index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *StrArray) Remove(index int) (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) {
if index < 0 || index >= len(a.array) {
return "", false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *StrArray) RemoveValue(value string) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
_, found := a.Remove(i)
return found
}
return false
}
@ -215,89 +234,92 @@ func (a *StrArray) PushRight(value ...string) *StrArray {
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *StrArray) PopLeft() string {
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopLeft() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return ""
return "", false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *StrArray) PopRight() string {
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return ""
return "", false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *StrArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopRand() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -525,11 +547,11 @@ func (a *StrArray) Merge(array interface{}) *StrArray {
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
func (a *StrArray) Fill(startIndex int, num int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -538,7 +560,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -592,27 +614,27 @@ func (a *StrArray) Pad(size int, value string) *StrArray {
}
// Rand randomly returns one item from array(no deleting).
func (a *StrArray) Rand() string {
func (a *StrArray) Rand() (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return "", false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *StrArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]string, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.

View File

@ -22,6 +22,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// SortedArray is a golang sorted array with rich features.
// It's using increasing order in default.
type SortedArray struct {
mu *rwmutex.RWMutex
@ -148,38 +149,46 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedArray) Get(index int) interface{} {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return nil, false
}
return a.array[index], true
}
// Remove removes an item by index.
func (a *SortedArray) Remove(index int) interface{} {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
if index < 0 || index >= len(a.array) {
return nil
return nil, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
@ -193,50 +202,53 @@ func (a *SortedArray) RemoveValue(value interface{}) bool {
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedArray) PopLeft() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return nil
return nil, false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *SortedArray) PopRight() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return nil
return nil, false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *SortedArray) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopRand() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedArray) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
@ -245,13 +257,14 @@ func (a *SortedArray) PopRands(size int) []interface{} {
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
return value
@ -261,12 +274,14 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -554,27 +569,27 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedArray) Rand() interface{} {
func (a *SortedArray) Rand() (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return nil, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedArray) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.

View File

@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// SortedIntArray is a golang sorted int array with rich features.
// It's using increasing order in default.
type SortedIntArray struct {
mu *rwmutex.RWMutex
@ -133,135 +134,145 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedIntArray) Get(index int) int {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedIntArray) Get(index int) (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return 0, false
}
return a.array[index], true
}
// Remove removes an item by index.
// Note that if the index is out of range of array, it returns 0.
func (a *SortedIntArray) Remove(index int) int {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedIntArray) Remove(index int) (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool) {
if index < 0 || index >= len(a.array) {
return 0
return 0, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedIntArray) RemoveValue(value int) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
_, found := a.Remove(i)
return found
}
return false
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, it returns 0.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedIntArray) PopLeft() int {
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopLeft() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return 0
return 0, false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, it returns 0.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedIntArray) PopRight() int {
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return 0
return 0, false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, it returns 0.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedIntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopRand() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -555,27 +566,27 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedIntArray) Rand() int {
func (a *SortedIntArray) Rand() (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return 0, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedIntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]int, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.

View File

@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/util/grand"
)
// SortedStrArray is a golang sorted string array with rich features.
// It's using increasing order in default.
type SortedStrArray struct {
mu *rwmutex.RWMutex
@ -118,39 +119,46 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedStrArray) Get(index int) string {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedStrArray) Get(index int) (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return "", false
}
return a.array[index], true
}
// Remove removes an item by index.
// Note that if the index is out of range of array, it returns an empty string.
func (a *SortedStrArray) Remove(index int) string {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
if index < 0 || index >= len(a.array) {
return ""
return "", false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
@ -164,89 +172,92 @@ func (a *SortedStrArray) RemoveValue(value string) bool {
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedStrArray) PopLeft() string {
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopLeft() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return ""
return "", false
}
value := a.array[0]
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedStrArray) PopRight() string {
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
return ""
return "", false
}
value := a.array[index]
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, it returns an empty string.
// Be very careful when use this function in loop statement.
// You can use IsEmpty() of Len() == 0 checks if this array empty.
func (a *SortedStrArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopRand() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
if size == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -540,27 +551,27 @@ func (a *SortedStrArray) Chunk(size int) [][]string {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedStrArray) Rand() string {
func (a *SortedStrArray) Rand() (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return "", false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedStrArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]string, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.

View File

@ -61,7 +61,7 @@ func Example_basic() {
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6
// 6 true
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
@ -95,9 +95,9 @@ func Example_popItem() {
fmt.Println(array.PopRights(2))
// Output:
// 1
// 1 true
// [2 3]
// 9
// 9 true
// [7 8]
}

View File

@ -28,17 +28,38 @@ func Test_Array_Basic(t *testing.T) {
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, 100)
t.Assert(array.Get(0), 100)
t.Assert(array.Get(1), 1)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Search(100), 0)
t.Assert(array3.Search(100), -1)
t.Assert(array.Contains(100), true)
t.Assert(array.Remove(0), 100)
t.Assert(array.Remove(-1), nil)
t.Assert(array.Remove(100000), nil)
t.Assert(array2.Remove(3), 3)
t.Assert(array2.Remove(1), 1)
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, nil)
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, nil)
t.Assert(ok, false)
v, ok = array2.Remove(3)
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array2.Remove(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Contains(100), false)
array.Append(4)
@ -85,10 +106,23 @@ func TestArray_PushAndPop(t *testing.T) {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
t.Assert(array.Slice(), expect)
t.Assert(array.PopLeft(), 0)
t.Assert(array.PopRight(), 3)
t.AssertIN(array.PopRand(), []interface{}{1, 2})
t.AssertIN(array.PopRand(), []interface{}{1, 2})
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.Assert(ok, true)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
t.Assert(array.Slice(), []interface{}{1, 2})
@ -106,11 +140,19 @@ func TestArray_PopRands(t *testing.T) {
func TestArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
t.Assert(array.PopLeft(), nil)
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
@ -185,9 +227,15 @@ func TestArray_Fill(t *testing.T) {
a2 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2, true)
t.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100})
t.Assert(array2.Fill(-1, 2, 100).Slice(), []interface{}{100, 100})
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []interface{}{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
})
}
@ -263,14 +311,15 @@ func TestArray_Rand(t *testing.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 7)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
})
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1)
i1 := a1.Rand()
i1, ok := a1.Rand()
t.Assert(ok, true)
t.Assert(a1.Contains(i1), true)
t.Assert(a1.Len(), 4)
})

View File

@ -29,14 +29,31 @@ func Test_IntArray_Basic(t *testing.T) {
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, 100)
t.Assert(array.Get(0), 100)
t.Assert(array.Get(1), 1)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Search(100), 0)
t.Assert(array2.Search(100), -1)
t.Assert(array.Contains(100), true)
t.Assert(array.Remove(0), 100)
t.Assert(array.Remove(-1), 0)
t.Assert(array.Remove(100000), 0)
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Contains(100), false)
array.Append(4)
t.Assert(array.Len(), 4)
@ -81,10 +98,27 @@ func TestIntArray_PushAndPop(t *testing.T) {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
t.Assert(array.Slice(), expect)
t.Assert(array.PopLeft(), 0)
t.Assert(array.PopRight(), 3)
t.AssertIN(array.PopRand(), []int{1, 2})
t.AssertIN(array.PopRand(), []int{1, 2})
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []int{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []int{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
t.Assert(array.Slice(), []int{1, 2})
@ -94,11 +128,23 @@ func TestIntArray_PushAndPop(t *testing.T) {
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArray()
t.Assert(array.PopLeft(), 0)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), 0)
v, ok = array.PopRight()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), 0)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
@ -173,9 +219,14 @@ func TestIntArray_Fill(t *testing.T) {
a2 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
t.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100})
t.Assert(array2.Fill(-1, 2, 100).Slice(), []int{100, 100})
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []int{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []int{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []int{100, 100})
})
}
@ -259,9 +310,12 @@ func TestIntArray_Rand(t *testing.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 7)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
t.AssertIN(array1.Rand(), a1)
v, ok := array1.Rand()
t.AssertIN(v, a1)
t.Assert(ok, true)
})
}
@ -358,7 +412,9 @@ func TestArray_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
t.Assert(array1.Get(2), 3)
v, ok := array1.Get(2)
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
})
}
@ -395,16 +451,19 @@ func TestIntArray_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5, 4}
array1 := garray.NewIntArrayFrom(a1)
n1 := array1.Remove(1)
t.Assert(n1, 2)
v, ok := array1.Remove(1)
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
n1 = array1.Remove(0)
t.Assert(n1, 1)
v, ok = array1.Remove(0)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
n1 = array1.Remove(2)
t.Assert(n1, 4)
v, ok = array1.Remove(2)
t.Assert(v, 4)
t.Assert(ok, true)
t.Assert(array1.Len(), 2)
})
}

View File

@ -29,13 +29,26 @@ func Test_StrArray_Basic(t *testing.T) {
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, "100")
t.Assert(array.Get(0), 100)
t.Assert(array.Get(1), 1)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
t.Assert(array.Search("100"), 0)
t.Assert(array.Contains("100"), true)
t.Assert(array.Remove(0), 100)
t.Assert(array.Remove(-1), "")
t.Assert(array.Remove(100000), "")
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, "")
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.Contains("100"), false)
array.Append("4")
t.Assert(array.Len(), 4)
@ -79,10 +92,27 @@ func TestStrArray_PushAndPop(t *testing.T) {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStrArrayFrom(expect)
t.Assert(array.Slice(), expect)
t.Assert(array.PopLeft(), "0")
t.Assert(array.PopRight(), "3")
t.AssertIN(array.PopRand(), []string{"1", "2"})
t.AssertIN(array.PopRand(), []string{"1", "2"})
v, ok := array.PopLeft()
t.Assert(v, "0")
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, "3")
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []string{"1", "2"})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []string{"1", "2"})
t.Assert(ok, true)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.Len(), 0)
array.PushLeft("1").PushRight("2")
t.Assert(array.Slice(), []string{"1", "2"})
@ -92,11 +122,19 @@ func TestStrArray_PushAndPop(t *testing.T) {
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArray()
t.Assert(array.PopLeft(), nil)
v, ok := array.PopLeft()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), nil)
v, ok = array.PopRight()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), nil)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
@ -171,10 +209,12 @@ func TestStrArray_Fill(t *testing.T) {
a2 := []string{"0"}
array1 := garray.NewStrArrayFrom(a1)
array2 := garray.NewStrArrayFrom(a2)
t.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0", "100", "100"})
t.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100", "100"})
s1 := array2.Fill(-1, 2, "100")
t.Assert(s1.Len(), 2)
t.Assert(array1.Fill(1, 2, "100"), nil)
t.Assert(array1.Slice(), []string{"0", "100", "100"})
t.Assert(array2.Fill(0, 2, "100"), nil)
t.Assert(array2.Slice(), []string{"100", "100"})
t.AssertNE(array2.Fill(-1, 2, "100"), nil)
t.Assert(array2.Len(), 2)
})
}
@ -250,10 +290,11 @@ func TestStrArray_Rand(t *testing.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
t.Assert(len(array1.Rands(2)), "2")
t.Assert(len(array1.Rands(10)), "7")
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
t.Assert(len(array1.Rand()), 1)
t.AssertIN(array1.Rand(), a1)
v, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(v, a1)
})
}
@ -373,9 +414,10 @@ func TestStrArray_PopRand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
str1 := array1.PopRand()
str1, ok := array1.PopRand()
t.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
t.Assert(array1.Len(), 6)
t.Assert(ok, true)
})
}
@ -405,11 +447,13 @@ func TestStrArray_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewStrArrayFrom(a1)
s1 := array1.Remove(1)
s1, ok := array1.Remove(1)
t.Assert(s1, "a")
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
s1 = array1.Remove(3)
s1, ok = array1.Remove(3)
t.Assert(s1, "c")
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
})
}

View File

@ -99,8 +99,13 @@ func TestSortedArray_Get(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
t.Assert(array1.Get(2), "f")
t.Assert(array1.Get(1), "c")
v, ok := array1.Get(2)
t.Assert(v, "f")
t.Assert(ok, true)
v, ok = array1.Get(1)
t.Assert(v, "c")
t.Assert(ok, true)
})
}
@ -112,20 +117,28 @@ func TestSortedArray_Remove(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Remove(1)
i1, ok := array1.Remove(1)
t.Assert(ok, true)
t.Assert(gconv.String(i1), "b")
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("b"), false)
t.Assert(array1.Remove(-1), nil)
t.Assert(array1.Remove(100000), nil)
v, ok := array1.Remove(-1)
t.Assert(v, nil)
t.Assert(ok, false)
i2 := array1.Remove(0)
v, ok = array1.Remove(100000)
t.Assert(v, nil)
t.Assert(ok, false)
i2, ok := array1.Remove(0)
t.Assert(ok, true)
t.Assert(gconv.String(i2), "a")
t.Assert(array1.Len(), 2)
t.Assert(array1.Contains("a"), false)
i3 := array1.Remove(1)
i3, ok := array1.Remove(1)
t.Assert(ok, true)
t.Assert(gconv.String(i3), "d")
t.Assert(array1.Len(), 1)
t.Assert(array1.Contains("d"), false)
@ -140,7 +153,8 @@ func TestSortedArray_PopLeft(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopLeft()
i1, ok := array1.PopLeft()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "a")
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"b", "c", "d"})
@ -155,7 +169,8 @@ func TestSortedArray_PopRight(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRight()
i1, ok := array1.PopRight()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "d")
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "b", "c"})
@ -170,7 +185,8 @@ func TestSortedArray_PopRand(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRand()
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
t.Assert(array1.Len(), 3)
@ -200,11 +216,19 @@ func TestSortedArray_PopRands(t *testing.T) {
func TestSortedArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArray(gutil.ComparatorInt)
t.Assert(array.PopLeft(), nil)
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
@ -406,7 +430,8 @@ func TestSortedArray_Rand(t *testing.T) {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rand()
i1, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.Assert(array1.Len(), 3)
})
@ -426,7 +451,7 @@ func TestSortedArray_Rands(t *testing.T) {
t.Assert(array1.Len(), 3)
i1 = array1.Rands(4)
t.Assert(len(i1), 3)
t.Assert(len(i1), 4)
})
}
@ -625,9 +650,22 @@ func TestSortedArray_Json(t *testing.T) {
t.Assert(user.Name, data["Name"])
t.AssertNE(user.Scores, nil)
t.Assert(user.Scores.Len(), 3)
t.AssertIN(user.Scores.PopLeft(), data["Scores"])
t.AssertIN(user.Scores.PopLeft(), data["Scores"])
t.AssertIN(user.Scores.PopLeft(), data["Scores"])
v, ok := user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
})
}

View File

@ -68,9 +68,17 @@ func TestSortedIntArray_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
t.Assert(array1.Get(0), 0)
t.Assert(array1.Get(1), 1)
t.Assert(array1.Get(3), 5)
v, ok := array1.Get(0)
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array1.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
v, ok = array1.Get(3)
t.Assert(v, 5)
t.Assert(ok, true)
})
}
@ -79,26 +87,39 @@ func TestSortedIntArray_Remove(t *testing.T) {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
t.Assert(array1.Remove(-1), 0)
t.Assert(array1.Remove(100000), 0)
v, ok := array1.Remove(-1)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array1.Remove(-100000)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array1.Remove(2)
t.Assert(v, 3)
t.Assert(ok, true)
i1 := array1.Remove(2)
t.Assert(i1, 3)
t.Assert(array1.Search(5), 2)
// 再次删除剩下的数组中的第一个
i2 := array1.Remove(0)
t.Assert(i2, 0)
v, ok = array1.Remove(0)
t.Assert(v, 0)
t.Assert(ok, true)
t.Assert(array1.Search(5), 1)
a2 := []int{1, 3, 4}
array2 := garray.NewSortedIntArrayFrom(a2)
i3 := array2.Remove(1)
v, ok = array2.Remove(1)
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array2.Search(1), 0)
t.Assert(i3, 3)
i3 = array2.Remove(1)
v, ok = array2.Remove(1)
t.Assert(v, 4)
t.Assert(ok, true)
t.Assert(array2.Search(4), -1)
t.Assert(i3, 4)
})
}
@ -106,8 +127,9 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopLeft()
t.Assert(i1, 1)
v, ok := array1.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(1), -1)
})
@ -117,8 +139,9 @@ func TestSortedIntArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRight()
t.Assert(i1, 5)
v, ok := array1.PopRight()
t.Assert(v, 5)
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(5), -1)
})
@ -128,7 +151,8 @@ func TestSortedIntArray_PopRand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRand()
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(i1), -1)
t.AssertIN(i1, []int{1, 3, 5, 2})
@ -155,11 +179,19 @@ func TestSortedIntArray_PopRands(t *testing.T) {
func TestSortedIntArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArray()
t.Assert(array.PopLeft(), 0)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), 0)
v, ok = array.PopRight()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), 0)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
@ -340,8 +372,9 @@ func TestSortedIntArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rand() //按每几个元素切成一个数组
ns1, ok := array1.Rand()
t.AssertIN(ns1, a1)
t.Assert(ok, true)
})
}
@ -349,13 +382,12 @@ func TestSortedIntArray_Rands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rands(2) //按每几个元素切成一个数组
ns1 := array1.Rands(2)
t.AssertIN(ns1, a1)
t.Assert(len(ns1), 2)
ns2 := array1.Rands(6) //按每几个元素切成一个数组
t.AssertIN(ns2, a1)
t.Assert(len(ns2), 5)
ns2 := array1.Rands(6)
t.Assert(len(ns2), 6)
})
}

View File

@ -68,8 +68,13 @@ func TestSortedStrArray_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
t.Assert(array1.Get(2), "c")
t.Assert(array1.Get(0), "a")
v, ok := array1.Get(2)
t.Assert(v, "c")
t.Assert(ok, true)
v, ok = array1.Get(0)
t.Assert(v, "a")
t.Assert(ok, true)
})
}
@ -78,20 +83,36 @@ func TestSortedStrArray_Remove(t *testing.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
t.Assert(array1.Remove(-1), "")
t.Assert(array1.Remove(100000), "")
v, ok := array1.Remove(-1)
t.Assert(v, "")
t.Assert(ok, false)
v, ok = array1.Remove(100000)
t.Assert(v, "")
t.Assert(ok, false)
v, ok = array1.Remove(2)
t.Assert(v, "c")
t.Assert(ok, true)
v, ok = array1.Get(2)
t.Assert(v, "d")
t.Assert(ok, true)
t.Assert(array1.Remove(2), "c")
t.Assert(array1.Get(2), "d")
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("c"), false)
t.Assert(array1.Remove(0), "a")
v, ok = array1.Remove(0)
t.Assert(v, "a")
t.Assert(ok, true)
t.Assert(array1.Len(), 2)
t.Assert(array1.Contains("a"), false)
// 此时array1里的元素只剩下2个
t.Assert(array1.Remove(1), "d")
v, ok = array1.Remove(1)
t.Assert(v, "d")
t.Assert(ok, true)
t.Assert(array1.Len(), 1)
})
}
@ -100,8 +121,9 @@ func TestSortedStrArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopLeft()
t.Assert(s1, "a")
v, ok := array1.PopLeft()
t.Assert(v, "a")
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("a"), false)
})
@ -111,8 +133,9 @@ func TestSortedStrArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRight()
t.Assert(s1, "e")
v, ok := array1.PopRight()
t.Assert(v, "e")
t.Assert(ok, ok)
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("e"), false)
})
@ -122,7 +145,8 @@ func TestSortedStrArray_PopRand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRand()
s1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains(s1), false)
@ -147,11 +171,19 @@ func TestSortedStrArray_PopRands(t *testing.T) {
func TestSortedStrArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArray()
t.Assert(array.PopLeft(), nil)
v, ok := array.PopLeft()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
t.Assert(array.PopRight(), nil)
v, ok = array.PopRight()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
t.Assert(array.PopRand(), nil)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
@ -283,7 +315,9 @@ func TestSortedStrArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
t.AssertIN(array1.Rand(), []string{"e", "a", "d"})
v, ok := array1.Rand()
t.AssertIN(v, []string{"e", "a", "d"})
t.Assert(ok, true)
})
}
@ -297,8 +331,7 @@ func TestSortedStrArray_Rands(t *testing.T) {
t.Assert(len(s1), 2)
s1 = array1.Rands(4)
t.AssertIN(s1, []string{"e", "a", "d"})
t.Assert(len(s1), 3)
t.Assert(len(s1), 4)
})
}

View File

@ -8,9 +8,11 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"time"
)
// getModel creates and returns a cloned model of current model if <safe> is true, or else it returns
@ -45,9 +47,25 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
}
// Remove key-value pairs of which the value is empty.
if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 {
m := gmap.NewStrAnyMapFrom(data)
m.FilterEmpty()
data = m.Map()
tempMap := make(Map, len(data))
for k, v := range data {
if empty.IsEmpty(v) {
continue
}
// Special type filtering.
switch r := v.(type) {
case time.Time:
if r.IsZero() {
continue
}
case gtime.Time:
if r.IsZero() {
continue
}
}
tempMap[k] = v
}
data = tempMap
}
if len(m.fields) > 0 && m.fields != "*" {

View File

@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/util/gutil"
"testing"
"time"
"github.com/gogf/gf/database/gdb"
@ -2098,3 +2099,27 @@ func Test_Model_FieldsExStruct(t *testing.T) {
t.Assert(n, 10)
})
}
func Test_Model_OmitEmpty_Time(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int `orm:"id" json:"id"`
Passport string `orm:"password" json:"pass_port"`
Password string `orm:"password" json:"password"`
Time time.Time `orm:"create_time" `
}
user := &User{
Id: 1,
Passport: "111",
Password: "222",
Time: time.Time{},
}
r, err := db.Table(table).OmitEmpty().Data(user).WherePri(1).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
})
}

View File

@ -9,8 +9,8 @@ package ghttp
import (
"context"
"crypto/tls"
"github.com/gogf/gf/text/gstr"
"net/http"
"strings"
"time"
"github.com/gogf/gf/text/gregex"
@ -94,7 +94,7 @@ func (c *Client) SetContentType(contentType string) *Client {
// SetHeaderRaw sets custom HTTP header using raw string.
func (c *Client) SetHeaderRaw(headers string) *Client {
for _, line := range strings.Split(strings.TrimSpace(headers), "\n") {
for _, line := range gstr.SplitAndTrim(headers, "\n") {
array, _ := gregex.MatchString(`^([\w\-]+):\s*(.+)`, line)
if len(array) >= 3 {
c.header[array[1]] = array[2]

View File

@ -18,7 +18,7 @@ import (
)
func Test_Client_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/hello", func(r *ghttp.Request) {
r.Response.Write("hello")
@ -42,8 +42,32 @@ func Test_Client_Basic(t *testing.T) {
})
}
func Test_Client_BasicAuth(t *testing.T) {
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/auth", func(r *ghttp.Request) {
if r.BasicAuth("admin", "admin") {
r.Response.Write("1")
} else {
r.Response.Write("0")
}
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
c := g.Client()
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
t.Assert(c.BasicAuth("admin", "123").GetContent("/auth"), "0")
t.Assert(c.BasicAuth("admin", "admin").GetContent("/auth"), "1")
})
}
func Test_Client_Cookie(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/cookie", func(r *ghttp.Request) {
r.Response.Write(r.Cookie.Get("test"))
@ -64,7 +88,7 @@ func Test_Client_Cookie(t *testing.T) {
}
func Test_Client_Cookies(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/cookie", func(r *ghttp.Request) {
r.Cookie.Set("test1", "1")
@ -97,7 +121,7 @@ func Test_Client_Cookies(t *testing.T) {
}
func Test_Client_Chain_Header(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/header1", func(r *ghttp.Request) {
r.Response.Write(r.Header.Get("test1"))
@ -122,7 +146,7 @@ func Test_Client_Chain_Header(t *testing.T) {
}
func Test_Client_Chain_Context(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/context", func(r *ghttp.Request) {
time.Sleep(1 * time.Second)
@ -147,7 +171,7 @@ func Test_Client_Chain_Context(t *testing.T) {
}
func Test_Client_Chain_Timeout(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/timeout", func(r *ghttp.Request) {
time.Sleep(1 * time.Second)
@ -168,7 +192,7 @@ func Test_Client_Chain_Timeout(t *testing.T) {
}
func Test_Client_Chain_ContentJson(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/json", func(r *ghttp.Request) {
r.Response.Write(r.Get("name"), r.Get("score"))
@ -197,7 +221,7 @@ func Test_Client_Chain_ContentJson(t *testing.T) {
}
func Test_Client_Chain_ContentXml(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/xml", func(r *ghttp.Request) {
r.Response.Write(r.Get("name"), r.Get("score"))

View File

@ -17,7 +17,7 @@ import (
)
func Test_Context(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {

View File

@ -17,7 +17,7 @@ import (
)
func Test_Cookie(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/set", func(r *ghttp.Request) {
r.Cookie.Set(r.GetString("k"), r.GetString("v"))

View File

@ -24,7 +24,7 @@ import (
func Test_Log(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
logDir := gfile.TempDir(gtime.TimestampNanoStr())
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/hello", func(r *ghttp.Request) {
r.Response.Write("hello")

View File

@ -17,7 +17,7 @@ import (
)
func Test_GetUrl(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/url", func(r *ghttp.Request) {
r.Response.Write(r.GetUrl())

View File

@ -20,7 +20,7 @@ import (
)
func Test_BindMiddleware_Basic1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -60,7 +60,7 @@ func Test_BindMiddleware_Basic1(t *testing.T) {
}
func Test_BindMiddleware_Basic2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -87,7 +87,7 @@ func Test_BindMiddleware_Basic2(t *testing.T) {
}
func Test_BindMiddleware_Basic3(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -131,7 +131,7 @@ func Test_BindMiddleware_Basic3(t *testing.T) {
}
func Test_BindMiddleware_Basic4(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {
@ -163,7 +163,7 @@ func Test_BindMiddleware_Basic4(t *testing.T) {
}
func Test_Middleware_With_Static(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {
@ -193,7 +193,7 @@ func Test_Middleware_With_Static(t *testing.T) {
}
func Test_Middleware_Status(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {
@ -224,7 +224,7 @@ func Test_Middleware_Status(t *testing.T) {
}
func Test_Middleware_Hook_With_Static(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
a := garray.New(true)
s.Group("/", func(group *ghttp.RouterGroup) {
@ -278,7 +278,7 @@ func Test_Middleware_Hook_With_Static(t *testing.T) {
}
func Test_BindMiddleware_Status(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -304,7 +304,7 @@ func Test_BindMiddleware_Status(t *testing.T) {
}
func Test_BindMiddlewareDefault_Basic1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -335,7 +335,7 @@ func Test_BindMiddlewareDefault_Basic1(t *testing.T) {
}
func Test_BindMiddlewareDefault_Basic2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("PUT:/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -368,7 +368,7 @@ func Test_BindMiddlewareDefault_Basic2(t *testing.T) {
}
func Test_BindMiddlewareDefault_Basic3(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -397,7 +397,7 @@ func Test_BindMiddlewareDefault_Basic3(t *testing.T) {
}
func Test_BindMiddlewareDefault_Basic4(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -426,7 +426,7 @@ func Test_BindMiddlewareDefault_Basic4(t *testing.T) {
}
func Test_BindMiddlewareDefault_Basic5(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -455,7 +455,7 @@ func Test_BindMiddlewareDefault_Basic5(t *testing.T) {
}
func Test_BindMiddlewareDefault_Status(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -501,7 +501,7 @@ func (o *ObjectMiddleware) Info(r *ghttp.Request) {
}
func Test_BindMiddlewareDefault_Basic6(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObject("/", new(ObjectMiddleware))
s.BindMiddlewareDefault(func(r *ghttp.Request) {
@ -534,7 +534,7 @@ func Test_BindMiddlewareDefault_Basic6(t *testing.T) {
}
func Test_Hook_Middleware_Basic1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/test/test", func(r *ghttp.Request) {
r.Response.Write("test")
@ -591,7 +591,7 @@ func MiddlewareCORS(r *ghttp.Request) {
}
func Test_Middleware_CORSAndAuth(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Use(MiddlewareCORS)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
@ -645,7 +645,7 @@ func MiddlewareScope3(r *ghttp.Request) {
}
func Test_Middleware_Scope(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareScope1)
@ -682,7 +682,7 @@ func Test_Middleware_Scope(t *testing.T) {
}
func Test_Middleware_Panic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
i := 0
s.Group("/", func(group *ghttp.RouterGroup) {

View File

@ -16,7 +16,7 @@ import (
)
func Test_Middleware_CORS1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
@ -79,7 +79,7 @@ func Test_Middleware_CORS1(t *testing.T) {
}
func Test_Middleware_CORS2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)

View File

@ -22,7 +22,7 @@ import (
func Test_Params_File_Single(t *testing.T) {
dstDirPath := gfile.TempDir(gtime.TimestampNanoStr())
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/upload/single", func(r *ghttp.Request) {
file := r.GetUploadFile("file")
@ -76,7 +76,7 @@ func Test_Params_File_Single(t *testing.T) {
func Test_Params_File_CustomName(t *testing.T) {
dstDirPath := gfile.TempDir(gtime.TimestampNanoStr())
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/upload/single", func(r *ghttp.Request) {
file := r.GetUploadFile("file")
@ -113,7 +113,7 @@ func Test_Params_File_CustomName(t *testing.T) {
func Test_Params_File_Batch(t *testing.T) {
dstDirPath := gfile.TempDir(gtime.TimestampNanoStr())
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/upload/batch", func(r *ghttp.Request) {
files := r.GetUploadFiles("file")

View File

@ -25,7 +25,7 @@ func Test_Params_Json_Request(t *testing.T) {
Pass1 string `p:"password1"`
Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"`
}
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.WriteExit(r.Get("id"), r.Get("name"))
@ -71,7 +71,7 @@ func Test_Params_Json_Response(t *testing.T) {
Pass2 string `json:"password2"`
}
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/json1", func(r *ghttp.Request) {
r.Response.WriteJson(User{

View File

@ -17,7 +17,7 @@ import (
)
func Test_Params_Page(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/list", func(r *ghttp.Request) {

View File

@ -26,7 +26,7 @@ func Test_Params_Struct(t *testing.T) {
Pass1 string `p:"password1"`
Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"`
}
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/struct1", func(r *ghttp.Request) {
if m := r.GetMap(); len(m) > 0 {

View File

@ -23,7 +23,7 @@ func Test_Params_Basic(t *testing.T) {
Pass1 string `params:"password1"`
Pass2 string `params:"password2"`
}
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
// GET
s.BindHandler("/get", func(r *ghttp.Request) {
@ -405,7 +405,7 @@ func Test_Params_Basic(t *testing.T) {
}
func Test_Params_SupportChars(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/form-value", func(r *ghttp.Request) {
r.Response.Write(r.GetQuery("test-value"))
@ -430,7 +430,7 @@ func Test_Params_SupportChars(t *testing.T) {
}
func Test_Params_Priority(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/query", func(r *ghttp.Request) {
r.Response.Write(r.GetQuery("a"))

View File

@ -24,7 +24,7 @@ func Test_Params_Xml_Request(t *testing.T) {
Pass1 string `p:"password1"`
Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"`
}
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.WriteExit(r.Get("id"), r.Get("name"))

View File

@ -20,7 +20,7 @@ import (
func TestServer_EnablePProf(t *testing.T) {
C(t, func(t *T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.EnablePProf("/pprof")
s.SetDumpRouterMap(false)

View File

@ -17,7 +17,7 @@ import (
)
func Test_Router_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/:name", func(r *ghttp.Request) {
r.Response.Write("/:name")
@ -52,7 +52,7 @@ func Test_Router_Basic(t *testing.T) {
// HTTP method register.
func Test_Router_Method(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("GET:/get", func(r *ghttp.Request) {
@ -94,7 +94,7 @@ func Test_Router_Method(t *testing.T) {
// Extra char '/' of the router.
func Test_Router_ExtraChar(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/api", func(group *ghttp.RouterGroup) {
group.GET("/test", func(r *ghttp.Request) {
@ -119,7 +119,7 @@ func Test_Router_ExtraChar(t *testing.T) {
// Custom status handler.
func Test_Router_Status(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/200", func(r *ghttp.Request) {
r.Response.WriteStatus(200)
@ -171,7 +171,7 @@ func Test_Router_Status(t *testing.T) {
}
func Test_Router_CustomStatusHandler(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("hello")
@ -200,7 +200,7 @@ func Test_Router_CustomStatusHandler(t *testing.T) {
// 404 not found router.
func Test_Router_404(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("hello")
@ -224,7 +224,7 @@ func Test_Router_404(t *testing.T) {
}
func Test_Router_Priority(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/admin", func(r *ghttp.Request) {
r.Response.Write("admin")

View File

@ -52,7 +52,7 @@ func (c *ControllerRest) Head() {
// 控制器注册测试
func Test_Router_ControllerRest(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindControllerRest("/", new(ControllerRest))
s.BindControllerRest("/{.struct}/{.method}", new(ControllerRest))

View File

@ -44,7 +44,7 @@ func (c *Controller) Info() {
}
func Test_Router_Controller1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindController("/", new(Controller))
s.BindController("/{.struct}/{.method}", new(Controller))
@ -75,7 +75,7 @@ func Test_Router_Controller1(t *testing.T) {
}
func Test_Router_Controller2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindController("/controller", new(Controller), "Show, Info")
s.SetPort(p)
@ -101,7 +101,7 @@ func Test_Router_Controller2(t *testing.T) {
}
func Test_Router_ControllerMethod(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindControllerMethod("/controller-info", new(Controller), "Info")
s.SetPort(p)

View File

@ -18,7 +18,7 @@ import (
)
func Test_Router_DomainBasic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindHandler("/:name", func(r *ghttp.Request) {
@ -69,7 +69,7 @@ func Test_Router_DomainBasic(t *testing.T) {
}
func Test_Router_DomainMethod(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindHandler("GET:/get", func(r *ghttp.Request) {
@ -161,7 +161,7 @@ func Test_Router_DomainMethod(t *testing.T) {
}
func Test_Router_DomainStatus(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindHandler("/200", func(r *ghttp.Request) {
@ -257,7 +257,7 @@ func Test_Router_DomainStatus(t *testing.T) {
}
func Test_Router_DomainCustomStatusHandler(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindHandler("/", func(r *ghttp.Request) {
@ -296,7 +296,7 @@ func Test_Router_DomainCustomStatusHandler(t *testing.T) {
}
func Test_Router_Domain404(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindHandler("/", func(r *ghttp.Request) {
@ -329,7 +329,7 @@ func Test_Router_Domain404(t *testing.T) {
}
func Test_Router_DomainGroup(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.Group("/", func(group *ghttp.RouterGroup) {

View File

@ -60,7 +60,7 @@ func (c *DomainControllerRest) Head() {
// 控制器注册测试
func Test_Router_DomainControllerRest(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindControllerRest("/", new(DomainControllerRest))

View File

@ -43,7 +43,7 @@ func (c *DomainController) Info() {
}
func Test_Router_DomainController1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindController("/", new(DomainController))
s.SetPort(p)
@ -93,7 +93,7 @@ func Test_Router_DomainController1(t *testing.T) {
}
func Test_Router_DomainController2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindController("/controller", new(DomainController), "Show, Info")
s.SetPort(p)
@ -146,7 +146,7 @@ func Test_Router_DomainController2(t *testing.T) {
}
func Test_Router_DomainControllerMethod(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindControllerMethod("/controller-info", new(DomainController), "Info")
s.SetPort(p)

View File

@ -55,7 +55,7 @@ func (o *DomainObjectRest) Head(r *ghttp.Request) {
}
func Test_Router_DomainObjectRest(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
d := s.Domain("localhost, local")
d.BindObjectRest("/", new(DomainObjectRest))

View File

@ -39,7 +39,7 @@ func (o *DomainObject) Info(r *ghttp.Request) {
}
func Test_Router_DomainObject1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindObject("/", new(DomainObject))
s.SetPort(p)
@ -88,7 +88,7 @@ func Test_Router_DomainObject1(t *testing.T) {
}
func Test_Router_DomainObject2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindObject("/object", new(DomainObject), "Show, Info")
s.SetPort(p)
@ -139,7 +139,7 @@ func Test_Router_DomainObject2(t *testing.T) {
}
func Test_Router_DomainObjectMethod(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Domain("localhost, local").BindObjectMethod("/object-info", new(DomainObject), "Info")
s.SetPort(p)

View File

@ -17,7 +17,7 @@ import (
)
func Test_Router_Exit(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) { r.Response.Write("1") },
@ -46,7 +46,7 @@ func Test_Router_Exit(t *testing.T) {
}
func Test_Router_ExitHook(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/priority/show", func(r *ghttp.Request) {
r.Response.Write("show")
@ -84,7 +84,7 @@ func Test_Router_ExitHook(t *testing.T) {
}
func Test_Router_ExitAll(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/priority/show", func(r *ghttp.Request) {
r.Response.Write("show")

View File

@ -17,7 +17,7 @@ import (
)
func Test_Router_Group_Group(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {

View File

@ -17,7 +17,7 @@ import (
)
func Test_Router_Group_Hook1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
group := s.Group("/api")
group.GET("/handler", func(r *ghttp.Request) {
@ -46,7 +46,7 @@ func Test_Router_Group_Hook1(t *testing.T) {
}
func Test_Router_Group_Hook2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
g := s.Group("/api")
g.GET("/handler", func(r *ghttp.Request) {
@ -76,7 +76,7 @@ func Test_Router_Group_Hook2(t *testing.T) {
}
func Test_Router_Group_Hook3(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.Group("/api").Bind([]g.Slice{
{"ALL", "handler", func(r *ghttp.Request) {

View File

@ -98,7 +98,7 @@ func (o *GroupObjRest) Head(r *ghttp.Request) {
}
func Test_Router_GroupRest(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
g := s.Group("/api")
ctl := new(GroupCtlRest)

View File

@ -71,7 +71,7 @@ func Handler(r *ghttp.Request) {
}
func Test_Router_GroupBasic1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
obj := new(GroupObject)
ctl := new(GroupController)
@ -118,7 +118,7 @@ func Test_Router_GroupBasic1(t *testing.T) {
}
func Test_Router_GroupBasic2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
obj := new(GroupObject)
ctl := new(GroupController)
@ -160,7 +160,7 @@ func Test_Router_GroupBasic2(t *testing.T) {
}
func Test_Router_GroupBuildInVar(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
obj := new(GroupObject)
ctl := new(GroupController)
@ -192,7 +192,7 @@ func Test_Router_GroupBuildInVar(t *testing.T) {
}
func Test_Router_Group_Mthods(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
obj := new(GroupObject)
ctl := new(GroupController)
@ -216,8 +216,8 @@ func Test_Router_Group_Mthods(t *testing.T) {
}
func Test_Router_Group_MultiServer(t *testing.T) {
p1 := ports.PopRand()
p2 := ports.PopRand()
p1, _ := ports.PopRand()
p2, _ := ports.PopRand()
s1 := g.Server(p1)
s2 := g.Server(p2)
s1.Group("/", func(group *ghttp.RouterGroup) {

View File

@ -17,7 +17,7 @@ import (
)
func Test_Router_Hook_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) { r.Response.Write("1") },
@ -44,7 +44,7 @@ func Test_Router_Hook_Basic(t *testing.T) {
}
func Test_Router_Hook_Fuzzy_Router(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
i := 1000
pattern1 := "/:name/info"
@ -88,7 +88,7 @@ func Test_Router_Hook_Fuzzy_Router(t *testing.T) {
}
func Test_Router_Hook_Priority(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/priority/show", func(r *ghttp.Request) {
r.Response.Write("show")
@ -127,7 +127,7 @@ func Test_Router_Hook_Priority(t *testing.T) {
}
func Test_Router_Hook_Multi(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
r.Response.Write("show")

View File

@ -23,7 +23,7 @@ func (o *NamesObject) ShowName(r *ghttp.Request) {
}
func Test_NameToUri_FullName(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetNameToUriType(ghttp.URI_TYPE_FULLNAME)
s.BindObject("/{.struct}/{.method}", new(NamesObject))
@ -44,7 +44,7 @@ func Test_NameToUri_FullName(t *testing.T) {
}
func Test_NameToUri_AllLower(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetNameToUriType(ghttp.URI_TYPE_ALLLOWER)
s.BindObject("/{.struct}/{.method}", new(NamesObject))
@ -65,7 +65,7 @@ func Test_NameToUri_AllLower(t *testing.T) {
}
func Test_NameToUri_Camel(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetNameToUriType(ghttp.URI_TYPE_CAMEL)
s.BindObject("/{.struct}/{.method}", new(NamesObject))
@ -86,7 +86,7 @@ func Test_NameToUri_Camel(t *testing.T) {
}
func Test_NameToUri_Default(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetNameToUriType(ghttp.URI_TYPE_DEFAULT)
s.BindObject("/{.struct}/{.method}", new(NamesObject))

View File

@ -55,7 +55,7 @@ func (o *ObjectRest) Head(r *ghttp.Request) {
}
func Test_Router_ObjectRest(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObjectRest("/", new(ObjectRest))
s.BindObjectRest("/{.struct}/{.method}", new(ObjectRest))

View File

@ -43,7 +43,7 @@ func (o *ObjectRest2) Delete(r *ghttp.Request) {
}
func Test_Router_ObjectRest_Id(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObjectRest("/object/:id", new(ObjectRest2))
s.SetPort(p)

View File

@ -39,7 +39,7 @@ func (o *Object) Info(r *ghttp.Request) {
}
func Test_Router_Object1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObject("/", new(Object))
s.BindObject("/{.struct}/{.method}", new(Object))
@ -70,7 +70,7 @@ func Test_Router_Object1(t *testing.T) {
}
func Test_Router_Object2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObject("/object", new(Object), "Show, Info")
s.SetPort(p)
@ -96,7 +96,7 @@ func Test_Router_Object2(t *testing.T) {
}
func Test_Router_ObjectMethod(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindObjectMethod("/object-info", new(Object), "Info")
s.SetPort(p)

View File

@ -17,7 +17,7 @@ import (
)
func Test_Session_Cookie(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set(r.GetString("k"), r.GetString("v"))
@ -64,7 +64,7 @@ func Test_Session_Cookie(t *testing.T) {
}
func Test_Session_Header(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set(r.GetString("k"), r.GetString("v"))
@ -115,7 +115,7 @@ func Test_Session_Header(t *testing.T) {
func Test_Session_StorageFile(t *testing.T) {
sessionId := ""
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set(r.GetString("k"), r.GetString("v"))

View File

@ -25,7 +25,7 @@ import (
func Test_Static_ServerRoot(t *testing.T) {
// SetServerRoot with absolute path
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)
@ -44,7 +44,7 @@ func Test_Static_ServerRoot(t *testing.T) {
// SetServerRoot with relative path
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`static/test/%d`, p)
defer gfile.Remove(path)
@ -64,7 +64,7 @@ func Test_Static_ServerRoot(t *testing.T) {
func Test_Static_ServerRoot_Security(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetServerRoot(gdebug.TestDataPath("static1"))
s.SetPort(p)
@ -85,7 +85,7 @@ func Test_Static_ServerRoot_Security(t *testing.T) {
func Test_Static_Folder_Forbidden(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)
@ -106,7 +106,7 @@ func Test_Static_Folder_Forbidden(t *testing.T) {
func Test_Static_IndexFolder(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)
@ -129,7 +129,7 @@ func Test_Static_IndexFolder(t *testing.T) {
func Test_Static_IndexFiles1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)
@ -151,7 +151,7 @@ func Test_Static_IndexFiles1(t *testing.T) {
func Test_Static_IndexFiles2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)
@ -173,7 +173,7 @@ func Test_Static_IndexFiles2(t *testing.T) {
func Test_Static_AddSearchPath1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
@ -196,7 +196,7 @@ func Test_Static_AddSearchPath1(t *testing.T) {
func Test_Static_AddSearchPath2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
@ -220,7 +220,7 @@ func Test_Static_AddSearchPath2(t *testing.T) {
func Test_Static_AddStaticPath(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
@ -245,7 +245,7 @@ func Test_Static_AddStaticPath(t *testing.T) {
func Test_Static_AddStaticPath_Priority(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d/test`, gfile.TempDir(), p)
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d/test`, gfile.TempDir(), p, p)
@ -270,7 +270,7 @@ func Test_Static_AddStaticPath_Priority(t *testing.T) {
func Test_Static_Rewrite(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
defer gfile.Remove(path)

View File

@ -24,7 +24,7 @@ import (
func Test_Template_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
v := gview.New(gdebug.TestDataPath("template", "basic"))
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetView(v)
s.BindHandler("/", func(r *ghttp.Request) {
@ -50,7 +50,7 @@ func Test_Template_Encode(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
v := gview.New(gdebug.TestDataPath("template", "basic"))
v.SetAutoEncode(true)
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetView(v)
s.BindHandler("/", func(r *ghttp.Request) {
@ -75,7 +75,7 @@ func Test_Template_Encode(t *testing.T) {
func Test_Template_Layout1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
v := gview.New(gdebug.TestDataPath("template", "layout1"))
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetView(v)
s.BindHandler("/layout", func(r *ghttp.Request) {
@ -105,7 +105,7 @@ func Test_Template_Layout1(t *testing.T) {
func Test_Template_Layout2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
v := gview.New(gdebug.TestDataPath("template", "layout2"))
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetView(v)
s.BindHandler("/main1", func(r *ghttp.Request) {
@ -144,7 +144,7 @@ func Test_Template_XSS(t *testing.T) {
v := gview.New()
v.SetAutoEncode(true)
c := "<br>"
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.SetView(v)
s.BindHandler("/", func(r *ghttp.Request) {

View File

@ -19,7 +19,7 @@ import (
)
func Test_WebSocket(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/ws", func(r *ghttp.Request) {
ws, err := r.WebSocket()

View File

@ -16,7 +16,7 @@ import (
)
func Test_Package_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
for {
@ -96,7 +96,7 @@ func Test_Package_Basic(t *testing.T) {
}
func Test_Package_Timeout(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
for {
@ -132,7 +132,7 @@ func Test_Package_Timeout(t *testing.T) {
}
func Test_Package_Option(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
option := gtcp.PkgOption{HeaderSize: 1}

View File

@ -16,7 +16,7 @@ import (
)
func Test_Pool_Package_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
for {
@ -96,7 +96,7 @@ func Test_Pool_Package_Basic(t *testing.T) {
}
func Test_Pool_Package_Timeout(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
for {
@ -132,7 +132,7 @@ func Test_Pool_Package_Timeout(t *testing.T) {
}
func Test_Pool_Package_Option(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
option := gtcp.PkgOption{HeaderSize: 1}

View File

@ -15,7 +15,7 @@ import (
)
func Test_Pool_Basic1(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
defer conn.Close()
for {
@ -42,7 +42,7 @@ func Test_Pool_Basic1(t *testing.T) {
}
func Test_Pool_Basic2(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) {
conn.Close()
})

View File

@ -17,7 +17,7 @@ import (
)
func Test_Basic(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gudp.NewServer(fmt.Sprintf("127.0.0.1:%d", p), func(conn *gudp.Conn) {
defer conn.Close()
for {
@ -75,7 +75,7 @@ func Test_Basic(t *testing.T) {
// If the read buffer size is less than the sent package size,
// the rest data would be dropped.
func Test_Buffer(t *testing.T) {
p := ports.PopRand()
p, _ := ports.PopRand()
s := gudp.NewServer(fmt.Sprintf("127.0.0.1:%d", p), func(conn *gudp.Conn) {
defer conn.Close()
for {

View File

@ -187,9 +187,9 @@ func (l *Logger) rotateChecksTimely() {
for _, array := range backupFilesMap {
diff := array.Len() - l.config.RotateBackupLimit
for i := 0; i < diff; i++ {
path := array.PopLeft().(string)
path, _ := array.PopLeft()
intlog.Printf(`remove exceeded backup limit file: %s`, path)
if err := gfile.Remove(path); err != nil {
if err := gfile.Remove(path.(string)); err != nil {
intlog.Print(err)
}
}

View File

@ -7,6 +7,7 @@
package gmutex_test
import (
"github.com/gogf/gf/os/glog"
"testing"
"time"
@ -239,35 +240,40 @@ func Test_Mutex_RLockFunc(t *testing.T) {
func Test_Mutex_TryRLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
mu := gmutex.New()
array := garray.New(true)
var (
mu = gmutex.New()
array = garray.New(true)
)
// First writing lock
go func() {
mu.LockFunc(func() {
array.Append(1)
time.Sleep(500 * time.Millisecond)
glog.Println("lock1 done")
time.Sleep(2000 * time.Millisecond)
})
}()
// This goroutine never gets the lock.
go func() {
time.Sleep(100 * time.Millisecond)
time.Sleep(1000 * time.Millisecond)
mu.TryRLockFunc(func() {
array.Append(1)
})
}()
for index := 0; index < 1000; index++ {
go func() {
time.Sleep(1000 * time.Millisecond)
time.Sleep(4000 * time.Millisecond)
mu.TryRLockFunc(func() {
array.Append(1)
})
}()
}
time.Sleep(50 * time.Millisecond)
time.Sleep(1000 * time.Millisecond)
t.Assert(array.Len(), 1)
time.Sleep(150 * time.Millisecond)
time.Sleep(1000 * time.Millisecond)
t.Assert(array.Len(), 1)
time.Sleep(600 * time.Millisecond)
time.Sleep(1000 * time.Millisecond)
t.Assert(array.Len(), 1)
time.Sleep(600 * time.Millisecond)
time.Sleep(2000 * time.Millisecond)
t.Assert(array.Len(), 1001)
})
}

View File

@ -52,7 +52,7 @@ func New(path string, cache bool) *SPath {
}
if len(path) > 0 {
if _, err := sp.Add(path); err != nil {
intlog.Print(err)
//intlog.Print(err)
}
}
return sp

View File

@ -11,7 +11,8 @@
// between gtimer and gcron are as follows:
// 1. package gcron is implemented based on package gtimer.
// 2. gtimer is designed for high performance and for millions of timing jobs.
// 3. gcron supports configuration pattern grammar like linux crontab, which is more manually readable.
// 3. gcron supports configuration pattern grammar like linux crontab, which is more manually
// readable.
// 4. gtimer's benchmark OP is measured in nanoseconds, and gcron's benchmark OP is measured
// in microseconds.
//

View File

@ -105,6 +105,7 @@ func Symbols(n int) string {
}
// Perm returns, as a slice of n int numbers, a pseudo-random permutation of the integers [0,n).
// TODO performance improving for large slice producing.
func Perm(n int) []int {
m := make([]int, n)
for i := 0; i < n; i++ {

View File

@ -18,13 +18,31 @@ import (
var buffer = make([]byte, 8)
func Benchmark_Rand1(b *testing.B) {
func Benchmark_Rand_Intn(b *testing.B) {
for i := 0; i < b.N; i++ {
grand.N(0, 99)
}
}
func Benchmark_Rand2(b *testing.B) {
func Benchmark_Perm10(b *testing.B) {
for i := 0; i < b.N; i++ {
grand.Perm(10)
}
}
func Benchmark_Perm100(b *testing.B) {
for i := 0; i < b.N; i++ {
grand.Perm(100)
}
}
func Benchmark_Rand_N1(b *testing.B) {
for i := 0; i < b.N; i++ {
grand.N(0, 99)
}
}
func Benchmark_Rand_N2(b *testing.B) {
for i := 0; i < b.N; i++ {
grand.N(0, 999999999)
}

View File

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