Compare commits

...

86 Commits

Author SHA1 Message Date
1e3aa5f080 fix: v2.9.5 (#4503) 2025-11-10 21:40:35 +08:00
fde47e8981 fix(cmd/gf): The problem of the command 'gen dao' becoming very slow (#4498)
fixed #4479

修复gf gen dao执行严重变慢的问题

主要调整,降级两个依赖库
`go get golang.org/x/tools@v0.26.0` // 直接依赖
`go get golang.org/x/text@v0.25.0` // 间接依赖

至于为什么这两个库会导致慢,还需要深入排查
2025-11-10 17:38:50 +08:00
c02148cd6b feat(‎container/garray): Sorted T Array (#4470)
Add the sorted T array

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: hailaz <739476267@qq.com>
2025-10-17 18:10:31 +08:00
XG
1d4e684949 refactor(cmd/gf): Optimize run command to reload only on file write events (#4476)
优化run命令使得只在文件有写入事件时才触发reload:
gf run 的文件监控逻辑之前会对所有文件系统事件做出响应,包括非内容修改的事件(如文件access
time变化),这会导致开发过程中不必要的频繁重载。本次修改在文件监控回调中增加了event.IsWrite()的判断,确保只有在文件内容被实际写入时才触发重载逻辑,优化了开发体验。
2025-10-16 16:20:24 +08:00
8ff0de88b8 build(contrib): upgrade nacos registry&config (#4473)
RT
2025-10-16 11:29:44 +08:00
ac3efe5a00 feat(os/gcfg): Add file watcher with custom callback support (#4446)
为`gcfg`添加配置文件变更自定义回调,实现了`WatcherAdapter`接口,以下是`AdapterFile`的用法
test.yaml
```
b: "b"

```
```
package main

import (
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcfg"
	"github.com/gogf/gf/v2/os/gctx"
)

func main() {
	ctx := gctx.New()
	file, _ := gcfg.NewAdapterFile("test.yaml")
	file.Data(ctx)
	file.AddWatcher("test", func() {
		value := file.MustGet(ctx, "b")
		fmt.Println(value.String())
	})
	server := g.Server()
	server.Run()
}
```
使用`g`和默认配置文件
```
	file := g.Cfg().GetAdapter().(*gcfg.AdapterFile)
	file.AddWatcher("test", func() {

	})
	file := g.Cfg().GetAdapter().(*gcfg.AdapterFile)
	file.RemoveWatcher("test")
```

注意:由于`gf`的`AdapterFile`使用的监听到文件变化删除缓存下一次重新初始化的懒加载方案,所有除了默认加载的`config.xxx`文件外,自定义的配置文件像`test.yaml`之类的都需要在`AddWatcher`前主动读取一次数据进行初始化监听(
`g.Cfg("test").Data(ctx)`)

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hunk Zhu <hunk@joy999.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 16:59:52 +08:00
613
2744fe2212 fix(net/gclient): fix content-type 'application/json;charset=utf-8' … (#4369)
fix(net/gclient): fix content-type 'application/json;charset=utf-8' can
not match `application/json`

---------

Co-authored-by: houseme <housemecn@gmail.com>
2025-10-15 16:14:33 +08:00
1682cc98bb feat(gdb): Allow to set table field metadata and allow to generate table fields registration code when generating dao (#4460)
`gdb`在第一次查询时会拉取一次`table`的`fields meta`信息,为后续orm的字段过滤和时间特性服务,`gen
dao`时已经获得了`table`的所有`fields
meta`,提供一个生成`table`的功能,允许客户自行决定是否生成,是否注入已知表结构缓存到指定`db`实例。
1. 能解决部分兼容`mysql`的二开数据库在获取`fields meta`时无法和`mysql`保持一致的问题。
2.
可以模拟表字段信息而不需要真实的数据库连接,对于已知的表结构,可以直接设置缓存,在无法连接数据库的情况下,仍然可以使用表字段信息,使用gdb构建sql时不需要受限于实际数据库即可使用`gdb.ToSQL()`方法。
4. 提升访问速度

生成的示例目录
<img width="389" height="670" alt="SCR-20251010-ntne"
src="https://github.com/user-attachments/assets/ebb08e70-cce1-4b73-9128-6ff784e4df3b"
/>

生成的示例代码
```golang
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================

package table

import (
	"context"

	"github.com/gogf/gf/v2/database/gdb"
)

// RolePermissions defines the fields of table "role_permissions" with their properties.
// This map is used internally by GoFrame ORM to understand table structure.
var RolePermissions = map[string]*gdb.TableField{
	"role_id": {
		Index:   0,
		Name:    "role_id",
		Type:    "bigint unsigned",
		Null:    false,
		Key:     "PRI",
		Default: nil,
		Extra:   "",
		Comment: "",
	},
	"permission_id": {
		Index:   1,
		Name:    "permission_id",
		Type:    "bigint unsigned",
		Null:    false,
		Key:     "PRI",
		Default: nil,
		Extra:   "",
		Comment: "",
	},
	"created_at": {
		Index:   2,
		Name:    "created_at",
		Type:    "timestamp",
		Null:    false,
		Key:     "",
		Default: "CURRENT_TIMESTAMP",
		Extra:   "DEFAULT_GENERATED",
		Comment: "",
	},
	"updated_at": {
		Index:   3,
		Name:    "updated_at",
		Type:    "timestamp",
		Null:    false,
		Key:     "",
		Default: "CURRENT_TIMESTAMP",
		Extra:   "DEFAULT_GENERATED on update CURRENT_TIMESTAMP",
		Comment: "",
	},
	"deleted_at": {
		Index:   4,
		Name:    "deleted_at",
		Type:    "timestamp",
		Null:    true,
		Key:     "",
		Default: nil,
		Extra:   "",
		Comment: "",
	},
}

// SetRolePermissionsTableFields registers the table fields definition to the database instance.
// db: database instance that implements gdb.DB interface.
// schema: optional schema/namespace name, especially for databases that support schemas.
func SetRolePermissionsTableFields(ctx context.Context, db gdb.DB, schema ...string) error {
	return db.GetCore().SetTableFields(ctx, "role_permissions", RolePermissions, schema...)
}

```
2025-10-15 15:50:16 +08:00
613
2742c98c06 fix(os/gcron): unit testing case of package gcron occasionally failed (#4419)
fix https://github.com/gogf/gf/issues/3999

1. fix  jobWaiter   sync.WaitGroup  data race
2. fix logger      glog.ILogger data race

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 15:17:05 +08:00
4226a23a39 feat(container/garray): add TArray (#4466)
Add TArray[T] for wrapping Array

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 15:08:26 +08:00
613
b8e414e125 fix(os/gcache): defaultcache lazy init (#4468)
defaultcache更改为懒加载,在用户使用redis缓存时,避免了程序启动时不必要的初始化开销。


<img width="2638" height="806" alt="image"
src="https://github.com/user-attachments/assets/96bb0097-8463-4303-971c-ee1a9ef671a6"
/>

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 15:01:47 +08:00
08c34b5ed7 feat(gf/build): Add support for the Loongson architecture (loong64) (#4467)
添加对龙芯架构(loong64)的支持

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-10-15 14:38:42 +08:00
416f314390 fix(contrib/drivers/pgsql): Merge duplicated fields, especially for key constraints. (#4465)
pgsql 执行TableFields 或者字段信息时需要合并key信息
2025-10-13 18:16:09 +08:00
98f0c36a1d fix: update gf cli to v2.9.4 (#4463)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: hailaz <hailaz@users.noreply.github.com>
2025-10-11 15:10:30 +08:00
2b7b4c8581 fix: v2.9.4 (#4461)
Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-10-11 14:58:01 +08:00
613
b8844f3d40 fix(net/ghttp): attachment filename support utf8 (#4459) 2025-10-10 10:32:02 +08:00
3e2176d799 fix:(cmd/gf): matching for table ex fix bug (#4458)
fix the bug: sometimes it won't remove all broad matching talbenames.

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-10-09 12:08:28 +08:00
2f9225057f fix(contrib/drivers/mysql): Fix unit test issue for batch insert in MySQL driver (#4456)
Resolve a temporary issue in the unit tests for batch insertion by
adjusting user IDs.

因为底层的插入随机性,会导致单元测试偶发失败。
2025-09-30 14:33:46 +08:00
7b373446dc feat(cmd/gf): add broad matching to gf gen dao's tableEx attribute. (#4453)
Add "*" and "?" to tableEx for "gf gen dao".

The "*" will match none or some char. And the "?" only match one char.

Co-authored-by: hailaz <739476267@qq.com>
2025-09-30 11:27:03 +08:00
0f6d47c7a8 fix(container/gqueue): Optimize queue length calculation and loop structure in test cases (#4455)
fixed #4376
2025-09-29 17:26:39 +08:00
f24729206b fix(database/gdb): Resolved the schema error in the database output log when using the database sharding feature (#4319)
error log
```
2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [  0 ms] [default] [db_0] [rows:1  ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1

2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [  1 ms] [default] [db_0] [rows:1  ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1

```
right log
```
2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [  0 ms] [default] [db_0] [rows:1  ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1

2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [  1 ms] [default] [db_1] [rows:1  ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1
```

```

type DbShardingRule struct {
}

func (d *DbShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) {
	if name, ok := value.(string); ok && (len(name) > 0) && gstr.HasPrefix(name, config.Prefix) {
		return name, nil
	}
	return "default", nil
}

func (d *DbShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) {
	return "", nil
}
```

```
config := gdb.ShardingConfig{
		Schema: gdb.ShardingSchemaConfig{
			Enable: true,
			Prefix: "db_",
			Rule:   &DbShardingRule{},
		},
	}
dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_0").Where("id", 1).One()
dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_1").Where("id", 2).One()
```
我有两个完全一样的数据库db_0和db_1,两个custom表里都只有一条数据,id是1和2,执行上面两条查询得的结果是正确的,
输出日志中应该分别是`[default] [db_0]`和`[default] [db_1]`,但是实际输出的都是`[default]
[db_0]`,

查看具体代码实现,该日志由Core实例中获取group和schema构成,而实际执行sql时如果使用了分库特性那么执行sql的link会使用当前面schema重新生成,但是没有重新赋给Core实例,所以输出日志的时候还是错的,只需要在生成link后生成日志前把schema赋给Core就行,方法执行完成后defer将schema重置回去,防止有人重复使用同一个gdb对象造成困扰

![SCR-20250618-ovoc](https://github.com/user-attachments/assets/815c364e-939f-4a2c-9669-d5b7d2742511)

![SCR-20250618-ovvk](https://github.com/user-attachments/assets/e7d0e375-78e6-4748-90ac-d02dba18720f)

![SCR-20250618-ozsu](https://github.com/user-attachments/assets/faa6d69b-331e-476b-8bf8-f62e564b04d3)

![SCR-20250618-ozwj](https://github.com/user-attachments/assets/15c524dc-dc19-4499-a3d3-32bf1d918a3a)
2025-09-28 17:57:27 +08:00
7e9715ab1d feat(contrib/drivers/mssql): mssql support LastInsertId (#4051)
修复mssqlserver的InsertAndGetId方法;插入记录如果是自增主键则返回ID

---------

Co-authored-by: 林孝义 <linxy@3755.com>
Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-09-28 17:55:08 +08:00
f565a347c4 fix(database/gdb): Fix GetArray return type and add Bools method (#4452)
Change the return type of the GetArray method to Array for consistency
in data structure. Introduce a new Bools method to convert Vars to a
slice of bools, along with corresponding test cases to validate the
functionality.

```go
// 改进前
res, _ := db.Model(table).Fields("id").Array()
ids := make([]int64, 0, len(res))
for _, v := range res {
ids = append(ids, v.Int64())
}
g.Dump(ids)
// 改进后
res, _ := db.Model(table).Fields("id").Array()
ids := res.Int64s()
g.Dump(ids)
```
2025-09-28 17:08:37 +08:00
f172e61585 fix(net/ghttp): Server Domain if is empty str, bind handler pattern will add @ which is not expect #4100 (#4101)
fix https://github.com/gogf/gf/issues/4100

---------

Co-authored-by: elonnzhang <elonnzhang@tencent.com>
Co-authored-by: hailaz <739476267@qq.com>
2025-09-26 18:49:36 +08:00
22d873f6bd fix(gerror): Fixed serialization failure issue when gerror.Error text field contains quote symbols (#4449)
如果调用internal 下的json.Marshal时 参数如果是gerror.Error 并且 字段中带有符号" 会导致序列化失败
原因是原gerror.Error 的MarshalJson方法只对字符串做了简单的处理 如果err.Error 返回的字符串中有符号"
这个符号会在序列化的时候被认为是字符串的终止导致序列化失败
<img width="854" height="186" alt="image"
src="https://github.com/user-attachments/assets/9a1e6d72-943f-41ad-a487-8a3c0f28f9f0"
/>

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-09-26 16:07:47 +08:00
613
b60b04e27a fix(database/gdb): performance improvement in fields grouping when in… (#4440)
fix https://github.com/gogf/gf/issues/3906

<img width="2604" height="980" alt="image"
src="https://github.com/user-attachments/assets/50852928-7ff5-4676-8ecf-6960c184e805"
/>

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-09-26 11:11:14 +08:00
613
d2bc5d812b fix(cmd/gf): run AddSigHandlerShutdown cannot work well (#4441)
https://github.com/gogf/gf/issues/3752

Sending [Interrupt] on Windows is not implemented.

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-09-23 10:58:24 +08:00
613
0648fd688e fix(cmd/gf): add extra option to controller the behavior downloading … (#4435)
https://github.com/gogf/gf/issues/3938
2025-09-22 17:30:06 +08:00
d0cfcce85b ci: Add CodeQL analysis workflow configuration (#4436) 2025-09-18 17:55:19 +08:00
2518d490c3 ci: Add Scorecard workflow for supply-chain security (#4437) 2025-09-18 17:55:03 +08:00
edc96a8c16 feat(database/gdb): Add the function of obtaining all configurations to facilitate business operations such as verification after addition. (#4389)
…ss operations such as verification after addition.

---------

Signed-off-by: sxp20008 <81209245@qq.com>
Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: hailaz <739476267@qq.com>
2025-09-18 16:21:02 +08:00
22427a08ad docs(i18n/gi18n): deleting the duplicate package documents (#4251)
Avoid in https://pkg.go.dev/github.com/gogf/gf/v2/i18n/gi18n display
duplicate content.

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-09-05 10:09:45 +08:00
41a5484620 fix(database/gredis): gredis support get raw client (#4306)
Fixes https://github.com/gogf/gf/issues/4298
Fixes https://github.com/gogf/gf/issues/2196
Fixes https://github.com/gogf/gf/issues/2135
```go
import goredis "github.com/redis/go-redis/v9"

func ExampleUsage(ctx context.Context, redis *Redis) error {
	client := redis.Client()
	universalClient, ok := client.(goredis.UniversalClient)
	if !ok {
		return errors.New("failed to assert to UniversalClient")
	}

	// Use universalClient for advanced operations like Pipeline
	pipe := universalClient.Pipeline()
	pipe.Set(ctx, "key1", "value1", 0)
	pipe.Set(ctx, "key2", "value2", 0)
	results, err := pipe.Exec(ctx)
	if err != nil {
		return err
	}
	// ... handle results
	return nil
}
```

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-09-03 16:09:43 +08:00
325ee45a55 fix: update gf cli to v2.9.3 (#4418)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: hailaz <hailaz@users.noreply.github.com>
2025-09-03 12:53:52 +08:00
627aa5d27f fix: Update version to v2.9.3 (#4417)
Change all references from v2.9.2 to v2.9.3 and update the release name
accordingly.
2025-09-03 12:48:06 +08:00
47db44843e fix(os/gtime): fix gtime time string handle logic (#4409)
1、gtime的StrToTime方法优化对时间字段格式兼容处理,并且针对时间越界抛出异常。
2、移除部分无用代码:
<img width="697" height="282" alt="image"
src="https://github.com/user-attachments/assets/92a88140-37c0-4ee1-aef7-c6418e9edd06"
/>

Fixes #4394

---------

Signed-off-by: Zjmainstay <hzgdys@163.com>
Co-authored-by: hailaz <739476267@qq.com>
2025-09-03 12:19:27 +08:00
a39498f74f fix: update gf cli to v2.9.3-rc4 (#4416)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: hailaz <hailaz@users.noreply.github.com>
2025-09-03 11:51:14 +08:00
80b866e11c fix: Update dependencies and exclude test data from go.mod processing (#4415)
Update dependencies and ensure that go.mod files in the test data
directory are excluded from processing during the tag creation for the
CLI tool.
2025-09-03 11:42:44 +08:00
2a77d3203e fix: Improve the typeMap check logic of "gf gen dao" (#4410)
Let it can support such as "Array(UInt256)", "Array(FixedString(25))".
Now we can typeMap as:
"array(uint256)": []big.Int
"array(fixedstring(25))": [25]string
"array(fixedstring)": []string
"array": []any

Now it will first match more precise rules. So it will match
array(uint256) and array(fixedstring(25)) first. Then the
"array(fixedstring)", the last is "array"
2025-09-03 11:16:38 +08:00
3c451bef82 fix: path ./cmd/gf (#4414) 2025-09-03 10:53:50 +08:00
5073f25691 chore: chmod +x update_version.sh (#4413) 2025-09-03 10:36:56 +08:00
40e97f1325 fix: #4269 (#4412)
fixed #4269
2025-09-02 22:49:05 +08:00
502d158bc0 fix: version 2.9.2 (#4405) 2025-09-01 15:33:50 +08:00
f08897a114 chore(tablewriter): upgrade to v1.0.9 and refactor table rendering logic (#4352)
### What’s Changed

* Upgraded `github.com/olekukonko/tablewriter` from previous version to
**v1.0.9**.

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-09-01 12:20:22 +08:00
b6181e4bde fix: report coverage on the latest go version (#4398) 2025-08-29 16:10:37 +08:00
613
f8cdeae2d7 fix(net/goai): fix g.Meta is passed as parameters of a request (#4397)
fix https://github.com/gogf/gf/issues/4247

<img width="2860" height="2308" alt="image"
src="https://github.com/user-attachments/assets/755cfe8a-b3ae-4c5a-ba04-68cc1b188ca7"
/>
2025-08-29 15:30:28 +08:00
bea060af4c feat: update linter config and deprecation notice (#4399)
- Add `gofmt` rewrite-rules to `.golangci.yml` for code formatting
consistency.
- Update deprecation comment in `gpage.go` to specify removal in version
3.0.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-29 15:26:56 +08:00
94cc233325 fix: disable specific staticcheck rules and update lint config (#4396)
fix: disable specific staticcheck rules and update lint config

- Disabled staticcheck rules SA1029, SA1019, S1000, and related checks
in `.golangci.yml` to filter out unwanted linter errors.
- Updated staticcheck checks list for more precise linting control.
- Clarified configuration for easier maintenance and future updates.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-29 10:32:30 +08:00
fsl
71743e6903 chore: add OpenSSF Scorecard for README.md (#3696)
Why is this needed:

The OpenSSF Scorecard improves open-source project's security by
providing automated, transparent assessments of their security
practices. It will help you identify vulnerabilities, adhere to best
practices, and continuously enhance your security posture, increasing
user trust and reducing the risk of security exploits.

I'll be the one to create the PR to add the scorecard GitHub action, and
I will also work with you to remediate the identified vulnerabilities.
I'll go through each [scorecard
check](https://github.com/ossf/scorecard/blob/main/docs/checks.md) to
see where the score has dropped and how it can be improved.


Integrate [scorecard](https://github.com/ossf/scorecard) in CI, and
display a Scorecard badge on the gogf repository
You also need to manually create a project, refer to
https://bestpractices.coreinfrastructure.org/en/projects
Manually create an gogf organization to report results, please see
https://sonarcloud.io/explore/projects?sort=-analysis_date

Signed-off-by: fsl <1171313930@qq.com>
2025-08-29 10:28:29 +08:00
f9ec3b19f7 fix(net/ghttp): wrong in-tag param parse for query param (#4227) (#4228)
Fixed issue #4227 and add unit test

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-08-29 09:46:48 +08:00
ee24da4e72 refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.

**Type alias migration to `any`:**

* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)

**Generated code and templates:**

* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)

**Container and utility updates:**

* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)

These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00
26f20787ba perf(net/gclient): optimize default http.Transport connection pool configuration (#4390) 2025-08-28 15:08:25 +08:00
1371b3bad5 fix: revert #4388 (#4392) 2025-08-28 13:00:08 +08:00
1da73451b9 fix: spacing in min value validation message (#4388)
Fix spacing in min value validation message
2025-08-27 19:05:55 +08:00
94b623e126 fix: update dependencies to version v2.9.1 for various contrib modules and drivers (#4386)
Fixes #4385
2025-08-27 12:21:38 +08:00
4262aa254d chore(deps): Update dependent versions to enhance compatibility and security (#4380)
#4344

---------

Co-authored-by: houseme <housemecn@gmail.com>
2025-08-23 14:53:49 +08:00
8cff64915b fix(tracing): set database span kind to client (#4334)
In [OpenTelemetry
spec](https://opentelemetry.io/docs/specs/semconv/database/database-spans/),
the span kind of database should be `client`.
2025-08-22 22:49:13 +08:00
bec98e8de0 fix(database/gdb): clickhouse can not support int128/int256/uint128/uint256 (#4370)
When use clickhouse and use field type int128/int256/uint128/uint256. It
can not work well and it will return 0 value.
Add the new field types to let it work well.

By the way, the data struct field need be set to big.Int when use the
types.
2025-08-22 22:05:15 +08:00
a6dbf4b7eb feat: upgrade workflow checkout version v5 (#4381)
feat: upgrade workflow checkout version v5

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-22 21:58:00 +08:00
0c2f60468b chore(deps): bump github.com/redis/go-redis/v9 from 9.7.0 to 9.12.1 in /contrib/nosql/redis (#4215)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis)
from 9.7.0 to 9.7.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/redis/go-redis/releases">github.com/redis/go-redis/v9's
releases</a>.</em></p>
<blockquote>
<h2>v9.7.3</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: handle network error on SETINFO (<a
href="https://redirect.github.com/redis/go-redis/issues/3295">#3295</a>)
(<a
href="https://github.com/redis/go-redis/security/advisories/GHSA-92cp-5422-2mw7">CVE-2025-29923</a>)</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/redis/go-redis/compare/v9.7.1...v9.7.3">https://github.com/redis/go-redis/compare/v9.7.1...v9.7.3</a></p>
<h2>v9.7.1</h2>
<h1>Changes</h1>
<ul>
<li>Recognize byte slice for key argument in cluster client hash slot
computation (<a
href="https://redirect.github.com/redis/go-redis/issues/3049">#3049</a>)</li>
<li>fix(search&amp;aggregate):fix error overwrite and typo <a
href="https://redirect.github.com/redis/go-redis/issues/3220">#3220</a>
(<a
href="https://redirect.github.com/redis/go-redis/issues/3224">#3224</a>)</li>
<li>fix: linter configuration (<a
href="https://redirect.github.com/redis/go-redis/issues/3279">#3279</a>)</li>
<li>fix(search): if ft.aggregate use limit when limitoffset is zero (<a
href="https://redirect.github.com/redis/go-redis/issues/3275">#3275</a>)</li>
<li>Reinstate read-only lock on hooks access in dialHook to fix data
race (<a
href="https://redirect.github.com/redis/go-redis/issues/3225">#3225</a>)</li>
<li>fix: flaky ClientKillByFilter test (<a
href="https://redirect.github.com/redis/go-redis/issues/3268">#3268</a>)</li>
<li>chore: fix some comments (<a
href="https://redirect.github.com/redis/go-redis/issues/3226">#3226</a>)</li>
<li>fix(aggregate, search): ft.aggregate bugfixes (<a
href="https://redirect.github.com/redis/go-redis/issues/3263">#3263</a>)</li>
<li>fix: add unstableresp3 to cluster client (<a
href="https://redirect.github.com/redis/go-redis/issues/3266">#3266</a>)</li>
<li>Fix race condition in clusterNodes.Addrs() (<a
href="https://redirect.github.com/redis/go-redis/issues/3219">#3219</a>)</li>
<li>SortByWithCount FTSearchOptions fix (<a
href="https://redirect.github.com/redis/go-redis/issues/3201">#3201</a>)</li>
<li>Eliminate redundant dial mutex causing unbounded connection queue
contention (<a
href="https://redirect.github.com/redis/go-redis/issues/3088">#3088</a>)</li>
<li>Add guidance on unstable RESP3 support for RediSearch commands to
README (<a
href="https://redirect.github.com/redis/go-redis/issues/3177">#3177</a>)</li>
</ul>
<h2>🚀 New Features</h2>
<ul>
<li>Add guidance on unstable RESP3 support for RediSearch commands to
README (<a
href="https://redirect.github.com/redis/go-redis/issues/3177">#3177</a>)</li>
</ul>
<h2>🐛 Bug Fixes</h2>
<ul>
<li>fix(search): if ft.aggregate use limit when limitoffset is zero (<a
href="https://redirect.github.com/redis/go-redis/issues/3275">#3275</a>)</li>
<li>fix: add unstableresp3 to cluster client (<a
href="https://redirect.github.com/redis/go-redis/issues/3266">#3266</a>)</li>
<li>fix(aggregate, search): ft.aggregate bugfixes (<a
href="https://redirect.github.com/redis/go-redis/issues/3263">#3263</a>)</li>
<li>SortByWithCount FTSearchOptions fix (<a
href="https://redirect.github.com/redis/go-redis/issues/3201">#3201</a>)</li>
<li>Recognize byte slice for key argument in cluster client hash slot
computation (<a
href="https://redirect.github.com/redis/go-redis/issues/3049">#3049</a>)</li>
</ul>
<h2>Contributors</h2>
<p>We'd like to thank all the contributors who worked on this
release!</p>
<p><a
href="https://github.com/ofekshenawa"><code>@​ofekshenawa</code></a>, <a
href="https://github.com/Cgol9"><code>@​Cgol9</code></a>, <a
href="https://github.com/LINKIWI"><code>@​LINKIWI</code></a>, <a
href="https://github.com/shawnwgit"><code>@​shawnwgit</code></a>, <a
href="https://github.com/zhuhaicity"><code>@​zhuhaicity</code></a>, <a
href="https://github.com/bitsark"><code>@​bitsark</code></a>, <a
href="https://github.com/vladvildanov"><code>@​vladvildanov</code></a>,
<a href="https://github.com/ndyakov"><code>@​ndyakov</code></a></p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/redis/go-redis/compare/v9.7.0...v9.7.1">https://github.com/redis/go-redis/compare/v9.7.0...v9.7.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a29d91d9ca"><code>a29d91d</code></a>
release 9.7.3, retract 9.7.2 (<a
href="https://redirect.github.com/redis/go-redis/issues/3314">#3314</a>)</li>
<li><a
href="ce3034c7b3"><code>ce3034c</code></a>
bump version to 9.7.2</li>
<li><a
href="0af2b32f93"><code>0af2b32</code></a>
fix: handle network error on SETINFO (<a
href="https://redirect.github.com/redis/go-redis/issues/3295">#3295</a>)
(CVE-2025-29923)</li>
<li><a
href="3d041a1dd6"><code>3d041a1</code></a>
release: 9.7.1 patch (<a
href="https://redirect.github.com/redis/go-redis/issues/3278">#3278</a>)</li>
<li>See full diff in <a
href="https://github.com/redis/go-redis/compare/v9.7.0...v9.7.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/redis/go-redis/v9&package-manager=go_modules&previous-version=9.7.0&new-version=9.7.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/gogf/gf/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 17:11:09 +08:00
656ae070da fix(cmd/gf): fix gen sharding dao in multiple shardingPattern tables … (#4379)
fix #4378 
fix #4330

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-22 16:46:23 +08:00
2d5fcd73cb chore: upgrade dependencies to latest versions and fix security vulne… (#4237)
This PR includes the following updates and fixes:

- **Dependency upgrades**: Updated all dependencies in `go.mod` to their
latest versions to ensure compatibility and leverage the latest features
and fixes.
- **Security fixes**:
- Resolved known vulnerabilities in `golang.org/x/net` by upgrading to
the latest secure version.
- Addressed security issues in `golang.org/x/crypto` by upgrading to the
latest secure version.

These changes improve the overall security and stability of the project.
Please review the changes and ensure compatibility with the updated
dependencies.

---------

Co-authored-by: hailaz <739476267@qq.com>
2025-08-22 15:29:16 +08:00
82043856f4 chore(polaris): Bump github.com/polarismesh/polaris-go from v1.5.8 to v1.6.1 (#4241)
- Updated `github.com/polarismesh/polaris-go` dependency to v1.6.1 in
`go.mod`.
- Ensured compatibility with the latest changes in the Polaris Go SDK.
- Verified that all related functionality works as expected after the
upgrade.
- Resolved any potential breaking changes introduced in the new version.

This upgrade includes performance improvements, bug fixes, and new
features provided by the Polaris Go SDK.
2025-08-22 15:07:34 +08:00
7ffdff37e4 chore: upgrade golangci-lint configuration and optimize codebase (#4236)
This PR includes the following changes:

- **Upgrade `.golangci.yml`**: Updated the configuration file to align
with the latest golangci-lint version, ensuring compatibility and
leveraging new features.
- **Refactor GitHub Action workflow**: Modified `golangci-lint.yml` in
the GitHub Actions workflow to reflect the updated configuration and
improve CI performance.
- **Codebase optimization**: Refactored code to address issues and
warnings raised by the updated golangci-lint rules, including:
  - Improved function length and complexity.
  - Enhanced error handling and variable naming conventions.
- Fixed minor issues such as unused imports and formatting
inconsistencies.

These changes aim to maintain code quality, ensure compatibility with
the latest tools, and improve overall maintainability.
2025-08-22 13:29:09 +08:00
613
24083b865d fix(internal/utils): fix +.1 is pass checks numeric (#4374) 2025-08-21 15:44:26 +08:00
8cb64c9f88 fix(cmd/gf): "unknown time zone" when using "gf gen dao" for clickhouse on windows platform (#4368)
fix bug: could not load time location: "unknown time zone Asia/Shanghai"

The bug will appear when I use gf in windows to do "gf gen dao" for
clickhouse.
2025-08-15 13:35:59 +08:00
5fa656d1cc fix(os/gtime): add handling for nil time pointers to avoid causing panic (#4323)
from https://github.com/gogf/gf/pull/4322
Fixes https://github.com/gogf/gf/issues/4307
2025-06-24 15:53:47 +08:00
09ec90746a chore: bump golang.org/x/tools to v0.34.0 for Go 1.25 compatibility (#4313)
chore: bump golang.org/x/tools to v0.34.0 for Go 1.25 compatibility

```
   # golang.org/x/tools/internal/tokeninternal
  /Users/brew/Library/Caches/Homebrew/go_mod_cache/pkg/mod/golang.org/x/tools@v0.21.1-0.20240508182429-e35e4ccd0d2d/internal/tokeninternal/tokeninternal.go:64:9: invalid array length -delta * delta (constant -256 of type int64)
```

relates to https://github.com/Homebrew/homebrew-core/pull/226636
2025-06-20 21:21:03 +08:00
c0d1e44526 fix(database/gdb): support multiple order fields in gdb_model_with and merged #4272 fix scanning functionality for deep slice types (#4320)
Fix:#4311
Merged: #4272
2025-06-20 21:09:41 +08:00
b323862b4c Merge pull request #1 from fainc/master
Update util/gconv/gconv_z_unit_scan_test.go
2025-06-20 19:23:04 +08:00
3d9cdb8997 fix(gdb): support multiple order fields in "with" 2025-06-20 19:11:01 +08:00
7180d895ea Update util/gconv/gconv_z_unit_scan_test.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-20 18:17:40 +08:00
afb1595fbe Merge branch 'gogf:master' into master 2025-06-20 18:07:15 +08:00
6637add9ca fix(net/ghttp): improve GetMetaTag method to handle nil and type checks (#4284)
修复没有做handler参数数量判断导致可能出现的数组越界问题
2025-06-19 09:43:24 +08:00
bc862cf97b chore: bump golang.org/x/tools to v0.34.0 for Go 1.25 compatibility
Signed-off-by: Rui Chen <rui@chenrui.dev>
2025-06-14 13:34:25 -04:00
9033ca087b fix(net/ghttp): improve GetMetaTag method to handle nil and type checks 2025-05-16 17:55:00 +08:00
b985fd978c Merge branch 'master' into master 2025-05-07 20:02:12 +08:00
88c4471500 fix(ci): change base image from expired ubuntu-22.04 to ubuntu-latest (#4273) 2025-05-07 19:06:00 +08:00
b73e2047db fix(gconv): fix scanning functionality for deep slice types 2025-05-06 13:37:45 +08:00
1534abdb05 feat(util/gpage): marked deprecated (#4230) 2025-04-02 19:56:28 +08:00
fee38b4531 feat(net/ghttp): enhance GetHeader method to support default values (#4210) 2025-03-25 20:42:30 +08:00
69e3362d0d feat: new version v2.9.0 (#4204) 2025-03-17 15:52:26 +08:00
9a61a6970f fix(database/gdb): fix transaction propagation feature (#4199) 2025-03-17 14:50:07 +08:00
abf77fac50 fix(cmd/gf): invalid binary suffix after installing binary using custom renamed file name that has suffix with . character (#4207) 2025-03-17 13:48:54 +08:00
07696fc779 feat(net/ghttp): add GetMetaTag function to retrieve metadata value for HandlerItem (#4206) 2025-03-17 09:21:00 +08:00
bc1e1019c5 refract(util/gconv): change Converter interface definition for more convenient usage (#4202) 2025-03-14 18:23:07 +08:00
766 changed files with 20157 additions and 9360 deletions

View File

@ -29,7 +29,7 @@ concurrency:
env:
TZ: "Asia/Shanghai"
# for unit testing cases of some components that only execute on the latest go version.
LATEST_GO_VERSION: "1.23"
LATEST_GO_VERSION: "1.25"
jobs:
code-test:
@ -38,19 +38,18 @@ jobs:
# 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
# When adding new go version to the list, make sure:
# 1. Update the `LATEST_GO_VERSION` env variable.
# 2. Update the `Report Coverage` action.
# 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
go-version: [ "1.22", "1.23" ]
go-version: [ "1.23", "1.24", "1.25" ]
goarch: [ "386", "amd64" ]
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
# Service containers to run with `code-test`
services:
# Etcd service.
# docker run -d --name etcd -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes bitnami/etcd:3.4.24
# docker run -d --name etcd -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes bitnamilegacy/etcd:3.4.24
etcd:
image: bitnami/etcd:3.4.24
image: bitnamilegacy/etcd:3.4.24
env:
ALLOW_NONE_AUTHENTICATION: yes
ports:
@ -206,7 +205,7 @@ jobs:
timezoneLinux: "Asia/Shanghai"
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Start Apollo Containers
run: docker compose -f ".github/workflows/apollo/docker-compose.yml" up -d --build
@ -227,9 +226,9 @@ jobs:
cache-dependency-path: '**/go.sum'
- name: Install Protoc
uses: arduino/setup-protoc@v2
uses: arduino/setup-protoc@v3
with:
version: "29.x"
version: "31.x"
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install the protocol compiler plugins for Go
@ -263,8 +262,8 @@ jobs:
- name: Report Coverage
uses: codecov/codecov-action@v4
# Only report coverage on the latest go version and amd64 arch
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.go-version == '1.23' && matrix.goarch == 'amd64' }}
# Only report coverage on the latest go version
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.go-version == env.LATEST_GO_VERSION }}
with:
flags: go-${{ matrix.go-version }}-${{ matrix.goarch }}
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -30,7 +30,7 @@ concurrency:
env:
TZ: "Asia/Shanghai"
# for unit testing cases of some components that only execute on the latest go version.
LATEST_GO_VERSION: "1.23"
LATEST_GO_VERSION: "1.25"
jobs:
code-test:
@ -40,7 +40,7 @@ jobs:
# When adding new go version to the list, make sure:
# 1. Update the `LATEST_GO_VERSION` env variable.
# 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
go-version: [ "1.22", "1.23" ]
go-version: [ "1.23", "1.24", "1.25" ]
goarch: [ "386", "amd64" ]
runs-on: ubuntu-latest
@ -52,7 +52,7 @@ jobs:
timezoneLinux: "Asia/Shanghai"
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Start Minikube
uses: medyagh/setup-minikube@master

100
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,100 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: [ "master", "develop" ]
pull_request:
branches: [ "master", "develop" ]
schedule:
- cron: '0 21 * * *'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: go
build-mode: autobuild
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Golang ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Mirror GitHub to Gitee
uses: Yikun/hub-mirror-action@v1.4
with:

View File

@ -4,7 +4,7 @@
# If a copy of the MIT was not distributed with this file,
# You can obtain one at https://github.com/gogf/gf.
name: GolangCI Lint
name: golangci-lint
on:
push:
branches:
@ -29,23 +29,24 @@ jobs:
golang-ci:
strategy:
matrix:
go-version: [ 'stable' ]
go-version: [ "stable" ]
name: golang-ci-lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Golang ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: golang-ci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v8
with:
# Required: specify the golangci-lint version without the patch version to always use the latest patch.
version: v1.64.5
only-new-issues: true
skip-cache: true
github-token: ${{ secrets.GITHUB_TOKEN }}
args: --timeout 3m0s --config=.golangci.yml -v
args: --config=.golangci.yml -v

View File

@ -23,6 +23,6 @@ jobs:
with:
actions: 'check-inactive'
inactive-label: 'inactive'
inactive-day: 7
inactive-day: 30
issue-state: open
exclude-labels: 'bug,planned,$exclude-empty'

View File

@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Github Code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set Up Golang Environment
uses: actions/setup-go@v5
with:
go-version: 1.23.4
go-version: 1.25
- name: Build CLI Binary
run: |
@ -34,7 +34,7 @@ jobs:
- name: Build CLI Binary For All Platform
run: |
cd cmd/gf
gf build main.go -n gf -a all -s all -p temp
gf build main.go -n gf -a all -s linux,windows,darwin,freebsd,netbsd,openbsd -p temp
- name: Move Files Before Release
run: |
@ -52,7 +52,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
name: GoFrame Release ${{ github.ref }}
name: GoFrame Release ${{ github.ref_name }}
draft: false
prerelease: false

80
.github/workflows/scorecard.yml vendored Normal file
View File

@ -0,0 +1,80 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '0 21 * * *'
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@v2.4.1
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@v4.6.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View File

@ -19,7 +19,7 @@ for file in `find . -name go.mod`; do
continue 1
fi
# package kuhecm was moved to sub ci procedure.
# package kubecm was moved to sub ci procedure.
if [ "kubecm" = $(basename $dirpath) ]; then
continue 1
fi

View File

@ -2,26 +2,67 @@
coverage=$1
# Function to compare version numbers
version_compare() {
local ver1=$1
local ver2=$2
# Remove 'go' prefix and 'v' if present
ver1=$(echo "$ver1" | sed 's/^go//; s/^v//')
ver2=$(echo "$ver2" | sed 's/^go//; s/^v//')
# Split versions into major.minor format
local major1=$(echo "$ver1" | cut -d. -f1)
local minor1=$(echo "$ver1" | cut -d. -f2)
local major2=$(echo "$ver2" | cut -d. -f1)
local minor2=$(echo "$ver2" | cut -d. -f2)
# Compare versions: return 0 if ver1 <= ver2, 1 otherwise
if [ "$major1" -lt "$major2" ]; then
return 0
elif [ "$major1" -eq "$major2" ] && [ "$minor1" -le "$minor2" ]; then
return 0
else
return 1
fi
}
# Get current Go version
current_go_version=$(go version | grep -oE 'go[0-9]+\.[0-9]+')
# find all path that contains go.mod.
for file in `find . -name go.mod`; do
dirpath=$(dirname $file)
echo $dirpath
echo "Processing: $dirpath"
# package kuhecm needs golang >= v1.19
if [ "kubecm" = $(basename $dirpath) ]; then
if ! go version|grep -qE "go1.[2-9][0-9]"; then
echo "ignore kubecm as go version: $(go version)"
continue 1
fi
else
continue 1
# Only process kubecm directory, skip others
if [ "kubecm" != $(basename $dirpath) ]; then
echo " Skipping: not kubecm directory"
continue
fi
cd $dirpath
go mod tidy
go build ./...
go test ./... -race || exit 1
# Read Go version requirement from go.mod
if [ -f "go.mod" ]; then
go_mod_version=$(grep '^go ' go.mod | awk '{print $2}' | head -1)
if [ -n "$go_mod_version" ]; then
echo " go.mod requires: go$go_mod_version"
echo " current version: $current_go_version"
# Check if go.mod version requirement is satisfied by current Go version
if version_compare "$go_mod_version" "$current_go_version"; then
echo " ✓ Version requirement satisfied, proceeding with build and test"
go mod tidy
go build ./...
go test ./... -race || exit 1
else
echo " ✗ Current Go version ($current_go_version) does not meet requirement (go$go_mod_version), skipping"
fi
fi
fi
cd -
done

50
.github/workflows/scripts/update_version.sh vendored Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Check if the number of parameters is 2
if [ $# -ne 2 ]; then
echo "Invalid parameters, please execute in format: version.sh [directory] [version]"
echo "Example: version.sh ./contrib v1.0.0"
exit 1
fi
# Check if the first parameter is a directory and exists
if [ ! -d "$1" ]; then
echo "Error: Directory does not exist"
exit 1
fi
# Check if the second parameter starts with 'v'
if [[ "$2" != v* ]]; then
echo "Error: Version number does not start with 'v'"
exit 1
fi
workdir=$1
newVersion=$2
echo "Preparing to replace version numbers in all go.mod files under ${workdir} directory to ${newVersion}"
# Check if file exists
if [ -f "go.work" ]; then
# File exists, rename it
mv go.work go.work.${newVersion}
echo "Backup go.work file to avoid affecting the upgrade"
fi
for file in `find ${workdir} -name go.mod`; do
goModPath=$(dirname $file)
echo ""
echo "processing dir: $goModPath"
cd $goModPath
go mod tidy
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf"
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf" | xargs -L1 go get -v
go mod tidy
cd -
done
if [ -f "go.work.${newVersion}" ]; then
# File exists, rename it back
mv go.work.${newVersion} go.work
echo "Restore go.work file"
fi

View File

@ -1,53 +0,0 @@
name: Sonarcloud Scan
on:
schedule:
# Weekly on Saturdays.
- cron: '30 1 * * 6'
push:
branches: [ master ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Declare default permissions as read only.
permissions: read
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-22.04
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Used to receive a badge. (Upcoming feature)
id-token: write
# Needs for private repositories.
contents: read
actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@v2.4.0 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@v4
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2.2.1
with:
sarif_file: results.sarif

View File

@ -4,36 +4,56 @@ on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
env:
TZ: Asia/Shanghai
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
build:
name: Auto Creating Tags
runs-on: ubuntu-latest
steps:
- name: Checkout Github Code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Auto Creating Tags For Contrib Packages
run: |
git config --global user.email "tagrobot@goframe.org"
git config --global user.name "TagRobot"
# auto create tags for contrib packages.
for file in `find contrib -name go.mod`; do
tag=$(dirname $file)/$GITHUB_REF_NAME
tag=$(dirname $file)/${{ github.ref_name }}
git tag $tag
git push origin $tag
done
- name: update dependencies
run: |
go env -w GOPRIVATE=github.com/gogf/gf
.github/workflows/scripts/update_version.sh ./cmd/gf ${{ github.ref_name }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
commit-message: 'update gf cli to ${{ github.ref_name }}'
title: 'fix: update gf cli to ${{ github.ref_name }}'
base: master
branch: fix/${{ github.ref_name }}
delete-branch: true
- name: Commit & Push changes
uses: actions-js/push@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: fix/${{ github.ref_name }}
author_name: TagRobot
author_email: tagrobot@goframe.org
message: 'fix: update gf cli to ${{ github.ref_name }}'
- name: Auto Creating Tags For cli tool
run: |
git config --global user.email "tagrobot@goframe.org"
git config --global user.name "TagRobot"
# auto create tag for cli tool
for file in `find cmd -name go.mod`; do
tag=$(dirname $file)/$GITHUB_REF_NAME
for file in `find cmd -name go.mod -not -path "*/testdata/*"`; do
tag=$(dirname $file)/${{ github.ref_name }}
git tag $tag
git push origin $tag
done

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ go.work.sum
node_modules
.docusaurus
output
.example/
.golangci.bck.yml

View File

@ -1,325 +1,220 @@
## This file contains all available configuration options
## with their default values.
# See https://github.com/golangci/golangci-lint#config-file
# See https://golangci-lint.run/usage/configuration/
# Options for analysis running.
version: "2"
run:
# Timeout for analysis, e.g. 30s, 5m.
# Default: 1m
timeout: 5m
# Exit code when at least one issue was found.
# Default: 1
issues-exit-code: 2
# Include test files or not.
# Default: true
tests: false
# List of build tags, all linters use it.
# Default: []
build-tags: []
# If set, we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
#
# Allowed values: readonly|vendor|mod
# Default: ""
modules-download-mode: readonly
# Allow multiple parallel golangci-lint instances running.
# If false, golangci-lint acquires file lock on start.
# Default: false
allow-parallel-runners: true
# Allow multiple golangci-lint instances running, but serialize them around a lock.
# If false, golangci-lint exits with an error if it fails to acquire file lock on start.
# Default: false
allow-serial-runners: true
# Define the Go version limit.
# Mainly related to generics support since go1.18.
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17
go: '1.20'
# Number of operating system threads (`GOMAXPROCS`) that can execute golangci-lint simultaneously.
# If it is explicitly set to 0 (i.e. not the default) then golangci-lint will automatically set the value to match Linux container CPU quota.
# Default: the number of logical CPUs in the machine
concurrency: 4
# Main linters configurations.
# See https://golangci-lint.run/usage/linters
go: "1.25"
modules-download-mode: readonly
issues-exit-code: 2
tests: false
allow-parallel-runners: true
allow-serial-runners: true
linters:
# Disable all default enabled linters.
disable-all: true
# Custom enable linters we want to use.
default: none
enable:
- errcheck # Errcheck is a program for checking for unchecked errors in go programs.
- errchkjson # Checks types passed to the JSON encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted.
- funlen # Tool for detection of long functions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode.
- gci # Gci controls Go package import order and makes it always deterministic.
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # Provides diagnostics that check for bugs, performance and style issues.
- gosimple # Linter for Go source code that specializes in simplifying code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- misspell # Finds commonly misspelled English words in comments
- nolintlint # Reports ill-formed or insufficient nolint directives
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
- staticcheck # It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary.
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library.
- whitespace # Tool for detection of leading and trailing whitespace
issues:
exclude-rules:
# helpers in tests often (rightfully) pass a *testing.T as their first argument
- path: _test\.go
text: "context.Context should be the first parameter of a function"
linters:
- revive
# Yes, they are, but it's okay in a test
- path: _test\.go
text: "exported func.*returns unexported type.*which can be annoying to use"
linters:
- revive
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"
# https://golangci-lint.run/usage/linters
linters-settings:
# https://golangci-lint.run/usage/linters/#misspell
misspell:
locale: US
ignore-words:
- cancelled
# https://golangci-lint.run/usage/linters/#gofmt
gofmt:
# Simplify code: gofmt with `-s` option.
# Default: true
simplify: true
# Apply the rewrite rules to the source before reformatting.
# https://pkg.go.dev/cmd/gofmt
# Default: []
rewrite-rules: [ ]
# - pattern: 'interface{}'
# replacement: 'any'
# - pattern: 'a[b:len(a)]'
# replacement: 'a[b:]'
goimports:
# A comma-separated list of prefixes, which, if set, checks import paths
# with the given prefixes are grouped after 3rd-party packages.
# Default: ""
local-prefixes: github.com/gogf/gf/v2
gci:
# Section configuration to compare against.
# Section names are case-insensitive and may contain parameters in ().
# The default order of sections is `standard > default > custom > blank > dot > alias > localmodule`,
# If `custom-order` is `true`, it follows the order of `sections` option.
# Default: ["standard", "default"]
sections:
- standard # Standard section: captures all standard packages.
- blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled.
- default # Default section: contains all imports that could not be matched to another section type.
- dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled.
# - alias # Alias section: contains all alias imports. This section is not present unless explicitly enabled.
# - localmodule # Local module section: contains all local packages. This section is not present unless explicitly enabled.
- prefix(github.com/gogf/gf) # Custom section: groups all imports with the specified Prefix.
- prefix(github.com/gogf/gf/cmd) # Custom section: groups all imports with the specified Prefix.
- prefix(github.com/gogf/gfcontrib) # Custom section: groups all imports with the specified Prefix.
- prefix(github.com/gogf/gf/example) # Custom section: groups all imports with the specified Prefix.
# Skip generated files.
# Default: true
skip-generated: true
# Enable custom order of sections.
# If `true`, make the section order the same as the order of `sections`.
# Default: false
custom-order: true
# Drops lexical ordering for custom sections.
# Default: false
no-lex-order: false
# https://golangci-lint.run/usage/linters/#revive
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
revive:
ignore-generated-header: true
severity: error
- errcheck
- errchkjson
- funlen
- goconst
- gocritic
- govet
- misspell
- nolintlint
- revive
- staticcheck
- usestdlibvars
- whitespace
settings:
funlen:
lines: 340
statements: -1
goconst:
match-constant: false
min-len: 4
min-occurrences: 30
numbers: true
min: 5
max: 20
ignore-calls: false
gocritic:
disabled-checks:
- ifElseChain
- assignOp
- appendAssign
- singleCaseSwitch
- regexpMust
- typeSwitchVar
- elseif
govet:
disable:
- asmdecl
- assign
- atomic
- atomicalign
- bools
- buildtag
- cgocall
- composites
- copylocks
- deepequalerrors
- errorsas
- fieldalignment
- findcall
- framepointer
- httpresponse
- ifaceassert
- loopclosure
- lostcancel
- nilfunc
- nilness
- reflectvaluecompare
- shift
- shadow
- sigchanyzer
- sortslice
- stdmethods
- stringintconv
- structtag
- testinggoroutine
- tests
- unmarshal
- unreachable
- unsafeptr
- unusedwrite
enable-all: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
unusedresult:
funcs:
- pkg.MyFunc
- context.WithCancel
stringmethods:
- MyMethod
misspell:
locale: US
ignore-rules:
- cancelled
revive:
severity: error
rules:
- name: atomic
- name: line-length-limit
arguments:
- 380
severity: error
- name: unhandled-error
severity: warning
disabled: true
- name: var-naming
arguments:
- - ID
- URL
- IP
- HTTP
- JSON
- API
- UID
- Id
- Api
- Uid
- Http
- Json
- Ip
- Url
- - VM
severity: warning
disabled: true
- name: string-format
arguments:
- - core.WriteError[1].Message
- /^([^A-Z]|$)/
- must not start with a capital letter
- - fmt.Errorf[0]
- /(^|[^\.!?])$/
- must not end in punctuation
- - panic
- /^[^\n]*$/
- must not contain line breaks
severity: warning
disabled: false
- name: function-result-limit
arguments:
- 4
severity: warning
disabled: false
staticcheck:
checks: [ "all","-S1000","-S1009","-S1016","-S1023","-S1025","-S1029","-S1034","-S1040","-SA1016","-SA1019","-SA1029","-SA4006","-SA4015","-SA6003","-SA9003","-ST1003","-QF1001","-QF1002","-QF1003","-QF1006","-QF1007","-QF1008","-QF1011","-QF1012","-ST1011" ]
initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS" ]
dot-import-whitelist: [ "fmt" ]
http-status-code-whitelist: [ "200", "400", "404", "500" ]
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- name: atomic
- name: line-length-limit
severity: error
arguments: [ 380 ]
- name: unhandled-error
severity: warning
disabled: true
arguments: []
- name: var-naming
severity: warning
disabled: true
arguments:
# AllowList
- [ "ID","URL","IP","HTTP","JSON","API","UID","Id","Api","Uid","Http","Json","Ip","Url" ]
# DenyList
- [ "VM" ]
- name: string-format
severity: warning
disabled: false
arguments:
- - 'core.WriteError[1].Message'
- '/^([^A-Z]|$)/'
- must not start with a capital letter
- - 'fmt.Errorf[0]'
- '/(^|[^\.!?])$/'
- must not end in punctuation
- - panic
- '/^[^\n]*$/'
- must not contain line breaks
- name: function-result-limit
severity: warning
disabled: false
arguments: [ 4 ]
# https://golangci-lint.run/usage/linters/#funlen
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 340
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: -1
# https://golangci-lint.run/usage/linters/#goconst
goconst:
# Minimal length of string constant.
# Default: 3
min-len: 4
# Minimum occurrences of constant string count to trigger issue.
# Default: 3
# For subsequent optimization, the value is reduced.
min-occurrences: 30
# Ignore test files.
# Default: false
ignore-tests: true
# Look for existing constants matching the values.
# Default: true
match-constant: false
# Search also for duplicated numbers.
# Default: false
numbers: true
# Minimum value, only works with goconst.numbers
# Default: 3
min: 5
# Maximum value, only works with goconst.numbers
# Default: 3
max: 20
# Ignore when constant is not used as function argument.
# Default: true
ignore-calls: false
# https://golangci-lint.run/usage/linters/#gocritic
gocritic:
disabled-checks:
- ifElseChain
- assignOp
- appendAssign
- singleCaseSwitch
- regexpMust
- typeSwitchVar
- elseif
# https://golangci-lint.run/usage/linters/#gosimple
gosimple:
# Sxxxx checks in https://staticcheck.io/docs/configuration/options/#checks
# Default: ["*"]
checks: [
"all", "-S1000", "-S1001", "-S1002", "-S1008", "-S1009", "-S1016", "-S1023", "-S1025", "-S1029", "-S1034", "-S1040"
]
# https://golangci-lint.run/usage/linters/#govet
govet:
# Report about shadowed variables.
# Default: false
# check-shadowing: true
# Settings per analyzer.
settings:
# Analyzer name, run `go tool vet help` to see all analyzers.
printf:
# Comma-separated list of print function names to check (in addition to default, see `go tool vet help printf`).
# Default: []
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
# shadow:
# Whether to be strict about shadowing; can be noisy.
# Default: false
# strict: false
unusedresult:
# Comma-separated list of functions whose results must be used
# (in addition to defaults context.WithCancel,context.WithDeadline,context.WithTimeout,context.WithValue,
# errors.New,fmt.Errorf,fmt.Sprint,fmt.Sprintf,sort.Reverse)
# Default []
funcs:
- pkg.MyFunc
- context.WithCancel
# Comma-separated list of names of methods of type func() string whose results must be used
# (in addition to default Error,String)
# Default []
stringmethods:
- MyMethod
# Enable all analyzers.
# Default: false
enable-all: true
# Disable analyzers by name.
# Run `go tool vet help` to see all analyzers.
# Default: []
disable:
- asmdecl
- assign
- atomic
- atomicalign
- bools
- buildtag
- cgocall
- composites
- copylocks
- deepequalerrors
- errorsas
- fieldalignment
- findcall
- framepointer
- httpresponse
- ifaceassert
- loopclosure
- lostcancel
- nilfunc
- nilness
- reflectvaluecompare
- shift
- shadow
- sigchanyzer
- sortslice
- stdmethods
- stringintconv
- structtag
- testinggoroutine
- tests
- unmarshal
- unreachable
- unsafeptr
- unusedwrite
# https://golangci-lint.run/usage/linters/#staticcheck
staticcheck:
# SAxxxx checks in https://staticcheck.io/docs/configuration/options/#checks
# Default: ["*"]
checks: [ "all","-SA1019","-SA4015","-SA1029","-SA1016","-SA9003","-SA4006","-SA6003" ]
- linters:
- revive
path: _test\.go
text: context.Context should be the first parameter of a function
- linters:
- revive
path: _test\.go
text: exported func.*returns unexported type.*which can be annoying to use
- linters:
- gocritic
text: 'unnecessaryDefer:'
- linters:
- goconst
path: (.+)_test\.go
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gci
- gofmt
- goimports
settings:
gci:
sections:
- standard
- blank
- default
- dot
- prefix(github.com/gogf/gf/v2)
- prefix(github.com/gogf/gf/cmd)
- prefix(github.com/gogf/gfcontrib)
- prefix(github.com/gogf/gf/example)
custom-order: true
no-lex-order: false
gofmt:
simplify: true
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
- pattern: 'reflect.Ptr'
replacement: 'reflect.Pointer'
- pattern: 'ioutil.ReadAll'
replacement: 'io.ReadAll'
- pattern: 'ioutil.WriteFile'
replacement: 'os.WriteFile'
- pattern: 'ioutil.ReadFile'
replacement: 'os.ReadFile'
- pattern: 'ioutil.NopCloser'
replacement: 'io.NopCloser'
goimports:
local-prefixes:
- github.com/gogf/gf/v2
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -26,6 +26,8 @@ for file in `find ${workdir} -name go.mod`; do
fi
cd $goModPath
# Remove indirect dependencies
sed -i '/\/\/ indirect/d' go.mod
go mod tidy
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod

View File

@ -1,4 +1,19 @@
#!/usr/bin/env bash
# Function to detect OS and set sed parameters
setup_sed() {
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
SED_INPLACE="sed -i ''"
else
# Linux/Windows Git Bash
SED_INPLACE="sed -i"
fi
}
# Initialize sed command
setup_sed
if [ $# -ne 2 ]; then
echo "Parameter exception, please execute in the format of $0 [directory] [version number]"
echo "PS$0 ./ v2.4.0"
@ -28,10 +43,10 @@ fi
if [[ true ]]; then
# Use sed to replace the version number in version.go
sed -i '' 's/VERSION = ".*"/VERSION = "'${newVersion}'"/' version.go
$SED_INPLACE 's/VERSION = ".*"/VERSION = "'${newVersion}'"/' version.go
# Use sed to replace the version number in README.MD
sed -i '' 's/version=[^"]*/version='${newVersion}'/' README.MD
$SED_INPLACE 's/version=[^"]*/version='${newVersion}'/' README.MD
fi
if [ -f "go.work" ]; then
@ -65,17 +80,21 @@ for file in `find ${workdir} -name go.mod`; do
go mod edit -replace github.com/gogf/gf/contrib/drivers/pgsql/v2=../../contrib/drivers/pgsql
go mod edit -replace github.com/gogf/gf/contrib/drivers/sqlite/v2=../../contrib/drivers/sqlite
fi
# Remove indirect dependencies
sed -i '/\/\/ indirect/d' go.mod
go mod tidy
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod
$SED_INPLACE '/^toolchain/d' go.mod
# Upgrading only GoFrame related libraries, sometimes even if a version number is specified,
# it may not be possible to successfully upgrade. Please confirm before submitting the code
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf"
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf" | xargs -L1 go get -v
# Remove indirect dependencies
sed -i '/\/\/ indirect/d' go.mod
go mod tidy
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod
$SED_INPLACE '/^toolchain/d' go.mod
if [ $goModPath = "./cmd/gf" ]; then
go mod edit -dropreplace github.com/gogf/gf/v2
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/clickhouse/v2

View File

@ -10,6 +10,24 @@ tidy:
lint:
golangci-lint run -c .golangci.yml
# make branch to=v2.4.0
.PHONY: branch
branch:
@set -e; \
newVersion=$(to); \
if [ -z "$$newVersion" ]; then \
echo "Error: 'to' variable is required. Usage: make branch to=vX.Y.Z"; \
exit 1; \
fi; \
branchName=fix/$$newVersion; \
echo "Switching to master branch..."; \
git checkout master; \
echo "Pulling latest changes from master..."; \
git pull origin master; \
echo "Creating and switching to branch $$branchName from master..."; \
git checkout -b $$branchName; \
echo "Branch $$branchName created successfully!"
# make version to=v2.4.0
.PHONY: version
version:
@ -18,6 +36,20 @@ version:
./.make_version.sh ./ $$newVersion; \
echo "make version to=$(to) done"
# make tag to=v2.4.0
.PHONY: tag
tag:
@set -e; \
newVersion=$(to); \
echo "Switching to master branch..."; \
git checkout master; \
echo "Pulling latest changes from master..."; \
git pull origin master; \
echo "Creating annotated tag $$newVersion..."; \
git tag -a $$newVersion -m "Release $$newVersion"; \
echo "Pushing tag $$newVersion..."; \
git push origin $$newVersion; \
echo "Tag $$newVersion created and pushed successfully!"
# update submodules
.PHONY: subup

View File

@ -4,6 +4,8 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/gogf/gf/v2.svg)](https://pkg.go.dev/github.com/gogf/gf/v2)
[![GoFrame CI](https://github.com/gogf/gf/actions/workflows/ci-main.yml/badge.svg)](https://github.com/gogf/gf/actions/workflows/ci-main.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/gogf/gf/badge)](https://scorecard.dev/viewer/?uri=github.com/gogf/gf)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/9233/badge)](https://bestpractices.coreinfrastructure.org/projects/9233)
[![Go Report Card](https://goreportcard.com/badge/github.com/gogf/gf/v2)](https://goreportcard.com/report/github.com/gogf/gf/v2)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg?style=flat)](https://github.com/gogf/gf)
@ -36,7 +38,7 @@ A powerful framework for faster, easier, and more efficient project development.
💖 [Thanks to all the contributors who made GoFrame possible](https://github.com/gogf/gf/graphs/contributors) 💖
<a href="https://github.com/gogf/gf/graphs/contributors">
<img src="https://goframe.org/img/contributors.svg?version=v2.9.0-beta" alt="goframe contributors"/>
<img src="https://goframe.org/img/contributors.svg?version=v2.9.5" alt="goframe contributors"/>
</a>
# License

View File

@ -1,33 +1,33 @@
module github.com/gogf/gf/cmd/gf/v2
go 1.22
go 1.23.0
require (
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.0-beta
github.com/gogf/gf/v2 v2.9.0-beta
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.5
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.5
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.5
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.5
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.5
github.com/gogf/gf/v2 v2.9.5
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f
github.com/olekukonko/tablewriter v0.0.5
github.com/olekukonko/tablewriter v1.1.0
github.com/schollz/progressbar/v3 v3.15.0
golang.org/x/mod v0.17.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
golang.org/x/mod v0.25.0
golang.org/x/tools v0.26.0
)
require (
aead.dev/minisign v0.2.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
@ -36,28 +36,31 @@ require (
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/microsoft/go-mssqldb v1.7.1 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/paulmach/orb v0.7.1 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sijms/go-ora/v2 v2.7.10 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect

View File

@ -12,8 +12,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occ
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 h1:lLAZliqrZEygkxosLaW1qHyeTb4Ho7fVCZ0WKCpLocU=
github.com/ClickHouse/clickhouse-go/v2 v2.0.15/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk=
@ -31,14 +31,14 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
@ -58,8 +58,8 @@ github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EO
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -84,14 +84,13 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -100,8 +99,12 @@ github.com/microsoft/go-mssqldb v1.7.1/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpth
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
@ -134,44 +137,50 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -184,22 +193,22 @@ golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,8 +1,6 @@
go 1.22
go 1.23.0
use (
./
)
use ./
// =====================================================================================================
// NOTE:

View File

@ -30,12 +30,10 @@ import (
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
var (
Build = cBuild{
nodeNameInConfigFile: "gfcli.build",
packedGoFileName: "internal/packed/build_pack_data.go",
}
)
var Build = cBuild{
nodeNameInConfigFile: "gfcli.build",
packedGoFileName: "internal/packed/build_pack_data.go",
}
type cBuild struct {
g.Meta `name:"build" brief:"{cBuildBrief}" dc:"{cBuildDc}" eg:"{cBuildEg}" ad:"{cBuildAd}"`
@ -65,45 +63,67 @@ It provides much more features for building binary:
`
cBuildAd = `
PLATFORMS
aix ppc64
android 386,amd64,arm,arm64
darwin amd64,arm64
dragonfly amd64
freebsd 386,amd64,arm
linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le
illumos amd64
ios arm64
js wasm
linux 386,amd64,arm,arm64,loong64,mips,mipsle,mips64,mips64le,ppc64,ppc64le,riscv64,s390x
netbsd 386,amd64,arm
openbsd 386,amd64,arm
windows 386,amd64
openbsd 386,amd64,arm,arm64
plan9 386,amd64,arm
solaris amd64
wasip1 wasm
windows 386,amd64,arm,arm64
`
// https://golang.google.cn/doc/install/source
cBuildPlatforms = `
aix ppc64
android 386
android amd64
android arm
android arm64
darwin amd64
darwin arm64
ios amd64
ios arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
illumos amd64
ios arm64
js wasm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux loong64
linux mips
linux mipsle
linux mips64
linux mips64le
linux ppc64
linux ppc64le
linux riscv64
linux s390x
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
windows 386
windows amd64
android arm
dragonfly amd64
openbsd arm64
plan9 386
plan9 amd64
plan9 arm
solaris amd64
wasip1 wasm
windows 386
windows amd64
windows arm
windows arm64
`
)

View File

@ -11,6 +11,8 @@ import (
"context"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/renderer"
"github.com/olekukonko/tablewriter/tw"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gproc"
@ -61,10 +63,23 @@ func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err err
}
array = append(array, []string{gstr.Trim(match[1]), gstr.Trim(match[2])})
}
tw := tablewriter.NewWriter(buffer)
tw.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
tw.AppendBulk(array)
tw.Render()
table := tablewriter.NewTable(buffer,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.On},
},
Symbols: tw.NewSymbols(tw.StyleASCII),
})),
tablewriter.WithConfig(tablewriter.Config{
Row: tw.CellConfig{
Formatting: tw.CellFormatting{AutoWrap: tw.WrapNone},
Alignment: tw.CellAlignment{PerColumn: []tw.Align{tw.AlignLeft, tw.AlignLeft}},
ColMaxWidths: tw.CellWidth{Global: 84},
},
}),
)
table.Bulk(array)
table.Render()
mlog.Print(buffer.String())
return
}

View File

@ -13,6 +13,7 @@ import (
"path/filepath"
"runtime"
"strings"
"time"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/frame/g"
@ -26,9 +27,7 @@ import (
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
var (
Run = cRun{}
)
var Run = cRun{}
type cRun struct {
g.Meta `name:"run" usage:"{cRunUsage}" brief:"{cRunBrief}" eg:"{cRunEg}" dc:"{cRunDc}"`
@ -62,9 +61,7 @@ which compiles and runs the go codes asynchronously when codes change.
cRunWatchPathsBrief = `watch additional paths for live reload, separated by ",". i.e. "manifest/config/*.yaml"`
)
var (
process *gproc.Process
)
var process *gproc.Process
func init() {
gtag.Sets(g.MapStrStr{
@ -118,8 +115,12 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
}
dirty := gtype.NewBool()
var outputPath = app.genOutputPath()
outputPath := app.genOutputPath()
callbackFunc := func(event *gfsnotify.Event) {
if !event.IsWrite() && !event.IsCreate() && !event.IsRemove() && !event.IsRename() {
return
}
if gfile.ExtName(event.Path) != "go" {
return
}
@ -207,8 +208,37 @@ func (app *cRunApp) End(ctx context.Context, sig os.Signal, outputPath string) {
// Delete the binary file.
// firstly, kill the process.
if process != nil {
if err := process.Kill(); err != nil {
mlog.Debugf("kill process error: %s", err.Error())
if sig != nil && runtime.GOOS != "windows" {
if err := process.Signal(sig); err != nil {
mlog.Debugf("send signal to process error: %s", err.Error())
if err := process.Kill(); err != nil {
mlog.Debugf("kill process error: %s", err.Error())
}
} else {
waitCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
done := make(chan error, 1)
go func() {
select {
case <-waitCtx.Done():
done <- waitCtx.Err()
case done <- process.Wait():
}
}()
err := <-done
if err != nil {
mlog.Debugf("process wait error: %s", err.Error())
if err := process.Kill(); err != nil {
mlog.Debugf("kill process error: %s", err.Error())
}
} else {
mlog.Debug("process exited gracefully")
}
}
} else {
if err := process.Kill(); err != nil {
mlog.Debugf("kill process error: %s", err.Error())
}
}
}
if err := gfile.RemoveFile(outputPath); err != nil {

View File

@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gstr"
@ -39,7 +40,11 @@ gf up
gf up -a
gf up -c
gf up -cf
gf up -a -m=install
gf up -a -m=install -p=github.com/gogf/gf/cmd/gf/v2@latest
`
cliMethodHttpDownload = "http"
cliMethodGoInstall = "install"
)
func init() {
@ -49,10 +54,14 @@ func init() {
}
type cUpInput struct {
g.Meta `name:"up" config:"gfcli.up"`
All bool `name:"all" short:"a" brief:"upgrade both version and cli, auto fix codes" orphan:"true"`
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool" orphan:"true"`
Fix bool `name:"fix" short:"f" brief:"auto fix codes(it only make sense if cli is to be upgraded)" orphan:"true"`
g.Meta `name:"up" config:"gfcli.up"`
All bool `name:"all" short:"a" brief:"upgrade both version and cli, auto fix codes" orphan:"true"`
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool" orphan:"true"`
Fix bool `name:"fix" short:"f" brief:"auto fix codes(it only make sense if cli is to be upgraded)" orphan:"true"`
CliDownloadingMethod string `name:"cli-download-method" short:"m" brief:"cli upgrade method: http=download binary via HTTP GET, install=upgrade via go install" d:"http"`
// CliModulePath specifies the module path for CLI installation via go install.
// This is used when CliDownloadingMethod is set to "install".
CliModulePath string `name:"cli-module-path" short:"p" brief:"custom cli module path for upgrade CLI tool with go install method" d:"github.com/gogf/gf/cmd/gf/v2@latest"`
}
type cUpOutput struct{}
@ -76,7 +85,7 @@ func (c cUp) Index(ctx context.Context, in cUpInput) (out *cUpOutput, err error)
}
if in.Cli {
if err = c.doUpgradeCLI(ctx); err != nil {
if err = c.doUpgradeCLI(ctx, in); err != nil {
return nil, err
}
}
@ -170,8 +179,22 @@ func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (out *doUpgradeV
}
// doUpgradeCLI downloads the new version binary with process.
func (c cUp) doUpgradeCLI(ctx context.Context) (err error) {
func (c cUp) doUpgradeCLI(ctx context.Context, in cUpInput) (err error) {
mlog.Print(`start upgrading cli...`)
fmt.Println(` cli upgrade method:`, in.CliDownloadingMethod)
switch in.CliDownloadingMethod {
case cliMethodHttpDownload:
return c.doUpgradeCLIWithHttpDownload(ctx)
case cliMethodGoInstall:
return c.doUpgradeCLIWithGoInstall(ctx, in)
default:
mlog.Fatalf(`invalid cli upgrade method: "%s", please use "http" or "install"`, in.CliDownloadingMethod)
}
return
}
func (c cUp) doUpgradeCLIWithHttpDownload(ctx context.Context) (err error) {
mlog.Print(`start upgrading cli with http get download...`)
var (
downloadUrl = fmt.Sprintf(
`https://github.com/gogf/gf/releases/latest/download/gf_%s_%s`,
@ -213,6 +236,41 @@ func (c cUp) doUpgradeCLI(ctx context.Context) (err error) {
return
}
func (c cUp) doUpgradeCLIWithGoInstall(ctx context.Context, in cUpInput) (err error) {
mlog.Print(`upgrading cli with go install...`)
if !genv.Contains("GOPATH") {
mlog.Fatal(`"GOPATH" environment variable does not exist, please check your go installation`)
}
command := fmt.Sprintf(`go install %s`, in.CliModulePath)
mlog.Printf(`running command: %s`, command)
err = gproc.ShellRun(ctx, command)
if err != nil {
return err
}
cliFilePath := gfile.Join(genv.Get("GOPATH").String(), "bin/gf")
if runtime.GOOS == "windows" {
cliFilePath += ".exe"
}
// It fails if file not exist or its size is less than 1MB.
if !gfile.Exists(cliFilePath) || gfile.Size(cliFilePath) < 1024*1024 {
mlog.Fatalf(`go install %s failed, "%s" does not exist or its size is less than 1MB`, in.CliModulePath, cliFilePath)
}
newFile, err := gfile.Open(cliFilePath)
if err != nil {
return err
}
// selfupdate
err = selfupdate.Apply(newFile, selfupdate.Options{})
if err != nil {
return err
}
return
}
func (c cUp) doAutoFixing(ctx context.Context, dirPath string, version string) (err error) {
mlog.Printf(`auto fixing directory path "%s" from version "%s" ...`, dirPath, version)
command := fmt.Sprintf(`gf fix -p %s`, dirPath)

View File

@ -104,7 +104,7 @@ func Test_Build_Single_VarMap(t *testing.T) {
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
VarMap: map[string]interface{}{
VarMap: map[string]any{
"a": "1",
"b": "2",
},

View File

@ -66,6 +66,7 @@ func Test_Gen_Dao_Issue2572(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: nil,
FieldMapping: nil,
}
@ -155,6 +156,7 @@ func Test_Gen_Dao_Issue2616(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: nil,
FieldMapping: nil,
}
@ -266,6 +268,7 @@ func Test_Gen_Dao_Issue2746(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: nil,
FieldMapping: nil,
}
@ -338,6 +341,7 @@ func Test_Gen_Dao_Issue3459(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: nil,
}
)
@ -378,7 +382,7 @@ func Test_Gen_Dao_Issue3459(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
//_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i]))
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
@ -450,7 +454,7 @@ func Test_Gen_Dao_Issue3749(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
//_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i]))
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}

View File

@ -26,25 +26,34 @@ func Test_Gen_Dao_Sharding(t *testing.T) {
tableSingle = "single_table"
table1 = "users_0001"
table2 = "users_0002"
table3 = "users_0003"
table3 = "orders_0001"
table4 = "orders_0002"
sqlFilePath = gtest.DataPath(`gendao`, `sharding`, `sharding.sql`)
)
dropTableWithDb(db, tableSingle)
dropTableWithDb(db, table1)
dropTableWithDb(db, table2)
dropTableWithDb(db, table3)
dropTableWithDb(db, table4)
t.AssertNil(execSqlFile(db, sqlFilePath))
defer dropTableWithDb(db, tableSingle)
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
defer dropTableWithDb(db, table3)
defer dropTableWithDb(db, table4)
var (
path = gfile.Temp(guid.S())
//path = "/Users/john/Temp/gen_dao_sharding"
// path = "/Users/john/Temp/gen_dao_sharding"
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Group: group,
Path: path,
Link: link,
Group: group,
Prefix: "",
ShardingPattern: []string{
`users_?`,
`orders_?`,
},
}
)
@ -65,13 +74,16 @@ func Test_Gen_Dao_Sharding(t *testing.T) {
generatedFiles, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(len(generatedFiles), 8)
t.Assert(len(generatedFiles), 12)
var (
daoSingleTableContent = gfile.GetContents(gfile.Join(path, "dao", "single_table.go"))
daoUsersContent = gfile.GetContents(gfile.Join(path, "dao", "users.go"))
daoOrdersContent = gfile.GetContents(gfile.Join(path, "dao", "orders.go"))
)
t.Assert(gstr.Contains(daoSingleTableContent, "SingleTable = singleTableDao{internal.NewSingleTableDao()}"), true)
t.Assert(gstr.Contains(daoUsersContent, "Users = usersDao{internal.NewUsersDao(userShardingHandler)}"), true)
t.Assert(gstr.Contains(daoUsersContent, "Users = usersDao{internal.NewUsersDao(usersShardingHandler)}"), true)
t.Assert(gstr.Contains(daoUsersContent, "m.Sharding(gdb.ShardingConfig{"), true)
t.Assert(gstr.Contains(daoOrdersContent, "Orders = ordersDao{internal.NewOrdersDao(ordersShardingHandler)}"), true)
t.Assert(gstr.Contains(daoOrdersContent, "m.Sharding(gdb.ShardingConfig{"), true)
})
}

View File

@ -69,6 +69,7 @@ func Test_Gen_Dao_Default(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: nil,
FieldMapping: nil,
}
@ -107,7 +108,7 @@ func Test_Gen_Dao_Default(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
@ -161,6 +162,7 @@ func Test_Gen_Dao_TypeMapping(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: map[gendao.DBFieldTypeName]gendao.CustomAttributeType{
"int": {
Type: "int64",
@ -208,7 +210,7 @@ func Test_Gen_Dao_TypeMapping(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
//_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i]))
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
@ -263,6 +265,7 @@ func Test_Gen_Dao_FieldMapping(t *testing.T) {
NoJsonTag: false,
NoModelComment: false,
Clear: false,
GenTable: false,
TypeMapping: map[gendao.DBFieldTypeName]gendao.CustomAttributeType{
"int": {
Type: "int64",
@ -311,7 +314,7 @@ func Test_Gen_Dao_FieldMapping(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
//_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i]))
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
@ -403,7 +406,7 @@ func Test_Gen_Dao_Sqlite3(t *testing.T) {
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
for i := range files {
//_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i]))
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}

View File

@ -55,7 +55,7 @@ func TestGenPbIssue3882(t *testing.T) {
func TestGenPbIssue3953(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
outputPath = gfile.Temp(guid.S())
outputPath = gfile.Temp("f" + guid.S())
outputApiPath = filepath.Join(outputPath, "api")
outputCtrlPath = filepath.Join(outputPath, "controller")

View File

@ -367,3 +367,145 @@ func Test_Issue_3955(t *testing.T) {
}
})
}
func Test_Issue_4330_TypeMapping_Ineffective(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`issue`, `3685`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
in = genpbentity.CGenPbEntityInput{
Path: path,
Package: "",
Link: link,
Tables: "",
Prefix: "",
RemovePrefix: "",
RemoveFieldPrefix: "",
NameCase: "",
JsonCase: "",
Option: "",
TypeMapping: map[genpbentity.DBFieldTypeName]genpbentity.CustomAttributeType{
"json": {
Type: "google.protobuf.Value",
Import: "google/protobuf/struct.proto",
},
"decimal": {
Type: "double",
},
},
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genpbentity.CGenPbEntity{}.PbEntity(ctx, in)
t.AssertNil(err)
// files
files, err := gfile.ScanDir(path, "*.proto", false)
t.AssertNil(err)
t.Assert(files, []string{
path + filepath.FromSlash("/table_user.proto"),
})
// contents
testPath := gtest.DataPath("issue", "4330")
expectFiles := []string{
testPath + filepath.FromSlash("/issue4330_double.proto"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func Test_Gen_Pbentity_Sharding(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
tableSingle = "single_table"
table1 = "users_0001"
table2 = "users_0002"
table3 = "orders_0001"
table4 = "orders_0002"
sqlFilePath = gtest.DataPath(`gendao`, `sharding`, `sharding.sql`)
)
dropTableWithDb(db, tableSingle)
dropTableWithDb(db, table1)
dropTableWithDb(db, table2)
dropTableWithDb(db, table3)
dropTableWithDb(db, table4)
t.AssertNil(execSqlFile(db, sqlFilePath))
defer dropTableWithDb(db, tableSingle)
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
defer dropTableWithDb(db, table3)
defer dropTableWithDb(db, table4)
var (
path = gfile.Temp(guid.S())
in = genpbentity.CGenPbEntityInput{
Path: path,
Package: "unittest",
Link: link,
Tables: "",
RemovePrefix: "",
RemoveFieldPrefix: "",
NameCase: "",
JsonCase: "",
Option: "",
TypeMapping: nil,
FieldMapping: nil,
ShardingPattern: []string{
`users_?`,
`orders_?`,
},
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genpbentity.CGenPbEntity{}.PbEntity(ctx, in)
t.AssertNil(err)
// files
t.AssertNil(err)
generatedFiles, err := gfile.ScanDir(path, "*.proto", true)
t.Assert(len(generatedFiles), 3)
var (
msgSingleTableContent = gfile.GetContents(gfile.Join(path, "single_table.proto"))
msgUsersContent = gfile.GetContents(gfile.Join(path, "users.proto"))
msgOrdersContent = gfile.GetContents(gfile.Join(path, "orders.proto"))
)
t.Assert(gstr.Contains(msgSingleTableContent, "message SingleTable {"), true)
t.Assert(gstr.Contains(msgUsersContent, "message Users {"), true)
t.Assert(gstr.Contains(msgOrdersContent, "message Orders {"), true)
})
}

View File

@ -9,13 +9,14 @@ package genctrl
import (
"context"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gtag"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
const (

Binary file not shown.

View File

@ -11,6 +11,9 @@ import (
"fmt"
"strings"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/renderer"
"github.com/olekukonko/tablewriter/tw"
"golang.org/x/mod/modfile"
"github.com/gogf/gf/v2/container/garray"
@ -44,8 +47,10 @@ type (
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
TablePath string `name:"tablePath" short:"tp" brief:"{CGenDaoBriefTablePath}" d:"table"`
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
TplDaoTablePath string `name:"tplDaoTablePath" short:"t0" brief:"{CGenDaoBriefTplDaoTablePath}"`
TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
@ -58,6 +63,7 @@ type (
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
GenTable bool `name:"genTable" short:"gt" brief:"{CGenDaoBriefGenTable}" orphan:"true"`
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"`
@ -99,6 +105,20 @@ var (
Type: "float64",
},
}
// tablewriter Options
twRenderer = tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
Borders: tw.Border{Top: tw.Off, Bottom: tw.Off, Left: tw.Off, Right: tw.Off},
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.Off},
},
Symbols: tw.NewSymbols(tw.StyleASCII),
}))
twConfig = tablewriter.WithConfig(tablewriter.Config{
Row: tw.CellConfig{
Formatting: tw.CellFormatting{AutoWrap: tw.WrapNone},
},
})
)
func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) {
@ -173,8 +193,29 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
// Table excluding.
if in.TablesEx != "" {
array := garray.NewStrArrayFrom(tableNames)
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
array.RemoveValue(v)
for _, p := range gstr.SplitAndTrim(in.TablesEx, ",") {
if gstr.Contains(p, "*") || gstr.Contains(p, "?") {
p = gstr.ReplaceByMap(p, map[string]string{
"\r": "",
"\n": "",
})
p = gstr.ReplaceByMap(p, map[string]string{
"*": "\r",
"?": "\n",
})
p = gregex.Quote(p)
p = gstr.ReplaceByMap(p, map[string]string{
"\r": ".*",
"\n": ".",
})
for _, v := range array.Clone().Slice() {
if gregex.IsMatchString(p, v) {
array.RemoveValue(v)
}
}
} else {
array.RemoveValue(p)
}
}
tableNames = array.Slice()
}
@ -219,14 +260,18 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
tableNames[i] = ""
continue
}
shardingNewTableSet.Add(newTableName)
// Add prefix to sharding table name, if not, the isSharding check would not match.
shardingNewTableSet.Add(in.Prefix + newTableName)
}
}
newTableName = in.Prefix + newTableName
newTableNames[i] = newTableName
if tableNames[i] != "" {
// If shardingNewTableSet contains newTableName (tableName is empty), it should not be added to tableNames, make it empty and filter later.
newTableNames[i] = newTableName
}
}
tableNames = garray.NewStrArrayFrom(tableNames).FilterEmpty().Slice()
newTableNames = garray.NewStrArrayFrom(newTableNames).FilterEmpty().Slice() // Filter empty table names. make sure that newTableNames and tableNames have the same length.
in.genItems.Scale()
// Dao: index and internal.
@ -237,6 +282,14 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
NewTableNames: newTableNames,
ShardingTableSet: shardingNewTableSet,
})
// Table: table fields.
generateTable(ctx, CGenDaoInternalInput{
CGenDaoInput: in,
DB: db,
TableNames: tableNames,
NewTableNames: newTableNames,
ShardingTableSet: shardingNewTableSet,
})
// Do.
generateDo(ctx, CGenDaoInternalInput{
CGenDaoInput: in,

View File

@ -127,6 +127,7 @@ func generateDaoIndex(in generateDaoIndexInput) {
tplView.ClearAssigns()
tplView.Assigns(gview.Params{
tplVarTableSharding: in.IsSharding,
tplVarTableShardingPrefix: in.NewTableName + "_",
tplVarImportPrefix: in.ImportPrefix,
tplVarTableName: in.TableName,
tplVarTableNameCamelCase: in.TableNameCamelCase,
@ -210,13 +211,9 @@ func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField, removeFieldP
fmt.Sprintf(` #"%s",`, field.Name),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
table := tablewriter.NewTable(buffer, twRenderer, twConfig)
table.Bulk(array)
table.Render()
namesContent := buffer.String()
// Let's do this hack of table writer for indent!
namesContent = gstr.Replace(namesContent, " #", "")
@ -251,13 +248,9 @@ func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField, removeF
" #" + fmt.Sprintf(`// %s`, comment),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
table := tablewriter.NewTable(buffer, twRenderer, twConfig)
table.Bulk(array)
table.Render()
defineContent := buffer.String()
// Let's do this hack of table writer for indent!
defineContent = gstr.Replace(defineContent, " #", "")

View File

@ -45,14 +45,14 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
IsDo: true,
})
)
// replace all types to interface{}.
// replace all types to any.
structDefinition, _ = gregex.ReplaceStringFuncMatch(
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
structDefinition,
func(match []string) string {
// If the type is already a pointer/slice/map, it does nothing.
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
return fmt.Sprintf(`%s any %s`, match[1], match[3])
}
return match[0]
},

View File

@ -41,28 +41,55 @@ func generateStructDefinition(ctx context.Context, in generateStructDefinitionIn
appendImports = append(appendImports, imports)
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
table := tablewriter.NewTable(buffer, twRenderer, twConfig)
table.Bulk(array)
table.Render()
stContent := buffer.String()
// Let's do this hack of table writer for indent!
stContent = gstr.Replace(stContent, " #", "")
stContent = gstr.Replace(stContent, "` ", "`")
stContent = gstr.Replace(stContent, "``", "")
buffer.Reset()
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
fmt.Fprintf(buffer, "type %s struct {\n", in.StructName)
if in.IsDo {
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
fmt.Fprintf(buffer, "g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName)
}
buffer.WriteString(stContent)
buffer.WriteString("}")
return buffer.String(), appendImports
}
func getTypeMappingInfo(
ctx context.Context, fieldType string, inTypeMapping map[DBFieldTypeName]CustomAttributeType,
) (typeNameStr, importStr string) {
if typeMapping, ok := inTypeMapping[strings.ToLower(fieldType)]; ok {
typeNameStr = typeMapping.Type
importStr = typeMapping.Import
return
}
tryTypeMatch, _ := gregex.MatchString(`(.+?)\(([^\(\)]+)\)([\s\)]*)`, fieldType)
var (
tryTypeName string
moreTry bool
)
if len(tryTypeMatch) == 4 {
tryTypeMatch3, _ := gregex.ReplaceString(`\s+`, "", tryTypeMatch[3])
tryTypeName = gstr.Trim(tryTypeMatch[1]) + tryTypeMatch3
moreTry = tryTypeMatch3 != ""
} else {
tryTypeName = gstr.Split(fieldType, " ")[0]
}
if tryTypeName != "" {
if typeMapping, ok := inTypeMapping[strings.ToLower(tryTypeName)]; ok {
typeNameStr = typeMapping.Type
importStr = typeMapping.Import
} else if moreTry {
typeNameStr, importStr = getTypeMappingInfo(ctx, tryTypeName, inTypeMapping)
}
}
return
}
// generateStructFieldDefinition generates and returns the attribute definition for specified field.
func generateStructFieldDefinition(
ctx context.Context, field *gdb.TableField, in generateStructDefinitionInput,
@ -75,21 +102,7 @@ func generateStructFieldDefinition(
)
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
var (
tryTypeName string
)
tryTypeMatch, _ := gregex.MatchString(`(.+?)\((.+)\)`, field.Type)
if len(tryTypeMatch) == 3 {
tryTypeName = gstr.Trim(tryTypeMatch[1])
} else {
tryTypeName = gstr.Split(field.Type, " ")[0]
}
if tryTypeName != "" {
if typeMapping, ok := in.TypeMapping[strings.ToLower(tryTypeName)]; ok {
localTypeNameStr = typeMapping.Type
appendImport = typeMapping.Import
}
}
localTypeNameStr, appendImport = getTypeMappingInfo(ctx, field.Type, in.TypeMapping)
}
if localTypeNameStr == "" {

View File

@ -0,0 +1,147 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gendao
import (
"bytes"
"context"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gview"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
)
// generateTable generates dao files for given tables.
func generateTable(ctx context.Context, in CGenDaoInternalInput) {
dirPathTable := gfile.Join(in.Path, in.TablePath)
if !in.GenTable {
if gfile.Exists(dirPathTable) {
in.genItems.AppendDirPath(dirPathTable)
}
return
}
in.genItems.AppendDirPath(dirPathTable)
for i := 0; i < len(in.TableNames); i++ {
var (
realTableName = in.TableNames[i]
newTableName = in.NewTableNames[i]
)
generateTableSingle(ctx, generateTableSingleInput{
CGenDaoInternalInput: in,
TableName: realTableName,
NewTableName: newTableName,
DirPathTable: dirPathTable,
})
}
}
// generateTableSingleInput is the input parameter for generateTableSingle.
type generateTableSingleInput struct {
CGenDaoInternalInput
// TableName specifies the table name of the table.
TableName string
// NewTableName specifies the prefix-stripped or custom edited name of the table.
NewTableName string
DirPathTable string
}
// generateTableSingle generates dao files for a single table.
func generateTableSingle(ctx context.Context, in generateTableSingleInput) {
// Generating table data preparing.
fieldMap, err := in.DB.TableFields(ctx, in.TableName)
if err != nil {
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
}
tableNameSnakeCase := gstr.CaseSnake(in.NewTableName)
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
// Add suffix to avoid the table name which contains "_test",
// which would make the go file a testing file.
fileName += "_table"
}
path := filepath.FromSlash(gfile.Join(in.DirPathTable, fileName+".go"))
in.genItems.AppendGeneratedFilePath(path)
if in.OverwriteDao || !gfile.Exists(path) {
var (
ctx = context.Background()
tplContent = getTemplateFromPathOrDefault(
in.TplDaoTablePath, consts.TemplateGenTableContent,
)
)
tplView.ClearAssigns()
tplView.Assigns(gview.Params{
tplVarGroupName: in.Group,
tplVarTableName: in.TableName,
tplVarTableNameCamelCase: formatFieldName(in.NewTableName, FieldNameCaseCamel),
tplVarPackageName: filepath.Base(in.TablePath),
tplVarTableFields: generateTableFields(fieldMap),
})
indexContent, err := tplView.ParseContent(ctx, tplContent)
if err != nil {
mlog.Fatalf("parsing template content failed: %v", err)
}
if err = gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {
utils.GoFmt(path)
mlog.Print("generated:", gfile.RealPath(path))
}
}
}
// generateTableFields generates and returns the field definition content for specified table.
func generateTableFields(fields map[string]*gdb.TableField) string {
var buf bytes.Buffer
fieldNames := make([]string, 0, len(fields))
for fieldName := range fields {
fieldNames = append(fieldNames, fieldName)
}
sort.Slice(fieldNames, func(i, j int) bool {
return fields[fieldNames[i]].Index < fields[fieldNames[j]].Index // asc
})
for index, fieldName := range fieldNames {
field := fields[fieldName]
buf.WriteString(" " + strconv.Quote(field.Name) + ": {\n")
buf.WriteString(" Index: " + gconv.String(field.Index) + ",\n")
buf.WriteString(" Name: " + strconv.Quote(field.Name) + ",\n")
buf.WriteString(" Type: " + strconv.Quote(field.Type) + ",\n")
buf.WriteString(" Null: " + gconv.String(field.Null) + ",\n")
buf.WriteString(" Key: " + strconv.Quote(field.Key) + ",\n")
buf.WriteString(" Default: " + generateDefaultValue(field.Default) + ",\n")
buf.WriteString(" Extra: " + strconv.Quote(field.Extra) + ",\n")
buf.WriteString(" Comment: " + strconv.Quote(field.Comment) + ",\n")
buf.WriteString(" },")
if index != len(fieldNames)-1 {
buf.WriteString("\n")
}
}
return buf.String()
}
// generateDefaultValue generates and returns the default value definition for specified field.
func generateDefaultValue(value interface{}) string {
if value == nil {
return "nil"
}
switch v := value.(type) {
case string:
return strconv.Quote(v)
default:
return gconv.String(v)
}
}

View File

@ -60,6 +60,7 @@ CONFIGURATION SUPPORT
CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
CGenDaoBriefImportPrefix = `custom import prefix for generated go files`
CGenDaoBriefDaoPath = `directory path for storing generated dao files under path`
CGenDaoBriefTablePath = `directory path for storing generated table files under path`
CGenDaoBriefDoPath = `directory path for storing generated do files under path`
CGenDaoBriefEntityPath = `directory path for storing generated entity files under path`
CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
@ -69,6 +70,7 @@ CONFIGURATION SUPPORT
CGenDaoBriefNoJsonTag = `no json tag will be added for each field`
CGenDaoBriefNoModelComment = `no model comment will be added for each field`
CGenDaoBriefClear = `delete all generated go files that do not exist in database`
CGenDaoBriefGenTable = `generate table files`
CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table`
CGenDaoBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table`
CGenDaoBriefShardingPattern = `sharding pattern for table name, e.g. "users_?" will be replace tables "users_001,users_002,..." to "users" dao`
@ -97,6 +99,8 @@ generated json tag case for model struct, cases are as follows:
tplVarTableNameCamelCase = `TplTableNameCamelCase`
tplVarTableNameCamelLowerCase = `TplTableNameCamelLowerCase`
tplVarTableSharding = `TplTableSharding`
tplVarTableShardingPrefix = `TplTableShardingPrefix`
tplVarTableFields = `TplTableFields`
tplVarPackageImports = `TplPackageImports`
tplVarImportPrefix = `TplImportPrefix`
tplVarStructDefine = `TplStructDefine`
@ -125,6 +129,7 @@ func init() {
`CGenDaoBriefStdTime`: CGenDaoBriefStdTime,
`CGenDaoBriefWithTime`: CGenDaoBriefWithTime,
`CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath,
`CGenDaoBriefTablePath`: CGenDaoBriefTablePath,
`CGenDaoBriefDoPath`: CGenDaoBriefDoPath,
`CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath,
`CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport,
@ -136,6 +141,7 @@ func init() {
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
`CGenDaoBriefClear`: CGenDaoBriefClear,
`CGenDaoBriefGenTable`: CGenDaoBriefGenTable,
`CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping,
`CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping,
`CGenDaoBriefShardingPattern`: CGenDaoBriefShardingPattern,

View File

@ -113,12 +113,12 @@ func (p *EnumsParser) ParsePackage(pkg *packages.Package) {
}
func (p *EnumsParser) Export() string {
var typeEnumMap = make(map[string][]interface{})
var typeEnumMap = make(map[string][]any)
for _, enum := range p.enums {
if typeEnumMap[enum.Type] == nil {
typeEnumMap[enum.Type] = make([]interface{}, 0)
typeEnumMap[enum.Type] = make([]any, 0)
}
var value interface{}
var value any
switch enum.Kind {
case constant.Int:
value = gconv.Int64(enum.Value)

View File

@ -109,7 +109,7 @@ func (c CGenPb) tagCommentIntoListMap(comment string, lineTagMap *gmap.ListMap)
func (c CGenPb) listMapToStructTag(lineTagMap *gmap.ListMap) string {
var tag string
lineTagMap.Iterator(func(key, value interface{}) bool {
lineTagMap.Iterator(func(key, value any) bool {
if tag != "" {
tag += " "
}

View File

@ -15,6 +15,8 @@ import (
"strings"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/renderer"
"github.com/olekukonko/tablewriter/tw"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gset"
@ -37,18 +39,19 @@ type (
CGenPbEntity struct{}
CGenPbEntityInput struct {
g.Meta `name:"pbentity" config:"{CGenPbEntityConfig}" brief:"{CGenPbEntityBrief}" eg:"{CGenPbEntityEg}" ad:"{CGenPbEntityAd}"`
Path string `name:"path" short:"p" brief:"{CGenPbEntityBriefPath}" d:"manifest/protobuf/pbentity"`
Package string `name:"package" short:"k" brief:"{CGenPbEntityBriefPackage}"`
GoPackage string `name:"goPackage" short:"g" brief:"{CGenPbEntityBriefGoPackage}"`
Link string `name:"link" short:"l" brief:"{CGenPbEntityBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenPbEntityBriefTables}"`
Prefix string `name:"prefix" short:"f" brief:"{CGenPbEntityBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenPbEntityBriefRemovePrefix}"`
RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenPbEntityBriefRemoveFieldPrefix}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
NameCase string `name:"nameCase" short:"n" brief:"{CGenPbEntityBriefNameCase}" d:"Camel"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenPbEntityBriefJsonCase}" d:"none"`
Option string `name:"option" short:"o" brief:"{CGenPbEntityBriefOption}"`
Path string `name:"path" short:"p" brief:"{CGenPbEntityBriefPath}" d:"manifest/protobuf/pbentity"`
Package string `name:"package" short:"k" brief:"{CGenPbEntityBriefPackage}"`
GoPackage string `name:"goPackage" short:"g" brief:"{CGenPbEntityBriefGoPackage}"`
Link string `name:"link" short:"l" brief:"{CGenPbEntityBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenPbEntityBriefTables}"`
Prefix string `name:"prefix" short:"f" brief:"{CGenPbEntityBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenPbEntityBriefRemovePrefix}"`
RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenPbEntityBriefRemoveFieldPrefix}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
NameCase string `name:"nameCase" short:"n" brief:"{CGenPbEntityBriefNameCase}" d:"Camel"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenPbEntityBriefJsonCase}" d:"none"`
Option string `name:"option" short:"o" brief:"{CGenPbEntityBriefOption}"`
ShardingPattern []string `name:"shardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"`
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenPbEntityBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenPbEntityBriefFieldMapping}" orphan:"true"`
@ -122,6 +125,7 @@ CONFIGURATION SUPPORT
CGenPbEntityBriefTablesEx = `generate all models exclude the specified tables, multiple prefix separated with ','`
CGenPbEntityBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','`
CGenPbEntityBriefOption = `extra protobuf options`
CGenPbEntityBriefShardingPattern = `sharding pattern for table name, e.g. "users_?" will replace tables "users_001,users_002,..." to "users" pbentity`
CGenPbEntityBriefGroup = `
specifying the configuration group name of database for generated ORM instance,
it's not necessary and the default value is "default"
@ -252,6 +256,7 @@ func init() {
`CGenPbEntityBriefNameCase`: CGenPbEntityBriefNameCase,
`CGenPbEntityBriefJsonCase`: CGenPbEntityBriefJsonCase,
`CGenPbEntityBriefOption`: CGenPbEntityBriefOption,
`CGenPbEntityBriefShardingPattern`: CGenPbEntityBriefShardingPattern,
`CGenPbEntityBriefTypeMapping`: CGenPbEntityBriefTypeMapping,
`CGenPbEntityBriefFieldMapping`: CGenPbEntityBriefFieldMapping,
})
@ -321,6 +326,7 @@ func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput)
}
tableNames := ([]string)(nil)
shardingNewTableSet := gset.NewStrSet()
if in.Tables != "" {
tableNames = gstr.SplitAndTrim(in.Tables, ",")
} else {
@ -348,6 +354,31 @@ func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput)
for _, v := range removePrefixArray {
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
}
var shardingTableName string
if len(in.ShardingPattern) > 0 {
for _, pattern := range in.ShardingPattern {
var (
match []string
regPattern = gstr.Replace(pattern, "?", `(.+)`)
)
match, err = gregex.MatchString(regPattern, newTableName)
if err != nil {
mlog.Fatalf(`invalid sharding pattern "%s": %+v`, pattern, err)
}
if len(match) < 2 {
continue
}
shardingTableName = gstr.Replace(pattern, "?", "")
shardingTableName = gstr.Trim(shardingTableName, `_.-`)
}
}
if shardingTableName != "" {
if shardingNewTableSet.Contains(shardingTableName) {
continue
}
shardingNewTableSet.Add(shardingTableName)
newTableName = shardingTableName
}
generatePbEntityContentFile(ctx, CGenPbEntityInternalInput{
CGenPbEntityInput: in,
DB: db,
@ -414,13 +445,22 @@ func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb
appendImports = append(appendImports, imports)
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
table := tablewriter.NewTable(buffer,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
Borders: tw.Border{Top: tw.Off, Bottom: tw.Off, Left: tw.On, Right: tw.Off},
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.Off},
},
Symbols: tw.NewSymbolCustom("Proto").WithColumn(" "),
})),
tablewriter.WithConfig(tablewriter.Config{
Row: tw.CellConfig{
Formatting: tw.CellFormatting{AutoWrap: tw.WrapNone},
},
}),
)
table.Bulk(array)
table.Render()
stContent := buffer.String()
// Let's do this hack of table writer for indent!
stContent = regexp.MustCompile(`\s+\n`).ReplaceAllString(gstr.Replace(stContent, " #", ""), "\n")
@ -441,14 +481,23 @@ func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPb
err error
ctx = gctx.GetInitCtx()
)
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
// match typeMapping after local type transform.
// eg: double => string, varchar => string etc.
localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
if err != nil {
panic(err)
}
if localTypeName != "" {
if typeMapping, ok := in.TypeMapping[strings.ToLower(string(localTypeName))]; ok {
if typeMappingLocal, localOk := in.TypeMapping[strings.ToLower(string(localTypeName))]; localOk {
localTypeNameStr = typeMappingLocal.Type
appendImport = typeMappingLocal.Import
}
}
// Try match unknown / string localTypeName with db type.
if localTypeName == "" || localTypeName == gdb.LocalTypeString {
formattedFieldType, _ := in.DB.GetFormattedDBTypeNameForField(field.Type)
if typeMapping, ok := in.TypeMapping[strings.ToLower(formattedFieldType)]; ok {
localTypeNameStr = typeMapping.Type
appendImport = typeMapping.Import
}

View File

@ -13,8 +13,6 @@ import (
"sync"
"sync/atomic"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gset"
@ -25,6 +23,9 @@ import (
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gtag"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
)
const (

View File

@ -33,7 +33,7 @@ func (c CGenService) generateType(generatedContent *bytes.Buffer, srcStructFunct
generatedContent.WriteString("type(")
generatedContent.WriteString("\n")
srcStructFunctions.Iterator(func(key, value interface{}) bool {
srcStructFunctions.Iterator(func(key, value any) bool {
var (
funcContents = make([]string, 0)
funcContent string
@ -71,7 +71,7 @@ func (c CGenService) generateVar(generatedContent *bytes.Buffer, srcStructFuncti
// Generating variable and register definitions.
var variableContent string
srcStructFunctions.Iterator(func(key, value interface{}) bool {
srcStructFunctions.Iterator(func(key, value any) bool {
structName := key.(string)
variableContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentVariable, g.MapStrStr{
"{StructName}": structName,
@ -93,7 +93,7 @@ func (c CGenService) generateVar(generatedContent *bytes.Buffer, srcStructFuncti
// See: const.TemplateGenServiceContentRegister
func (c CGenService) generateFunc(generatedContent *bytes.Buffer, srcStructFunctions *gmap.ListMap) {
// Variable register function definitions.
srcStructFunctions.Iterator(func(key, value interface{}) bool {
srcStructFunctions.Iterator(func(key, value any) bool {
structName := key.(string)
generatedContent.WriteString(gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentRegister, g.MapStrStr{
"{StructName}": structName,

View File

@ -1,12 +1,15 @@
module github.com/gogf/gf/cmd/gf/cmd/gf/testdata/vardump/v2
go 1.18
go 1.23.0
require github.com/gogf/gf/v2 v2.8.2
toolchain go1.24.6
require github.com/gogf/gf/v2 v2.9.4
require (
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
golang.org/x/text v0.28.0 // indirect
)
replace github.com/gogf/gf/v2 => ../../../../../../../

View File

@ -1,29 +1,62 @@
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -12,11 +12,11 @@ import (
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
type TableUser struct {
g.Meta `orm:"table:table_user, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,11 +12,11 @@ import (
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
type TableUser struct {
g.Meta `orm:"table:table_user, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,10 +12,10 @@ import (
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
type TableUser struct {
g.Meta `orm:"table:table_user, do:true"`
Id interface{} //
Passport interface{} //
Password interface{} //
Nickname interface{} //
Id any //
Passport any //
Password any //
Nickname any //
CreatedAt *gtime.Time //
UpdatedAt *gtime.Time //
}

View File

@ -12,11 +12,11 @@ import (
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
type TableUser struct {
g.Meta `orm:"table:table_user, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -1,44 +1,54 @@
CREATE TABLE `single_table` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
CREATE TABLE `single_table`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10, 2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users_0001` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
CREATE TABLE `users_0001`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10, 2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users_0002` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
CREATE TABLE `users_0002`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10, 2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users_0003` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
CREATE TABLE `orders_0001`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ORDER ID',
`amount` decimal(10, 2) unsigned DEFAULT NULL COMMENT 'Total amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `orders_0002`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ORDER ID',
`amount` decimal(10, 2) unsigned DEFAULT NULL COMMENT 'Total amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)

View File

@ -63,14 +63,14 @@ func (s *sArticle) T3(ctx context.Context, b *gdbas.Model) (c, d *gdbas.Model, e
* random comment
*/
// func (s *sArticle) T4(i interface{}) interface{}
// func (s *sArticle) T4(i any) any
// # $ % ^ & * ( ) _ + - = { } | [ ] \ : " ; ' < > ? , . /
func (s *sArticle) T4(i interface{}) interface{} {
func (s *sArticle) T4(i any) any {
return nil
}
/**
* func (s *sArticle) T4(i interface{}) interface{} {
* func (s *sArticle) T4(i any) any {
* return nil
* }
*/

View File

@ -36,9 +36,9 @@ type (
* @author oldme
*/
T3(ctx context.Context, b *gdbas.Model) (c *gdbas.Model, d *gdbas.Model, err error)
// func (s *sArticle) T4(i interface{}) interface{}
// func (s *sArticle) T4(i any) any
// # $ % ^ & * ( ) _ + - = { } | [ ] \ : " ; ' < > ? , . /
T4(i interface{}) interface{}
T4(i any) any
}
)

View File

@ -12,11 +12,11 @@ import (
// User1 is the golang structure of table user1 for DAO operations like Where/Data.
type User1 struct {
g.Meta `orm:"table:user1, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,11 +12,11 @@ import (
// User2 is the golang structure of table user2 for DAO operations like Where/Data.
type User2 struct {
g.Meta `orm:"table:user2, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,11 +12,11 @@ import (
// User1 is the golang structure of table user1 for DAO operations like Where/Data.
type User1 struct {
g.Meta `orm:"table:user1, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,11 +12,11 @@ import (
// User2 is the golang structure of table user2 for DAO operations like Where/Data.
type User2 struct {
g.Meta `orm:"table:user2, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
Id any // User ID
Passport any // User Passport
Password any // User Password
Nickname any // User Nickname
Score any // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -12,11 +12,11 @@ import (
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
type TableUser struct {
g.Meta `orm:"table:table_user, do:true"`
Id interface{} // User ID
ParentId interface{} //
Passport interface{} // User Passport
PassWord interface{} // User Password
Nickname2 interface{} // User Nickname
Id any // User ID
ParentId any //
Passport any // User Passport
PassWord any // User Password
Nickname2 any // User Nickname
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@ -0,0 +1,23 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
syntax = "proto3";
package pbentity;
option go_package = "github.com/gogf/gf/cmd/gf/v2/internal/cmd/api/pbentity";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
message TableUser {
uint32 Id = 1; // User ID
string Passport = 2; // User Passport
string Password = 3; // User Password
string Nickname = 4; // User Nickname
double Score = 5; // Total score amount.
google.protobuf.Value Data = 6; // User Data
google.protobuf.Timestamp CreateAt = 7; // Created Time
google.protobuf.Timestamp UpdateAt = 8; // Updated Time
}

View File

@ -0,0 +1,23 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
syntax = "proto3";
package pbentity;
option go_package = "github.com/gogf/gf/cmd/gf/v2/internal/cmd/api/pbentity";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
message TableUser {
uint32 Id = 1; // User ID
string Passport = 2; // User Passport
string Password = 3; // User Password
string Nickname = 4; // User Nickname
string Score = 5; // Total score amount.
google.protobuf.Value Data = 6; // User Data
google.protobuf.Timestamp CreateAt = 7; // Created Time
google.protobuf.Timestamp UpdateAt = 8; // Updated Time
}

View File

@ -27,7 +27,7 @@ var (
// {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations.
{{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{
{{- if .TplTableSharding -}}
internal.New{{.TplTableNameCamelCase}}Dao(userShardingHandler),
internal.New{{.TplTableNameCamelCase}}Dao({{.TplTableNameCamelLowerCase}}ShardingHandler),
{{- else -}}
internal.New{{.TplTableNameCamelCase}}Dao(),
{{- end -}}
@ -35,13 +35,13 @@ var (
)
{{if .TplTableSharding -}}
// userShardingHandler is the handler for sharding operations.
// {{.TplTableNameCamelLowerCase}}ShardingHandler is the handler for sharding operations.
// You can fill this sharding handler with your custom implementation.
func userShardingHandler(m *gdb.Model) *gdb.Model {
func {{.TplTableNameCamelLowerCase}}ShardingHandler(m *gdb.Model) *gdb.Model {
m = m.Sharding(gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: "",
Prefix: "{{.TplTableShardingPrefix}}",
// Replace Rule field with your custom sharding rule.
// Or you can use "&gdb.DefaultShardingRule{}" for default sharding rule.
Rule: nil,

View File

@ -0,0 +1,35 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package consts
const TemplateGenTableContent = `
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package {{.TplPackageName}}
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
)
// {{.TplTableNameCamelCase}} defines the fields of table "{{.TplTableName}}" with their properties.
// This map is used internally by GoFrame ORM to understand table structure.
var {{.TplTableNameCamelCase}} = map[string]*gdb.TableField{
{{.TplTableFields}}
}
// Set{{.TplTableNameCamelCase}}TableFields registers the table fields definition to the database instance.
// db: database instance that implements gdb.DB interface.
// schema: optional schema/namespace name, especially for databases that support schemas.
func Set{{.TplTableNameCamelCase}}TableFields(ctx context.Context, db gdb.DB, schema ...string) error {
return db.GetCore().SetTableFields(ctx, "{{.TplTableName}}", {{.TplTableNameCamelCase}}, schema...)
}
`

View File

@ -162,8 +162,14 @@ func (s serviceInstall) getGoPathBin() string {
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
var (
folderPaths []serviceInstallAvailablePath
binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
binaryFileName = "gf"
)
// Windows binary file name suffix.
if runtime.GOOS == "windows" {
binaryFileName += ".exe"
}
// $GOPATH/bin
if goPathBin := s.getGoPathBin(); goPathBin != "" {
folderPaths = s.checkAndAppendToAvailablePath(

View File

@ -51,26 +51,26 @@ func SetHeaderPrint(enabled bool) {
}
}
func Print(v ...interface{}) {
func Print(v ...any) {
logger.Print(ctx, v...)
}
func Printf(format string, v ...interface{}) {
func Printf(format string, v ...any) {
logger.Printf(ctx, format, v...)
}
func Fatal(v ...interface{}) {
func Fatal(v ...any) {
logger.Fatal(ctx, v...)
}
func Fatalf(format string, v ...interface{}) {
func Fatalf(format string, v ...any) {
logger.Fatalf(ctx, format, v...)
}
func Debug(v ...interface{}) {
func Debug(v ...any) {
logger.Debug(ctx, v...)
}
func Debugf(format string, v ...interface{}) {
func Debugf(format string, v ...any) {
logger.Debugf(ctx, format, v...)
}

View File

@ -12,12 +12,13 @@ import (
"golang.org/x/tools/imports"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
// GoFmt formats the source file and adds or removes import statements as necessary.

View File

@ -7,6 +7,8 @@
package main
import (
_ "time/tzdata"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gctx"

View File

@ -6,7 +6,10 @@
package garray
import "strings"
import (
"sort"
"strings"
)
// defaultComparatorInt for int comparison.
func defaultComparatorInt(a, b int) int {
@ -24,6 +27,14 @@ func defaultComparatorStr(a, b string) int {
return strings.Compare(a, b)
}
// defaultSorter is a generic sorting function that sorts a slice of comparable types
// using the provided comparator function.
func defaultSorter[T comparable](values []T, comparator func(a T, b T) int) {
sort.Slice(values, func(i, j int) bool {
return comparator(values[i], values[j]) < 0
})
}
// quickSortInt is the quick-sorting algorithm implements for int.
func quickSortInt(values []int, comparator func(a, b int) int) {
if len(values) <= 1 {
@ -67,3 +78,51 @@ func quickSortStr(values []string, comparator func(a, b string) int) {
quickSortStr(values[:head], comparator)
quickSortStr(values[head+1:], comparator)
}
// tToAnySlice converts []T to []any
func tToAnySlice[T any](values []T) []any {
if values == nil {
return nil
}
anyValues := make([]any, len(values), cap(values))
for k, v := range values {
anyValues[k] = v
}
return anyValues
}
// anyToTSlice is convert []any to []T
func anyToTSlice[T any](values []any) []T {
if values == nil {
return nil
}
tValues := make([]T, len(values), cap(values))
for k, v := range values {
tValues[k], _ = v.(T)
}
return tValues
}
// tToAnySlices converts [][]T to [][]any
func tToAnySlices[T any](values [][]T) [][]any {
if values == nil {
return nil
}
anyValues := make([][]any, len(values), cap(values))
for k, v := range values {
anyValues[k] = tToAnySlice(v)
}
return anyValues
}
// anyToTSlices converts [][]any to [][]T
func anyToTSlices[T any](values [][]any) [][]T {
if values == nil {
return nil
}
tValues := make([][]T, len(values), cap(values))
for k, v := range values {
tValues[k] = anyToTSlice[T](v)
}
return tValues
}

View File

@ -28,7 +28,7 @@ import (
// when its initialization and cannot be changed then.
type Array struct {
mu rwmutex.RWMutex
array []interface{}
array []any
}
// New creates and returns an empty array.
@ -49,7 +49,7 @@ func NewArray(safe ...bool) *Array {
func NewArraySize(size int, cap int, safe ...bool) *Array {
return &Array{
mu: rwmutex.Create(safe...),
array: make([]interface{}, size, cap),
array: make([]any, size, cap),
}
}
@ -59,7 +59,7 @@ func NewArrayRange(start, end, step int, safe ...bool) *Array {
if step == 0 {
panic(fmt.Sprintf(`invalid step value: %d`, step))
}
slice := make([]interface{}, 0)
slice := make([]any, 0)
index := 0
for i := start; i <= end; i += step {
slice = append(slice, i)
@ -70,20 +70,20 @@ func NewArrayRange(start, end, step int, safe ...bool) *Array {
// NewFrom is alias of NewArrayFrom.
// See NewArrayFrom.
func NewFrom(array []interface{}, safe ...bool) *Array {
func NewFrom(array []any, safe ...bool) *Array {
return NewArrayFrom(array, safe...)
}
// NewFromCopy is alias of NewArrayFromCopy.
// See NewArrayFromCopy.
func NewFromCopy(array []interface{}, safe ...bool) *Array {
func NewFromCopy(array []any, safe ...bool) *Array {
return NewArrayFromCopy(array, safe...)
}
// NewArrayFrom creates and returns an array with given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
func NewArrayFrom(array []any, safe ...bool) *Array {
return &Array{
mu: rwmutex.Create(safe...),
array: array,
@ -93,8 +93,8 @@ func NewArrayFrom(array []interface{}, safe ...bool) *Array {
// NewArrayFromCopy creates and returns an array from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
newArray := make([]interface{}, len(array))
func NewArrayFromCopy(array []any, safe ...bool) *Array {
newArray := make([]any, len(array))
copy(newArray, array)
return &Array{
mu: rwmutex.Create(safe...),
@ -104,14 +104,14 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns `nil`.
func (a *Array) At(index int) (value interface{}) {
func (a *Array) At(index int) (value any) {
value, _ = a.Get(index)
return
}
// 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) {
func (a *Array) Get(index int) (value any, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if index < 0 || index >= len(a.array) {
@ -121,7 +121,7 @@ func (a *Array) Get(index int) (value interface{}, found bool) {
}
// Set sets value to specified index.
func (a *Array) Set(index int, value interface{}) error {
func (a *Array) Set(index int, value any) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
@ -132,7 +132,7 @@ func (a *Array) Set(index int, value interface{}) error {
}
// SetArray sets the underlying slice array with the given `array`.
func (a *Array) SetArray(array []interface{}) *Array {
func (a *Array) SetArray(array []any) *Array {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
@ -140,7 +140,7 @@ func (a *Array) SetArray(array []interface{}) *Array {
}
// Replace replaces the array items by given `array` from the beginning of array.
func (a *Array) Replace(array []interface{}) *Array {
func (a *Array) Replace(array []any) *Array {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
@ -164,7 +164,7 @@ func (a *Array) Sum() (sum int) {
}
// SortFunc sorts the array by custom function `less`.
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
func (a *Array) SortFunc(less func(v1, v2 any) bool) *Array {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
@ -174,26 +174,26 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
}
// InsertBefore inserts the `values` to the front of `index`.
func (a *Array) InsertBefore(index int, values ...interface{}) error {
func (a *Array) InsertBefore(index int, values ...any) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]interface{}{}, a.array[index:]...)
rear := append([]any{}, a.array[index:]...)
a.array = append(a.array[0:index], values...)
a.array = append(a.array, rear...)
return nil
}
// InsertAfter inserts the `values` to the back of `index`.
func (a *Array) InsertAfter(index int, values ...interface{}) error {
func (a *Array) InsertAfter(index int, values ...any) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]interface{}{}, a.array[index+1:]...)
rear := append([]any{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], values...)
a.array = append(a.array, rear...)
return nil
@ -201,14 +201,14 @@ func (a *Array) InsertAfter(index int, values ...interface{}) error {
// 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) {
func (a *Array) Remove(index int) (value any, 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) {
func (a *Array) doRemoveWithoutLock(index int) (value any, found bool) {
if index < 0 || index >= len(a.array) {
return nil, false
}
@ -232,7 +232,7 @@ func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *Array) RemoveValue(value interface{}) bool {
func (a *Array) RemoveValue(value any) bool {
a.mu.Lock()
defer a.mu.Unlock()
if i := a.doSearchWithoutLock(value); i != -1 {
@ -243,7 +243,7 @@ func (a *Array) RemoveValue(value interface{}) bool {
}
// RemoveValues removes multiple items by `values`.
func (a *Array) RemoveValues(values ...interface{}) {
func (a *Array) RemoveValues(values ...any) {
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
@ -254,7 +254,7 @@ func (a *Array) RemoveValues(values ...interface{}) {
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *Array) PushLeft(value ...interface{}) *Array {
func (a *Array) PushLeft(value ...any) *Array {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
@ -263,7 +263,7 @@ func (a *Array) PushLeft(value ...interface{}) *Array {
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *Array) PushRight(value ...interface{}) *Array {
func (a *Array) PushRight(value ...any) *Array {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
@ -272,14 +272,14 @@ func (a *Array) PushRight(value ...interface{}) *Array {
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, the `found` is false.
func (a *Array) PopRand() (value interface{}, found bool) {
func (a *Array) PopRand() (value any, 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{} {
func (a *Array) PopRands(size int) []any {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
@ -288,7 +288,7 @@ func (a *Array) PopRands(size int) []interface{} {
if size >= len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
array := make([]any, size)
for i := 0; i < size; i++ {
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
@ -297,7 +297,7 @@ func (a *Array) PopRands(size int) []interface{} {
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, the `found` is false.
func (a *Array) PopLeft() (value interface{}, found bool) {
func (a *Array) PopLeft() (value any, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
@ -310,7 +310,7 @@ func (a *Array) PopLeft() (value interface{}, found bool) {
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, the `found` is false.
func (a *Array) PopRight() (value interface{}, found bool) {
func (a *Array) PopRight() (value any, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
@ -323,7 +323,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
}
// PopLefts pops and returns `size` items from the beginning of array.
func (a *Array) PopLefts(size int) []interface{} {
func (a *Array) PopLefts(size int) []any {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
@ -340,7 +340,7 @@ func (a *Array) PopLefts(size int) []interface{} {
}
// PopRights pops and returns `size` items from the end of array.
func (a *Array) PopRights(size int) []interface{} {
func (a *Array) PopRights(size int) []any {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
@ -364,7 +364,7 @@ func (a *Array) PopRights(size int) []interface{} {
// If `end` is negative, then the offset will start from the end of array.
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *Array) Range(start int, end ...int) []interface{} {
func (a *Array) Range(start int, end ...int) []any {
a.mu.RLock()
defer a.mu.RUnlock()
offsetEnd := len(a.array)
@ -377,9 +377,9 @@ func (a *Array) Range(start int, end ...int) []interface{} {
if start < 0 {
start = 0
}
array := ([]interface{})(nil)
array := ([]any)(nil)
if a.mu.IsSafe() {
array = make([]interface{}, offsetEnd-start)
array = make([]any, offsetEnd-start)
copy(array, a.array[start:offsetEnd])
} else {
array = a.array[start:offsetEnd]
@ -400,7 +400,7 @@ func (a *Array) Range(start int, end ...int) []interface{} {
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
//
// Any possibility crossing the left border of array, it will fail.
func (a *Array) SubSlice(offset int, length ...int) []interface{} {
func (a *Array) SubSlice(offset int, length ...int) []any {
a.mu.RLock()
defer a.mu.RUnlock()
size := len(a.array)
@ -429,7 +429,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]interface{}, size)
s := make([]any, size)
copy(s, a.array[offset:])
return s
} else {
@ -438,7 +438,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} {
}
// Append is alias of PushRight, please See PushRight.
func (a *Array) Append(value ...interface{}) *Array {
func (a *Array) Append(value ...any) *Array {
a.PushRight(value...)
return a
}
@ -454,11 +454,11 @@ func (a *Array) Len() int {
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *Array) Slice() []interface{} {
func (a *Array) Slice() []any {
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
array := make([]any, len(a.array))
copy(array, a.array)
return array
} else {
@ -466,15 +466,15 @@ func (a *Array) Slice() []interface{} {
}
}
// Interfaces returns current array as []interface{}.
func (a *Array) Interfaces() []interface{} {
// Interfaces returns current array as []any.
func (a *Array) Interfaces() []any {
return a.Slice()
}
// Clone returns a new array, which is a copy of current array.
func (a *Array) Clone() (newArray *Array) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
array := make([]any, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewArrayFrom(array, a.mu.IsSafe())
@ -484,26 +484,26 @@ func (a *Array) Clone() (newArray *Array) {
func (a *Array) Clear() *Array {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
a.array = make([]any, 0)
}
a.mu.Unlock()
return a
}
// Contains checks whether a value exists in the array.
func (a *Array) Contains(value interface{}) bool {
func (a *Array) Contains(value any) bool {
return a.Search(value) != -1
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) int {
func (a *Array) Search(value any) int {
a.mu.RLock()
defer a.mu.RUnlock()
return a.doSearchWithoutLock(value)
}
func (a *Array) doSearchWithoutLock(value interface{}) int {
func (a *Array) doSearchWithoutLock(value any) int {
if len(a.array) == 0 {
return -1
}
@ -527,9 +527,9 @@ func (a *Array) Unique() *Array {
}
var (
ok bool
temp interface{}
uniqueSet = make(map[interface{}]struct{})
uniqueArray = make([]interface{}, 0, len(a.array))
temp any
uniqueSet = make(map[any]struct{})
uniqueArray = make([]any, 0, len(a.array))
)
for i := 0; i < len(a.array); i++ {
temp = a.array[i]
@ -544,7 +544,7 @@ func (a *Array) Unique() *Array {
}
// LockFunc locks writing by callback function `f`.
func (a *Array) LockFunc(f func(array []interface{})) *Array {
func (a *Array) LockFunc(f func(array []any)) *Array {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
@ -552,7 +552,7 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array {
}
// RLockFunc locks reading by callback function `f`.
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
func (a *Array) RLockFunc(f func(array []any)) *Array {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
@ -563,13 +563,13 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *Array) Merge(array interface{}) *Array {
func (a *Array) Merge(array any) *Array {
return a.Append(gconv.Interfaces(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{}) error {
func (a *Array) Fill(startIndex int, num int, value any) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 || startIndex > len(a.array) {
@ -588,7 +588,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error {
// Chunk splits an array into multiple arrays,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *Array) Chunk(size int) [][]interface{} {
func (a *Array) Chunk(size int) [][]any {
if size < 1 {
return nil
}
@ -596,7 +596,7 @@ func (a *Array) Chunk(size int) [][]interface{} {
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
var n [][]any
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
@ -612,7 +612,7 @@ func (a *Array) Chunk(size int) [][]interface{} {
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of `size` is less than or equal to the length of the array
// then no padding takes place.
func (a *Array) Pad(size int, val interface{}) *Array {
func (a *Array) Pad(size int, val any) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
@ -623,7 +623,7 @@ func (a *Array) Pad(size int, val interface{}) *Array {
n = -size
}
n -= len(a.array)
tmp := make([]interface{}, n)
tmp := make([]any, n)
for i := 0; i < n; i++ {
tmp[i] = val
}
@ -636,7 +636,7 @@ func (a *Array) Pad(size int, val interface{}) *Array {
}
// Rand randomly returns one item from array(no deleting).
func (a *Array) Rand() (value interface{}, found bool) {
func (a *Array) Rand() (value any, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
@ -646,13 +646,13 @@ func (a *Array) Rand() (value interface{}, found bool) {
}
// Rands randomly returns `size` items from array(no deleting).
func (a *Array) Rands(size int) []interface{} {
func (a *Array) Rands(size int) []any {
a.mu.RLock()
defer a.mu.RUnlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
array := make([]interface{}, size)
array := make([]any, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
@ -697,8 +697,8 @@ func (a *Array) Join(glue string) string {
}
// CountValues counts the number of occurrences of all values in the array.
func (a *Array) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
func (a *Array) CountValues() map[any]int {
m := make(map[any]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
@ -708,13 +708,13 @@ func (a *Array) CountValues() map[interface{}]int {
}
// Iterator is alias of IteratorAsc.
func (a *Array) Iterator(f func(k int, v interface{}) bool) {
func (a *Array) Iterator(f func(k int, v any) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
func (a *Array) IteratorAsc(f func(k int, v any) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
@ -726,7 +726,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
func (a *Array) IteratorDesc(f func(k int, v any) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
@ -772,7 +772,7 @@ func (a Array) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *Array) UnmarshalJSON(b []byte) error {
if a.array == nil {
a.array = make([]interface{}, 0)
a.array = make([]any, 0)
}
a.mu.Lock()
defer a.mu.Unlock()
@ -783,7 +783,7 @@ func (a *Array) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *Array) UnmarshalValue(value interface{}) error {
func (a *Array) UnmarshalValue(value any) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -798,7 +798,7 @@ func (a *Array) UnmarshalValue(value interface{}) error {
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *Array) Filter(filter func(index int, value interface{}) bool) *Array {
func (a *Array) Filter(filter func(index int, value any) bool) *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
@ -841,7 +841,7 @@ func (a *Array) FilterEmpty() *Array {
}
// Walk applies a user supplied function `f` to every item of array.
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
func (a *Array) Walk(f func(value any) any) *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
@ -856,13 +856,13 @@ func (a *Array) IsEmpty() bool {
}
// DeepCopy implements interface for deep copy of current type.
func (a *Array) DeepCopy() interface{} {
func (a *Array) DeepCopy() any {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]interface{}, len(a.array))
newSlice := make([]any, len(a.array))
for i, v := range a.array {
newSlice[i] = deepcopy.Copy(v)
}

View File

@ -478,11 +478,11 @@ func (a *IntArray) Slice() []int {
return array
}
// Interfaces returns current array as []interface{}.
func (a *IntArray) Interfaces() []interface{} {
// Interfaces returns current array as []any.
func (a *IntArray) Interfaces() []any {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
array := make([]any, len(a.array))
for k, v := range a.array {
array[k] = v
}
@ -581,7 +581,7 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *IntArray) Merge(array interface{}) *IntArray {
func (a *IntArray) Merge(array any) *IntArray {
return a.Append(gconv.Ints(array)...)
}
@ -788,7 +788,7 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *IntArray) UnmarshalValue(value interface{}) error {
func (a *IntArray) UnmarshalValue(value any) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -846,7 +846,7 @@ func (a *IntArray) IsEmpty() bool {
}
// DeepCopy implements interface for deep copy of current type.
func (a *IntArray) DeepCopy() interface{} {
func (a *IntArray) DeepCopy() any {
if a == nil {
return nil
}

View File

@ -454,11 +454,11 @@ func (a *StrArray) Slice() []string {
return array
}
// Interfaces returns current array as []interface{}.
func (a *StrArray) Interfaces() []interface{} {
// Interfaces returns current array as []any.
func (a *StrArray) Interfaces() []any {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
array := make([]any, len(a.array))
for k, v := range a.array {
array[k] = v
}
@ -573,7 +573,7 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *StrArray) Merge(array interface{}) *StrArray {
func (a *StrArray) Merge(array any) *StrArray {
return a.Append(gconv.Strings(array)...)
}
@ -787,7 +787,7 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *StrArray) UnmarshalValue(value interface{}) error {
func (a *StrArray) UnmarshalValue(value any) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -845,7 +845,7 @@ func (a *StrArray) IsEmpty() bool {
}
// DeepCopy implements interface for deep copy of current type.
func (a *StrArray) DeepCopy() interface{} {
func (a *StrArray) DeepCopy() any {
if a == nil {
return nil
}

View File

@ -0,0 +1,493 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"github.com/gogf/gf/v2/util/gconv"
)
// TArray is a golang array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
// TArray is a wrapper of Array. It is designed to make using Array more convenient.
type TArray[T comparable] struct {
Array
}
// NewTArray creates and returns an empty array.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArray[T comparable](safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArray(safe...),
}
}
// NewTArraySize create and returns an array with given size and cap.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArraySize[T comparable](size int, cap int, safe ...bool) *TArray[T] {
arr := NewArraySize(size, cap, safe...)
ret := &TArray[T]{
Array: *arr,
}
return ret
}
// NewTArrayFrom creates and returns an array with given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArrayFrom[T comparable](array []T, safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArrayFrom(tToAnySlice(array), safe...),
}
}
// NewTArrayFromCopy creates and returns an array from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArrayFromCopy[T comparable](array []T, safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArrayFromCopy(tToAnySlice(array), safe...),
}
}
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns `nil`.
func (a *TArray[T]) At(index int) (value T) {
value, _ = a.Array.At(index).(T)
return
}
// 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 *TArray[T]) Get(index int) (value T, found bool) {
val, found := a.Array.Get(index)
if !found {
return
}
value, _ = val.(T)
return
}
// Set sets value to specified index.
func (a *TArray[T]) Set(index int, value T) error {
return a.Array.Set(index, value)
}
// SetArray sets the underlying slice array with the given `array`.
func (a *TArray[T]) SetArray(array []T) *TArray[T] {
a.Array.SetArray(tToAnySlice(array))
return a
}
// Replace replaces the array items by given `array` from the beginning of array.
func (a *TArray[T]) Replace(array []T) *TArray[T] {
a.Array.Replace(tToAnySlice(array))
return a
}
// Sum returns the sum of values in an array.
func (a *TArray[T]) Sum() int {
return a.Array.Sum()
}
// SortFunc sorts the array by custom function `less`.
func (a *TArray[T]) SortFunc(less func(v1, v2 T) bool) *TArray[T] {
a.Array.SortFunc(func(v1, v2 any) bool {
v1t, _ := v1.(T)
v2t, _ := v2.(T)
return less(v1t, v2t)
})
return a
}
// InsertBefore inserts the `values` to the front of `index`.
func (a *TArray[T]) InsertBefore(index int, values ...T) error {
return a.Array.InsertBefore(index, tToAnySlice(values)...)
}
// InsertAfter inserts the `values` to the back of `index`.
func (a *TArray[T]) InsertAfter(index int, values ...T) error {
return a.Array.InsertAfter(index, tToAnySlice(values)...)
}
// Remove removes an item by index.
// If the given `index` is out of range of the array, the `found` is false.
func (a *TArray[T]) Remove(index int) (value T, found bool) {
val, found := a.Array.Remove(index)
if !found {
return
}
value, _ = val.(T)
return
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *TArray[T]) RemoveValue(value T) bool {
return a.Array.RemoveValue(value)
}
// RemoveValues removes multiple items by `values`.
func (a *TArray[T]) RemoveValues(values ...T) {
a.Array.RemoveValues(tToAnySlice(values)...)
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *TArray[T]) PushLeft(value ...T) *TArray[T] {
a.Array.PushLeft(tToAnySlice(value)...)
return a
}
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *TArray[T]) PushRight(value ...T) *TArray[T] {
a.Array.PushRight(tToAnySlice(value)...)
return a
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopRand() (value T, found bool) {
val, found := a.Array.PopRand()
if !found {
return
}
value, _ = val.(T)
return
}
// PopRands randomly pops and returns `size` items out of array.
func (a *TArray[T]) PopRands(size int) []T {
return anyToTSlice[T](a.Array.PopRands(size))
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopLeft() (value T, found bool) {
val, found := a.Array.PopLeft()
if !found {
return
}
value, _ = val.(T)
return
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopRight() (value T, found bool) {
val, found := a.Array.PopRight()
if !found {
return
}
value, _ = val.(T)
return
}
// PopLefts pops and returns `size` items from the beginning of array.
func (a *TArray[T]) PopLefts(size int) []T {
return anyToTSlice[T](a.Array.PopLefts(size))
}
// PopRights pops and returns `size` items from the end of array.
func (a *TArray[T]) PopRights(size int) []T {
return anyToTSlice[T](a.Array.PopRights(size))
}
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// If `end` is negative, then the offset will start from the end of array.
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *TArray[T]) Range(start int, end ...int) []T {
return anyToTSlice[T](a.Array.Range(start, end...))
}
// SubSlice returns a slice of elements from the array as specified
// by the `offset` and `size` parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
//
// If offset is non-negative, the sequence will start at that offset in the array.
// If offset is negative, the sequence will start that far from the end of the array.
//
// If length is given and is positive, then the sequence will have up to that many elements in it.
// If the array is shorter than the length, then only the available array elements will be present.
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
//
// Any possibility crossing the left border of array, it will fail.
func (a *TArray[T]) SubSlice(offset int, length ...int) []T {
return anyToTSlice[T](a.Array.SubSlice(offset, length...))
}
// Append is alias of PushRight, please See PushRight.
func (a *TArray[T]) Append(value ...T) *TArray[T] {
a.Array.Append(tToAnySlice(value)...)
return a
}
// Len returns the length of array.
func (a *TArray[T]) Len() int {
return a.Array.Len()
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *TArray[T]) Slice() []T {
return anyToTSlice[T](a.Array.Slice())
}
// Interfaces returns current array as []any.
func (a *TArray[T]) Interfaces() []any {
return a.Array.Interfaces()
}
// Clone returns a new array, which is a copy of current array.
func (a *TArray[T]) Clone() *TArray[T] {
return &TArray[T]{
Array: *a.Array.Clone(),
}
}
// Clear deletes all items of current array.
func (a *TArray[T]) Clear() *TArray[T] {
a.Array.Clear()
return a
}
// Contains checks whether a value exists in the array.
func (a *TArray[T]) Contains(value T) bool {
return a.Array.Contains(value)
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *TArray[T]) Search(value T) int {
return a.Array.Search(value)
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *TArray[T]) Unique() *TArray[T] {
a.Array.Unique()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *TArray[T]) LockFunc(f func(array []T)) *TArray[T] {
a.Array.LockFunc(func(array []any) {
vals := anyToTSlice[T](array)
f(vals)
for k, v := range vals {
array[k] = v
}
})
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *TArray[T]) RLockFunc(f func(array []T)) *TArray[T] {
a.Array.RLockFunc(func(array []any) {
f(anyToTSlice[T](array))
})
return a
}
// Merge merges `array` into current array.
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *TArray[T]) Merge(array any) *TArray[T] {
switch v := array.(type) {
case *Array:
return a.Merge(v.Slice())
case *StrArray:
return a.Merge(v.Slice())
case *IntArray:
return a.Merge(v.Slice())
case *TArray[T]:
a.Array.Merge(&v.Array)
case []T:
a.Array.Merge(v)
case TArray[T]:
a.Array.Merge(&v.Array)
default:
var vals []T
if err := gconv.Scan(v, &vals); err != nil {
panic(err)
}
a.Append(vals...)
}
return a
}
// Fill fills an array with num entries of the value `value`,
// keys starting at the `startIndex` parameter.
func (a *TArray[T]) Fill(startIndex int, num int, value T) error {
return a.Array.Fill(startIndex, num, value)
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *TArray[T]) Chunk(size int) (values [][]T) {
return anyToTSlices[T](a.Array.Chunk(size))
}
// Pad pads array to the specified length with `value`.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of `size` is less than or equal to the length of the array
// then no padding takes place.
func (a *TArray[T]) Pad(size int, val T) *TArray[T] {
a.Array.Pad(size, val)
return a
}
// Rand randomly returns one item from array(no deleting).
func (a *TArray[T]) Rand() (value T, found bool) {
val, found := a.Array.Rand()
if !found {
return
}
value, _ = val.(T)
return
}
// Rands randomly returns `size` items from array(no deleting).
func (a *TArray[T]) Rands(size int) []T {
return anyToTSlice[T](a.Array.Rands(size))
}
// Shuffle randomly shuffles the array.
func (a *TArray[T]) Shuffle() *TArray[T] {
a.Array.Shuffle()
return a
}
// Reverse makes array with elements in reverse order.
func (a *TArray[T]) Reverse() *TArray[T] {
a.Array.Reverse()
return a
}
// Join joins array elements with a string `glue`.
func (a *TArray[T]) Join(glue string) string {
return a.Array.Join(glue)
}
// CountValues counts the number of occurrences of all values in the array.
func (a *TArray[T]) CountValues() (valueCnt map[T]int) {
valueCnt = map[T]int{}
for k, v := range a.Array.CountValues() {
k0, _ := k.(T)
valueCnt[k0] = v
}
return
}
// Iterator is alias of IteratorAsc.
func (a *TArray[T]) Iterator(f func(k int, v T) bool) {
a.Array.Iterator(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *TArray[T]) IteratorAsc(f func(k int, v T) bool) {
a.Array.IteratorAsc(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *TArray[T]) IteratorDesc(f func(k int, v T) bool) {
a.Array.IteratorDesc(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *TArray[T]) String() string {
if a == nil {
return ""
}
return a.Array.String()
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a TArray[T]) MarshalJSON() ([]byte, error) {
return a.Array.MarshalJSON()
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *TArray[T]) UnmarshalJSON(b []byte) error {
return a.Array.UnmarshalJSON(b)
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *TArray[T]) UnmarshalValue(value any) error {
return a.Array.UnmarshalValue(value)
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *TArray[T]) Filter(filter func(index int, value T) bool) *TArray[T] {
a.Array.Filter(func(index int, value any) bool {
val, _ := value.(T)
return filter(index, val)
})
return a
}
// FilterNil removes all nil value of the array.
func (a *TArray[T]) FilterNil() *TArray[T] {
a.Array.FilterNil()
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *TArray[T]) FilterEmpty() *TArray[T] {
a.Array.FilterEmpty()
return a
}
// Walk applies a user supplied function `f` to every item of array.
func (a *TArray[T]) Walk(f func(value T) T) *TArray[T] {
a.Array.Walk(func(value any) any {
val, _ := value.(T)
return f(val)
})
return a
}
// IsEmpty checks whether the array is empty.
func (a *TArray[T]) IsEmpty() bool {
return a.Array.IsEmpty()
}
// DeepCopy implements interface for deep copy of current type.
func (a *TArray[T]) DeepCopy() any {
if a == nil {
return nil
}
arr := a.Array.DeepCopy().(*Array)
return &TArray[T]{
Array: *arr,
}
}

View File

@ -7,19 +7,10 @@
package garray
import (
"bytes"
"fmt"
"math"
"sort"
"github.com/gogf/gf/v2/internal/deepcopy"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/gogf/gf/v2/util/gutil"
)
// SortedArray is a golang sorted array with rich features.
@ -28,10 +19,14 @@ import (
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedArray struct {
mu rwmutex.RWMutex
array []interface{}
unique bool // Whether enable unique feature(false)
comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
*SortedTArray[any]
}
// lazyInit lazily initializes the array.
func (a *SortedArray) lazyInit() {
if a.SortedTArray == nil {
a.SortedTArray = NewSortedTArraySize[any](0, nil, false)
}
}
// NewSortedArray creates and returns an empty sorted array.
@ -40,28 +35,26 @@ type SortedArray struct {
// if it returns value < 0, means `a` < `b`; the `a` will be inserted before `b`;
// if it returns value = 0, means `a` = `b`; the `a` will be replaced by `b`;
// if it returns value > 0, means `a` > `b`; the `a` will be inserted after `b`;
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
func NewSortedArray(comparator func(a, b any) int, safe ...bool) *SortedArray {
return NewSortedArraySize(0, comparator, safe...)
}
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
func NewSortedArraySize(cap int, comparator func(a, b any) int, safe ...bool) *SortedArray {
return &SortedArray{
mu: rwmutex.Create(safe...),
array: make([]interface{}, 0, cap),
comparator: comparator,
SortedTArray: NewSortedTArraySize(cap, comparator, safe...),
}
}
// NewSortedArrayRange creates and returns an array by a range from `start` to `end`
// with step value `step`.
func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
func NewSortedArrayRange(start, end, step int, comparator func(a, b any) int, safe ...bool) *SortedArray {
if step == 0 {
panic(fmt.Sprintf(`invalid step value: %d`, step))
}
slice := make([]interface{}, 0)
slice := make([]any, 0)
index := 0
for i := start; i <= end; i += step {
slice = append(slice, i)
@ -73,7 +66,7 @@ func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{})
// NewSortedArrayFrom creates and returns an sorted array with given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
func NewSortedArrayFrom(array []any, comparator func(a, b any) int, safe ...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, safe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
@ -85,233 +78,120 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
newArray := make([]interface{}, len(array))
func NewSortedArrayFromCopy(array []any, comparator func(a, b any) int, safe ...bool) *SortedArray {
newArray := make([]any, len(array))
copy(newArray, array)
return NewSortedArrayFrom(newArray, comparator, safe...)
}
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns `nil`.
func (a *SortedArray) At(index int) (value interface{}) {
value, _ = a.Get(index)
return
func (a *SortedArray) At(index int) (value any) {
a.lazyInit()
return a.SortedTArray.At(index)
}
// SetArray sets the underlying slice array with the given `array`.
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
func (a *SortedArray) SetArray(array []any) *SortedArray {
a.lazyInit()
a.SortedTArray.SetArray(array)
return a
}
// SetComparator sets/changes the comparator for sorting.
// It resorts the array as the comparator is changed.
func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
a.mu.Lock()
defer a.mu.Unlock()
a.comparator = comparator
sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
func (a *SortedArray) SetComparator(comparator func(a, b any) int) {
a.lazyInit()
a.SortedTArray.SetComparator(comparator)
}
// Sort sorts the array in increasing order.
// The parameter `reverse` controls whether sort
// in increasing order(default) or decreasing order
func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
a.lazyInit()
a.SortedTArray.Sort()
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
return a.Append(values...)
func (a *SortedArray) Add(values ...any) *SortedArray {
a.lazyInit()
a.SortedTArray.Add(values...)
return a
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedArray) Append(values ...interface{}) *SortedArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
a.array = append(a.array[:index], append([]interface{}{value}, a.array[index:]...)...)
}
func (a *SortedArray) Append(values ...any) *SortedArray {
a.SortedTArray.Append(values...)
return a
}
// 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()
if index < 0 || index >= len(a.array) {
return nil, false
}
return a.array[index], true
func (a *SortedArray) Get(index int) (value any, found bool) {
a.lazyInit()
return a.SortedTArray.Get(index)
}
// Remove removes an item by index.
// 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, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
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]
a.array = append(a.array[:index], a.array[index+1:]...)
return value, true
func (a *SortedArray) Remove(index int) (value any, found bool) {
a.lazyInit()
return a.SortedTArray.Remove(index)
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedArray) RemoveValue(value interface{}) bool {
a.mu.Lock()
defer a.mu.Unlock()
if i, r := a.binSearch(value, false); r == 0 {
_, res := a.doRemoveWithoutLock(i)
return res
}
return false
func (a *SortedArray) RemoveValue(value any) bool {
a.lazyInit()
return a.SortedTArray.RemoveValue(value)
}
// RemoveValues removes an item by `values`.
func (a *SortedArray) RemoveValues(values ...interface{}) {
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
if i, r := a.binSearch(value, false); r == 0 {
a.doRemoveWithoutLock(i)
}
}
func (a *SortedArray) RemoveValues(values ...any) {
a.lazyInit()
a.SortedTArray.RemoveValues(values...)
}
// PopLeft pops and returns an item from the beginning of array.
// 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, false
}
value = a.array[0]
a.array = a.array[1:]
return value, true
func (a *SortedArray) PopLeft() (value any, found bool) {
a.lazyInit()
return a.SortedTArray.PopLeft()
}
// PopRight pops and returns an item from the end of array.
// 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, false
}
value = a.array[index]
a.array = a.array[:index]
return value, true
func (a *SortedArray) PopRight() (value any, found bool) {
a.lazyInit()
return a.SortedTArray.PopRight()
}
// PopRand randomly pops and return an item out of 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)))
func (a *SortedArray) PopRand() (value any, found bool) {
a.lazyInit()
return a.SortedTArray.PopRand()
}
// 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 <= 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++ {
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
func (a *SortedArray) PopRands(size int) []any {
a.lazyInit()
return a.SortedTArray.PopRands(size)
}
// PopLefts pops and returns `size` items from the beginning of array.
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
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
func (a *SortedArray) PopLefts(size int) []any {
a.lazyInit()
return a.SortedTArray.PopLefts(size)
}
// PopRights pops and returns `size` items from the end of array.
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
return value
func (a *SortedArray) PopRights(size int) []any {
a.lazyInit()
return a.SortedTArray.PopRights(size)
}
// Range picks and returns items by range, like array[start:end].
@ -321,27 +201,8 @@ func (a *SortedArray) PopRights(size int) []interface{} {
// If `end` is negative, then the offset will start from the end of array.
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *SortedArray) Range(start int, end ...int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
offsetEnd := len(a.array)
if len(end) > 0 && end[0] < offsetEnd {
offsetEnd = end[0]
}
if start > offsetEnd {
return nil
}
if start < 0 {
start = 0
}
array := ([]interface{})(nil)
if a.mu.IsSafe() {
array = make([]interface{}, offsetEnd-start)
copy(array, a.array[start:offsetEnd])
} else {
array = a.array[start:offsetEnd]
}
return array
func (a *SortedArray) Range(start int, end ...int) []any {
return a.SortedTArray.Range(start, end...)
}
// SubSlice returns a slice of elements from the array as specified
@ -357,199 +218,92 @@ func (a *SortedArray) Range(start int, end ...int) []interface{} {
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
//
// Any possibility crossing the left border of array, it will fail.
func (a *SortedArray) SubSlice(offset int, length ...int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
size := len(a.array)
if len(length) > 0 {
size = length[0]
}
if offset > len(a.array) {
return nil
}
if offset < 0 {
offset = len(a.array) + offset
if offset < 0 {
return nil
}
}
if size < 0 {
offset += size
size = -size
if offset < 0 {
return nil
}
}
end := offset + size
if end > len(a.array) {
end = len(a.array)
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]interface{}, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:end]
}
func (a *SortedArray) SubSlice(offset int, length ...int) []any {
a.lazyInit()
return a.SortedTArray.SubSlice(offset, length...)
}
// Sum returns the sum of values in an array.
func (a *SortedArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.lazyInit()
return a.SortedTArray.Sum()
}
// Len returns the length of array.
func (a *SortedArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.lazyInit()
return a.SortedTArray.Len()
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *SortedArray) Slice() []interface{} {
var array []interface{}
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
func (a *SortedArray) Slice() []any {
a.lazyInit()
return a.SortedTArray.Slice()
}
// Interfaces returns current array as []interface{}.
func (a *SortedArray) Interfaces() []interface{} {
return a.Slice()
// Interfaces returns current array as []any.
func (a *SortedArray) Interfaces() []any {
a.lazyInit()
return a.SortedTArray.Interfaces()
}
// Contains checks whether a value exists in the array.
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) != -1
func (a *SortedArray) Contains(value any) bool {
a.lazyInit()
return a.SortedTArray.Contains(value)
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *SortedArray) Search(value interface{}) (index int) {
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If `result` equals to 0, it means the value at `index` is equals to `value`.
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
// If `result` greater than 0, it means the value at `index` is greater than `value`.
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = min + (max-min)/2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
func (a *SortedArray) Search(value any) (index int) {
a.lazyInit()
return a.SortedTArray.Search(value)
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also does unique check, remove all repeated items.
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
a.lazyInit()
a.SortedTArray.SetUnique(unique)
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedArray) Unique() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
a.lazyInit()
a.SortedTArray.Unique()
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedArray) Clone() (newArray *SortedArray) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedArrayFrom(array, a.comparator, a.mu.IsSafe())
a.lazyInit()
return &SortedArray{
SortedTArray: a.SortedTArray.Clone(),
}
}
// Clear deletes all items of current array.
func (a *SortedArray) Clear() *SortedArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
a.lazyInit()
a.SortedTArray.Clear()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
f(a.array)
func (a *SortedArray) LockFunc(f func(array []any)) *SortedArray {
a.lazyInit()
a.SortedTArray.LockFunc(f)
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
func (a *SortedArray) RLockFunc(f func(array []any)) *SortedArray {
a.lazyInit()
a.SortedTArray.RLockFunc(f)
return a
}
@ -557,112 +311,60 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedArray) Merge(array interface{}) *SortedArray {
func (a *SortedArray) Merge(array any) *SortedArray {
return a.Add(gconv.Interfaces(array)...)
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *SortedArray) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
func (a *SortedArray) Chunk(size int) [][]any {
a.lazyInit()
return a.SortedTArray.Chunk(size)
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedArray) Rand() (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return nil, false
}
return a.array[grand.Intn(len(a.array))], true
func (a *SortedArray) Rand() (value any, found bool) {
a.lazyInit()
return a.SortedTArray.Rand()
}
// 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 <= 0 || len(a.array) == 0 {
return nil
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return array
func (a *SortedArray) Rands(size int) []any {
a.lazyInit()
return a.SortedTArray.Rands(size)
}
// Join joins array elements with a string `glue`.
func (a *SortedArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.lazyInit()
return a.SortedTArray.Join(glue)
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedArray) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
func (a *SortedArray) CountValues() map[any]int {
a.lazyInit()
return a.SortedTArray.CountValues()
}
// Iterator is alias of IteratorAsc.
func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
func (a *SortedArray) Iterator(f func(k int, v any) bool) {
a.lazyInit()
a.SortedTArray.Iterator(f)
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
func (a *SortedArray) IteratorAsc(f func(k int, v any) bool) {
a.lazyInit()
a.SortedTArray.IteratorAsc(f)
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
func (a *SortedArray) IteratorDesc(f func(k int, v any) bool) {
a.lazyInit()
a.SortedTArray.IteratorDesc(f)
}
// String returns current array as a string, which implements like json.Marshal does.
@ -670,173 +372,72 @@ func (a *SortedArray) String() string {
if a == nil {
return ""
}
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
buffer.WriteByte('[')
s := ""
for k, v := range a.array {
s = gconv.String(v)
if gstr.IsNumeric(s) {
buffer.WriteString(s)
} else {
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
}
if k != len(a.array)-1 {
buffer.WriteByte(',')
}
}
buffer.WriteByte(']')
return buffer.String()
a.lazyInit()
return a.SortedTArray.String()
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a SortedArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
a.lazyInit()
return a.SortedTArray.MarshalJSON()
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
// Note that the comparator is set as string comparator in default.
func (a *SortedArray) UnmarshalJSON(b []byte) error {
if a.comparator == nil {
a.array = make([]interface{}, 0)
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
defer a.mu.Unlock()
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
return err
}
if a.comparator != nil && a.array != nil {
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
}
return nil
a.lazyInit()
return a.SortedTArray.UnmarshalJSON(b)
}
// UnmarshalValue is an interface implement which sets any type of value for array.
// Note that the comparator is set as string comparator in default.
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceAny(value)
}
if a.comparator != nil && a.array != nil {
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
}
return err
func (a *SortedArray) UnmarshalValue(value any) (err error) {
a.lazyInit()
return a.SortedTArray.UnmarshalValue(value)
}
// FilterNil removes all nil value of the array.
func (a *SortedArray) FilterNil() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
a.lazyInit()
a.SortedTArray.FilterNil()
return a
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *SortedArray) Filter(filter func(index int, value interface{}) bool) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if filter(i, a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
func (a *SortedArray) Filter(filter func(index int, value any) bool) *SortedArray {
a.lazyInit()
a.SortedTArray.Filter(filter)
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *SortedArray) FilterEmpty() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
a.lazyInit()
a.SortedTArray.FilterEmpty()
return a
}
// Walk applies a user supplied function `f` to every item of array.
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
for i, v := range a.array {
a.array[i] = f(v)
}
func (a *SortedArray) Walk(f func(value any) any) *SortedArray {
a.lazyInit()
a.SortedTArray.Walk(f)
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (a *SortedArray) getComparator() func(a, b interface{}) int {
if a.comparator == nil {
panic("comparator is missing for sorted array")
}
return a.comparator
a.lazyInit()
return a.SortedTArray.IsEmpty()
}
// DeepCopy implements interface for deep copy of current type.
func (a *SortedArray) DeepCopy() interface{} {
if a == nil {
return nil
func (a *SortedArray) DeepCopy() any {
a.lazyInit()
return &SortedArray{
SortedTArray: a.SortedTArray.DeepCopy().(*SortedTArray[any]),
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]interface{}, len(a.array))
for i, v := range a.array {
newSlice[i] = deepcopy.Copy(v)
}
return NewSortedArrayFrom(newSlice, a.comparator, a.mu.IsSafe())
}

View File

@ -7,15 +7,9 @@
package garray
import (
"bytes"
"fmt"
"math"
"sort"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
)
// SortedIntArray is a golang sorted int array with rich features.
@ -24,10 +18,15 @@ import (
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedIntArray struct {
mu rwmutex.RWMutex
array []int
unique bool // Whether enable unique feature(false)
comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
*SortedTArray[int]
}
// lazyInit lazily initializes the array.
func (a *SortedIntArray) lazyInit() {
if a.SortedTArray == nil {
a.SortedTArray = NewSortedTArraySize(0, defaultComparatorInt, false)
a.SetSorter(quickSortInt)
}
}
// NewSortedIntArray creates and returns an empty sorted array.
@ -49,10 +48,10 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
a := NewSortedTArraySize(cap, defaultComparatorInt, safe...)
a.SetSorter(quickSortInt)
return &SortedIntArray{
mu: rwmutex.Create(safe...),
array: make([]int, 0, cap),
comparator: defaultComparatorInt,
SortedTArray: a,
}
}
@ -77,7 +76,7 @@ func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray
func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, safe...)
a.array = array
sort.Ints(a.array)
a.sorter(a.array, defaultComparatorInt)
return a
}
@ -93,16 +92,14 @@ func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray {
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns `0`.
func (a *SortedIntArray) At(index int) (value int) {
value, _ = a.Get(index)
return
a.lazyInit()
return a.SortedTArray.At(index)
}
// SetArray sets the underlying slice array with the given `array`.
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
quickSortInt(a.array, a.getComparator())
a.lazyInit()
a.SortedTArray.SetArray(array)
return a
}
@ -110,200 +107,95 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
// The parameter `reverse` controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
quickSortInt(a.array, a.getComparator())
a.lazyInit()
a.SortedTArray.Sort()
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
a.lazyInit()
return a.Append(values...)
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
}
a.lazyInit()
a.SortedTArray.Append(values...)
return a
}
// 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()
if index < 0 || index >= len(a.array) {
return 0, false
}
return a.array[index], true
a.lazyInit()
return a.SortedTArray.Get(index)
}
// Remove removes an item by index.
// 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, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
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]
a.array = append(a.array[:index], a.array[index+1:]...)
return value, true
a.lazyInit()
return a.SortedTArray.Remove(index)
}
// 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 {
a.mu.Lock()
defer a.mu.Unlock()
if i, r := a.binSearch(value, false); r == 0 {
_, res := a.doRemoveWithoutLock(i)
return res
}
return false
a.lazyInit()
return a.SortedTArray.RemoveValue(value)
}
// RemoveValues removes an item by `values`.
func (a *SortedIntArray) RemoveValues(values ...int) {
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
if i, r := a.binSearch(value, false); r == 0 {
a.doRemoveWithoutLock(i)
}
}
a.lazyInit()
a.SortedTArray.RemoveValues(values...)
}
// PopLeft pops and returns an item from the beginning of array.
// 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, false
}
value = a.array[0]
a.array = a.array[1:]
return value, true
a.lazyInit()
return a.SortedTArray.PopLeft()
}
// PopRight pops and returns an item from the end of array.
// 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, false
}
value = a.array[index]
a.array = a.array[:index]
return value, true
a.lazyInit()
return a.SortedTArray.PopRight()
}
// PopRand randomly pops and return an item out of 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)))
a.lazyInit()
return a.SortedTArray.PopRand()
}
// 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 <= 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++ {
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
a.lazyInit()
return a.SortedTArray.PopRands(size)
}
// 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()
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
a.lazyInit()
return a.SortedTArray.PopLefts(size)
}
// 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 size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
return value
a.lazyInit()
return a.SortedTArray.PopRights(size)
}
// Range picks and returns items by range, like array[start:end].
@ -314,26 +206,8 @@ func (a *SortedIntArray) PopRights(size int) []int {
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *SortedIntArray) Range(start int, end ...int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
offsetEnd := len(a.array)
if len(end) > 0 && end[0] < offsetEnd {
offsetEnd = end[0]
}
if start > offsetEnd {
return nil
}
if start < 0 {
start = 0
}
array := ([]int)(nil)
if a.mu.IsSafe() {
array = make([]int, offsetEnd-start)
copy(array, a.array[start:offsetEnd])
} else {
array = a.array[start:offsetEnd]
}
return array
a.lazyInit()
return a.SortedTArray.Range(start, end...)
}
// SubSlice returns a slice of elements from the array as specified
@ -350,198 +224,91 @@ func (a *SortedIntArray) Range(start int, end ...int) []int {
//
// Any possibility crossing the left border of array, it will fail.
func (a *SortedIntArray) SubSlice(offset int, length ...int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
size := len(a.array)
if len(length) > 0 {
size = length[0]
}
if offset > len(a.array) {
return nil
}
if offset < 0 {
offset = len(a.array) + offset
if offset < 0 {
return nil
}
}
if size < 0 {
offset += size
size = -size
if offset < 0 {
return nil
}
}
end := offset + size
if end > len(a.array) {
end = len(a.array)
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]int, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:end]
}
a.lazyInit()
return a.SortedTArray.SubSlice(offset, length...)
}
// Len returns the length of array.
func (a *SortedIntArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.lazyInit()
return a.SortedTArray.Len()
}
// Sum returns the sum of values in an array.
func (a *SortedIntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
a.lazyInit()
return a.SortedTArray.Sum()
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *SortedIntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
a.lazyInit()
return a.SortedTArray.Slice()
}
// Interfaces returns current array as []interface{}.
func (a *SortedIntArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
// Interfaces returns current array as []any.
func (a *SortedIntArray) Interfaces() []any {
a.lazyInit()
return a.SortedTArray.Interfaces()
}
// Contains checks whether a value exists in the array.
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) != -1
a.lazyInit()
return a.SortedTArray.Contains(value)
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *SortedIntArray) Search(value int) (index int) {
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If `result` equals to 0, it means the value at `index` is equals to `value`.
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
// If `result` greater than 0, it means the value at `index` is greater than `value`.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = min + int((max-min)/2)
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
a.lazyInit()
return a.SortedTArray.Search(value)
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
a.lazyInit()
a.SortedTArray.SetUnique(unique)
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedIntArray) Unique() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
a.lazyInit()
a.SortedTArray.Unique()
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedIntArrayFrom(array, a.mu.IsSafe())
a.lazyInit()
return &SortedIntArray{
SortedTArray: a.SortedTArray.Clone(),
}
}
// Clear deletes all items of current array.
func (a *SortedIntArray) Clear() *SortedIntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0)
}
a.mu.Unlock()
a.lazyInit()
a.SortedTArray.Clear()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
a.lazyInit()
a.SortedTArray.LockFunc(f)
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
a.lazyInit()
a.SortedTArray.RLockFunc(f)
return a
}
@ -549,7 +316,8 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
func (a *SortedIntArray) Merge(array any) *SortedIntArray {
a.lazyInit()
return a.Add(gconv.Ints(array)...)
}
@ -557,104 +325,52 @@ func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *SortedIntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
a.lazyInit()
return a.SortedTArray.Chunk(size)
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedIntArray) Rand() (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return 0, false
}
return a.array[grand.Intn(len(a.array))], true
a.lazyInit()
return a.SortedTArray.Rand()
}
// 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 <= 0 || len(a.array) == 0 {
return nil
}
array := make([]int, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return array
a.lazyInit()
return a.SortedTArray.Rands(size)
}
// Join joins array elements with a string `glue`.
func (a *SortedIntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.lazyInit()
return a.SortedTArray.Join(glue)
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedIntArray) CountValues() map[int]int {
m := make(map[int]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
a.lazyInit()
return a.SortedTArray.CountValues()
}
// Iterator is alias of IteratorAsc.
func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
a.lazyInit()
a.SortedTArray.Iterator(f)
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
a.lazyInit()
a.SortedTArray.IteratorAsc(f)
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
a.lazyInit()
a.SortedTArray.IteratorDesc(f)
}
// String returns current array as a string, which implements like json.Marshal does.
@ -662,73 +378,64 @@ func (a *SortedIntArray) String() string {
if a == nil {
return ""
}
a.lazyInit()
return "[" + a.Join(",") + "]"
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
a.lazyInit()
return a.SortedTArray.MarshalJSON()
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
if a.comparator == nil {
a.array = make([]int, 0)
a.lazyInit()
if a.comparator == nil || a.sorter == nil {
a.comparator = defaultComparatorInt
a.sorter = quickSortInt
a.array = make([]int, 0)
}
a.mu.Lock()
defer a.mu.Unlock()
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
return err
}
if a.array != nil {
sort.Ints(a.array)
}
return nil
return a.SortedTArray.UnmarshalJSON(b)
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
func (a *SortedIntArray) UnmarshalValue(value any) (err error) {
a.lazyInit()
if a.comparator == nil || a.sorter == nil {
a.comparator = defaultComparatorInt
a.sorter = quickSortInt
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceInt(value)
}
if a.array != nil {
sort.Ints(a.array)
}
return err
return a.SortedTArray.UnmarshalValue(value)
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *SortedIntArray) Filter(filter func(index int, value int) bool) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if filter(i, a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
a.lazyInit()
a.SortedTArray.Filter(filter)
return a
}
// FilterEmpty removes all zero value of the array.
func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
a.lazyInit()
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
if a.array[0] != 0 && a.array[len(a.array)-1] != 0 {
a.SortedTArray.FilterEmpty()
return a
}
for i := 0; i < len(a.array); {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
@ -739,6 +446,7 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
i--
} else {
break
}
@ -748,40 +456,21 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
// Walk applies a user supplied function `f` to every item of array.
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer quickSortInt(a.array, a.getComparator())
for i, v := range a.array {
a.array[i] = f(v)
}
a.lazyInit()
a.SortedTArray.Walk(f)
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedIntArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it returns a default comparator.
func (a *SortedIntArray) getComparator() func(a, b int) int {
if a.comparator == nil {
return defaultComparatorInt
}
return a.comparator
a.lazyInit()
return a.SortedTArray.IsEmpty()
}
// DeepCopy implements interface for deep copy of current type.
func (a *SortedIntArray) DeepCopy() interface{} {
if a == nil {
return nil
func (a *SortedIntArray) DeepCopy() any {
a.lazyInit()
return &SortedIntArray{
SortedTArray: a.SortedTArray.DeepCopy().(*SortedTArray[int]),
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]int, len(a.array))
copy(newSlice, a.array)
return NewSortedIntArrayFrom(newSlice, a.mu.IsSafe())
}

View File

@ -8,15 +8,10 @@ package garray
import (
"bytes"
"math"
"sort"
"strings"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
)
// SortedStrArray is a golang sorted string array with rich features.
@ -25,10 +20,15 @@ import (
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedStrArray struct {
mu rwmutex.RWMutex
array []string
unique bool // Whether enable unique feature(false)
comparator func(a, b string) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
*SortedTArray[string]
}
// lazyInit lazily initializes the array.
func (a *SortedStrArray) lazyInit() {
if a.SortedTArray == nil {
a.SortedTArray = NewSortedTArraySize(0, defaultComparatorStr, false)
a.SetSorter(quickSortStr)
}
}
// NewSortedStrArray creates and returns an empty sorted array.
@ -50,10 +50,10 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
a := NewSortedTArraySize(cap, defaultComparatorStr, safe...)
a.SetSorter(quickSortStr)
return &SortedStrArray{
mu: rwmutex.Create(safe...),
array: make([]string, 0, cap),
comparator: defaultComparatorStr,
SortedTArray: a,
}
}
@ -78,218 +78,112 @@ func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray {
// SetArray sets the underlying slice array with the given `array`.
func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
quickSortStr(a.array, a.getComparator())
a.lazyInit()
a.SortedTArray.SetArray(array)
return a
}
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns an empty string.
func (a *SortedStrArray) At(index int) (value string) {
value, _ = a.Get(index)
return
a.lazyInit()
return a.SortedTArray.At(index)
}
// Sort sorts the array in increasing order.
// The parameter `reverse` controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedStrArray) Sort() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
quickSortStr(a.array, a.getComparator())
a.lazyInit()
a.SortedTArray.Sort()
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
return a.Append(values...)
a.lazyInit()
a.SortedTArray.Add(values...)
return a
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
}
a.lazyInit()
a.SortedTArray.Append(values...)
return a
}
// 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()
if index < 0 || index >= len(a.array) {
return "", false
}
return a.array[index], true
a.lazyInit()
return a.SortedTArray.Get(index)
}
// Remove removes an item by index.
// 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 "", false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
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]
a.array = append(a.array[:index], a.array[index+1:]...)
return value, true
a.lazyInit()
return a.SortedTArray.Remove(index)
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedStrArray) RemoveValue(value string) bool {
a.mu.Lock()
defer a.mu.Unlock()
if i, r := a.binSearch(value, false); r == 0 {
_, res := a.doRemoveWithoutLock(i)
return res
}
return false
a.lazyInit()
return a.SortedTArray.RemoveValue(value)
}
// RemoveValues removes an item by `values`.
func (a *SortedStrArray) RemoveValues(values ...string) {
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
if i, r := a.binSearch(value, false); r == 0 {
a.doRemoveWithoutLock(i)
}
}
a.lazyInit()
a.SortedTArray.RemoveValues(values...)
}
// PopLeft pops and returns an item from the beginning of array.
// 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 "", false
}
value = a.array[0]
a.array = a.array[1:]
return value, true
a.lazyInit()
return a.SortedTArray.PopLeft()
}
// PopRight pops and returns an item from the end of array.
// 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 "", false
}
value = a.array[index]
a.array = a.array[:index]
return value, true
a.lazyInit()
return a.SortedTArray.PopRight()
}
// PopRand randomly pops and return an item out of 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)))
a.lazyInit()
return a.SortedTArray.PopRand()
}
// 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 <= 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++ {
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
a.lazyInit()
return a.SortedTArray.PopRands(size)
}
// 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()
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
a.lazyInit()
return a.SortedTArray.PopLefts(size)
}
// 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 size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
return value
a.lazyInit()
return a.SortedTArray.PopRights(size)
}
// Range picks and returns items by range, like array[start:end].
@ -300,26 +194,8 @@ func (a *SortedStrArray) PopRights(size int) []string {
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *SortedStrArray) Range(start int, end ...int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
offsetEnd := len(a.array)
if len(end) > 0 && end[0] < offsetEnd {
offsetEnd = end[0]
}
if start > offsetEnd {
return nil
}
if start < 0 {
start = 0
}
array := ([]string)(nil)
if a.mu.IsSafe() {
array = make([]string, offsetEnd-start)
copy(array, a.array[start:offsetEnd])
} else {
array = a.array[start:offsetEnd]
}
return array
a.lazyInit()
return a.SortedTArray.Range(start, end...)
}
// SubSlice returns a slice of elements from the array as specified
@ -336,95 +212,46 @@ func (a *SortedStrArray) Range(start int, end ...int) []string {
//
// Any possibility crossing the left border of array, it will fail.
func (a *SortedStrArray) SubSlice(offset int, length ...int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
size := len(a.array)
if len(length) > 0 {
size = length[0]
}
if offset > len(a.array) {
return nil
}
if offset < 0 {
offset = len(a.array) + offset
if offset < 0 {
return nil
}
}
if size < 0 {
offset += size
size = -size
if offset < 0 {
return nil
}
}
end := offset + size
if end > len(a.array) {
end = len(a.array)
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]string, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:end]
}
a.lazyInit()
return a.SortedTArray.SubSlice(offset, length...)
}
// Sum returns the sum of values in an array.
func (a *SortedStrArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.lazyInit()
return a.SortedTArray.Sum()
}
// Len returns the length of array.
func (a *SortedStrArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.lazyInit()
return a.SortedTArray.Len()
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *SortedStrArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
a.lazyInit()
return a.SortedTArray.Slice()
}
// Interfaces returns current array as []interface{}.
func (a *SortedStrArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
// Interfaces returns current array as []any.
func (a *SortedStrArray) Interfaces() []any {
a.lazyInit()
return a.SortedTArray.Interfaces()
}
// Contains checks whether a value exists in the array.
func (a *SortedStrArray) Contains(value string) bool {
return a.Search(value) != -1
a.lazyInit()
return a.SortedTArray.Contains(value)
}
// ContainsI checks whether a value exists in the array with case-insensitively.
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
func (a *SortedStrArray) ContainsI(value string) bool {
a.lazyInit()
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
@ -441,109 +268,52 @@ func (a *SortedStrArray) ContainsI(value string) bool {
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *SortedStrArray) Search(value string) (index int) {
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If `result` equals to 0, it means the value at `index` is equals to `value`.
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
// If `result` greater than 0, it means the value at `index` is greater than `value`.
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = min + int((max-min)/2)
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
a.lazyInit()
return a.SortedTArray.Search(value)
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
a.lazyInit()
a.SortedTArray.SetUnique(unique)
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedStrArray) Unique() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
a.lazyInit()
a.SortedTArray.Unique()
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedStrArray) Clone() (newArray *SortedStrArray) {
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedStrArrayFrom(array, a.mu.IsSafe())
a.lazyInit()
return &SortedStrArray{
SortedTArray: a.SortedTArray.Clone(),
}
}
// Clear deletes all items of current array.
func (a *SortedStrArray) Clear() *SortedStrArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
a.lazyInit()
a.SortedTArray.Clear()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
a.lazyInit()
a.SortedTArray.LockFunc(f)
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
a.lazyInit()
a.SortedTArray.RLockFunc(f)
return a
}
@ -551,7 +321,8 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
func (a *SortedStrArray) Merge(array any) *SortedStrArray {
a.lazyInit()
return a.Add(gconv.Strings(array)...)
}
@ -559,104 +330,52 @@ func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *SortedStrArray) Chunk(size int) [][]string {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
a.lazyInit()
return a.SortedTArray.Chunk(size)
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedStrArray) Rand() (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return "", false
}
return a.array[grand.Intn(len(a.array))], true
a.lazyInit()
return a.SortedTArray.Rand()
}
// 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 <= 0 || len(a.array) == 0 {
return nil
}
array := make([]string, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return array
a.lazyInit()
return a.SortedTArray.Rands(size)
}
// Join joins array elements with a string `glue`.
func (a *SortedStrArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(v)
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.lazyInit()
return a.SortedTArray.Join(glue)
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedStrArray) CountValues() map[string]int {
m := make(map[string]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
a.lazyInit()
return a.SortedTArray.CountValues()
}
// Iterator is alias of IteratorAsc.
func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
a.lazyInit()
a.SortedTArray.Iterator(f)
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
a.lazyInit()
a.SortedTArray.IteratorAsc(f)
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
a.lazyInit()
a.SortedTArray.IteratorDesc(f)
}
// String returns current array as a string, which implements like json.Marshal does.
@ -664,6 +383,7 @@ func (a *SortedStrArray) String() string {
if a == nil {
return ""
}
a.lazyInit()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
@ -681,67 +401,56 @@ func (a *SortedStrArray) String() string {
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
a.lazyInit()
return a.SortedTArray.MarshalJSON()
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
if a.comparator == nil {
a.array = make([]string, 0)
a.lazyInit()
if a.comparator == nil || a.sorter == nil {
a.comparator = defaultComparatorStr
a.sorter = quickSortStr
a.array = make([]string, 0)
}
a.mu.Lock()
defer a.mu.Unlock()
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
return err
}
if a.array != nil {
sort.Strings(a.array)
}
return nil
return a.SortedTArray.UnmarshalJSON(b)
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
func (a *SortedStrArray) UnmarshalValue(value any) (err error) {
a.lazyInit()
if a.comparator == nil || a.sorter == nil {
a.comparator = defaultComparatorStr
a.sorter = quickSortStr
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceStr(value)
}
if a.array != nil {
sort.Strings(a.array)
}
return err
return a.SortedTArray.UnmarshalValue(value)
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *SortedStrArray) Filter(filter func(index int, value string) bool) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if filter(i, a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
a.lazyInit()
a.SortedTArray.Filter(filter)
return a
}
// FilterEmpty removes all empty string value of the array.
func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
a.lazyInit()
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
if a.array[0] != "" && a.array[len(a.array)-1] != "" {
a.SortedTArray.FilterEmpty()
return a
}
for i := 0; i < len(a.array); {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
@ -752,6 +461,7 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
i--
} else {
break
}
@ -761,40 +471,21 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
// Walk applies a user supplied function `f` to every item of array.
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer quickSortStr(a.array, a.getComparator())
for i, v := range a.array {
a.array[i] = f(v)
}
a.lazyInit()
a.SortedTArray.Walk(f)
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedStrArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it returns a default comparator.
func (a *SortedStrArray) getComparator() func(a, b string) int {
if a.comparator == nil {
return defaultComparatorStr
}
return a.comparator
a.lazyInit()
return a.SortedTArray.IsEmpty()
}
// DeepCopy implements interface for deep copy of current type.
func (a *SortedStrArray) DeepCopy() interface{} {
if a == nil {
return nil
func (a *SortedStrArray) DeepCopy() any {
a.lazyInit()
return &SortedStrArray{
SortedTArray: a.SortedTArray.DeepCopy().(*SortedTArray[string]),
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]string, len(a.array))
copy(newSlice, a.array)
return NewSortedStrArrayFrom(newSlice, a.mu.IsSafe())
}

View File

@ -0,0 +1,852 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"bytes"
"math"
"github.com/gogf/gf/v2/internal/deepcopy"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/gogf/gf/v2/util/gutil"
)
// SortedTArray is a golang sorted array with rich features.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedTArray[T comparable] struct {
mu rwmutex.RWMutex
array []T
unique bool // Whether enable unique feature(false)
comparator func(a, b T) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
sorter func(values []T, comparator func(a, b T) int)
}
// NewSortedTArray creates and returns an empty sorted array.
// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default.
// The parameter `comparator` used to compare values to sort in array,
// if it returns value < 0, means `a` < `b`; the `a` will be inserted before `b`;
// if it returns value = 0, means `a` = `b`; the `a` will be replaced by `b`;
// if it returns value > 0, means `a` > `b`; the `a` will be inserted after `b`;
func NewSortedTArray[T comparable](comparator func(a, b T) int, safe ...bool) *SortedTArray[T] {
if comparator == nil {
comparator = gutil.ComparatorTStr
}
return NewSortedTArraySize(0, comparator, safe...)
}
// NewSortedTArraySize create and returns an sorted array with given size and cap.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedTArraySize[T comparable](cap int, comparator func(a, b T) int, safe ...bool) *SortedTArray[T] {
if comparator == nil {
comparator = gutil.ComparatorTStr
}
return &SortedTArray[T]{
mu: rwmutex.Create(safe...),
array: make([]T, 0, cap),
comparator: comparator,
sorter: nil,
}
}
// NewSortedTArrayFrom creates and returns an sorted array with given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedTArrayFrom[T comparable](array []T, comparator func(a, b T) int, safe ...bool) *SortedTArray[T] {
if comparator == nil {
comparator = gutil.ComparatorTStr
}
a := NewSortedTArraySize(0, comparator, safe...)
a.array = array
a.getSorter()(a.array, a.getComparator())
return a
}
// NewSortedTArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedTArrayFromCopy[T comparable](array []T, comparator func(a, b T) int, safe ...bool) *SortedTArray[T] {
if comparator == nil {
comparator = gutil.ComparatorTStr
}
newArray := make([]T, len(array))
copy(newArray, array)
return NewSortedTArrayFrom(newArray, comparator, safe...)
}
func (a *SortedTArray[T]) getSorter() func(values []T, comparator func(a, b T) int) {
if a.sorter == nil {
return defaultSorter
} else {
return a.sorter
}
}
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns the zero value of type `T`
func (a *SortedTArray[T]) At(index int) (value T) {
value, _ = a.Get(index)
return
}
// SetArray sets the underlying slice array with the given `array`.
func (a *SortedTArray[T]) SetArray(array []T) *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
a.getSorter()(a.array, a.getComparator())
return a
}
// SetSorter sets/changes the sorter for sorting.
func (a *SortedTArray[T]) SetSorter(sorter func(values []T, comparator func(a, b T) int)) {
if sorter == nil {
a.sorter = defaultSorter
} else {
a.sorter = sorter
}
a.sorter(a.array, a.getComparator())
}
// SetComparator sets/changes the comparator for sorting.
// It resorts the array as the comparator is changed.
func (a *SortedTArray[T]) SetComparator(comparator func(a, b T) int) {
a.mu.Lock()
defer a.mu.Unlock()
if comparator == nil {
comparator = gutil.ComparatorTStr
}
a.comparator = comparator
a.getSorter()(a.array, comparator)
}
// Sort sorts the array in increasing order.
// The parameter `reverse` controls whether sort
// in increasing order(default) or decreasing order
func (a *SortedTArray[T]) Sort() *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
a.getSorter()(a.array, a.getComparator())
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedTArray[T]) Add(values ...T) *SortedTArray[T] {
return a.Append(values...)
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedTArray[T]) Append(values ...T) *SortedTArray[T] {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
a.array = append(a.array[:index], append([]T{value}, a.array[index:]...)...)
}
return a
}
// 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 *SortedTArray[T]) Get(index int) (value T, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if index < 0 || index >= len(a.array) {
found = false
return
}
return a.array[index], true
}
// Remove removes an item by index.
// If the given `index` is out of range of the array, the `found` is false.
func (a *SortedTArray[T]) Remove(index int) (value T, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedTArray[T]) doRemoveWithoutLock(index int) (value T, found bool) {
if index < 0 || index >= len(a.array) {
found = false
return
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
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]
a.array = append(a.array[:index], a.array[index+1:]...)
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 *SortedTArray[T]) RemoveValue(value T) bool {
a.mu.Lock()
defer a.mu.Unlock()
if i, r := a.binSearch(value, false); r == 0 {
_, res := a.doRemoveWithoutLock(i)
return res
}
return false
}
// RemoveValues removes an item by `values`.
func (a *SortedTArray[T]) RemoveValues(values ...T) {
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
if i, r := a.binSearch(value, false); r == 0 {
a.doRemoveWithoutLock(i)
}
}
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, the `found` is false.
func (a *SortedTArray[T]) PopLeft() (value T, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
found = false
return
}
value = a.array[0]
a.array = a.array[1:]
return value, true
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, the `found` is false.
func (a *SortedTArray[T]) PopRight() (value T, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index < 0 {
found = false
return
}
value = a.array[index]
a.array = a.array[:index]
return value, true
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, the `found` is false.
func (a *SortedTArray[T]) PopRand() (value T, 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 *SortedTArray[T]) PopRands(size int) []T {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]T, size)
for i := 0; i < size; i++ {
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns `size` items from the beginning of array.
func (a *SortedTArray[T]) PopLefts(size int) []T {
a.mu.Lock()
defer a.mu.Unlock()
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.
func (a *SortedTArray[T]) PopRights(size int) []T {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// If `end` is negative, then the offset will start from the end of array.
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *SortedTArray[T]) Range(start int, end ...int) []T {
a.mu.RLock()
defer a.mu.RUnlock()
offsetEnd := len(a.array)
if len(end) > 0 && end[0] < offsetEnd {
offsetEnd = end[0]
}
if start > offsetEnd {
return nil
}
if start < 0 {
start = 0
}
array := ([]T)(nil)
if a.mu.IsSafe() {
array = make([]T, offsetEnd-start)
copy(array, a.array[start:offsetEnd])
} else {
array = a.array[start:offsetEnd]
}
return array
}
// SubSlice returns a slice of elements from the array as specified
// by the `offset` and `size` parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
//
// If offset is non-negative, the sequence will start at that offset in the array.
// If offset is negative, the sequence will start that far from the end of the array.
//
// If length is given and is positive, then the sequence will have up to that many elements in it.
// If the array is shorter than the length, then only the available array elements will be present.
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
//
// Any possibility crossing the left border of array, it will fail.
func (a *SortedTArray[T]) SubSlice(offset int, length ...int) []T {
a.mu.RLock()
defer a.mu.RUnlock()
size := len(a.array)
if len(length) > 0 {
size = length[0]
}
if offset > len(a.array) {
return nil
}
if offset < 0 {
offset = len(a.array) + offset
if offset < 0 {
return nil
}
}
if size < 0 {
offset += size
size = -size
if offset < 0 {
return nil
}
}
end := offset + size
if end > len(a.array) {
end = len(a.array)
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]T, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:end]
}
}
// Sum returns the sum of values in an array.
func (a *SortedTArray[T]) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Len returns the length of array.
func (a *SortedTArray[T]) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *SortedTArray[T]) Slice() []T {
var array []T
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]T, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Interfaces returns current array as []any.
func (a *SortedTArray[T]) Interfaces() []any {
return tToAnySlice(a.Slice())
}
// Contains checks whether a value exists in the array.
func (a *SortedTArray[T]) Contains(value T) bool {
return a.Search(value) != -1
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *SortedTArray[T]) Search(value T) (index int) {
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
// It returns the last compared index and the result.
// If `result` equals to 0, it means the value at `index` is equals to `value`.
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
// If `result` greater than 0, it means the value at `index` is greater than `value`.
func (a *SortedTArray[T]) binSearch(value T, lock bool) (index int, result int) {
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = min + (max-min)/2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also does unique check, remove all repeated items.
func (a *SortedTArray[T]) SetUnique(unique bool) *SortedTArray[T] {
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedTArray[T]) Unique() *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
for i := 0; i < len(a.array)-1; {
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+2:]...)
} else {
i++
}
}
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedTArray[T]) Clone() (newArray *SortedTArray[T]) {
a.mu.RLock()
array := make([]T, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedTArrayFrom[T](array, a.comparator, a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *SortedTArray[T]) Clear() *SortedTArray[T] {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]T, 0)
}
a.mu.Unlock()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *SortedTArray[T]) LockFunc(f func(array []T)) *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer a.getSorter()(a.array, a.getComparator())
f(a.array)
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *SortedTArray[T]) RLockFunc(f func(array []T)) *SortedTArray[T] {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
}
// Merge merges `array` into current array.
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedTArray[T]) Merge(array any) *SortedTArray[T] {
var vals []T
switch v := array.(type) {
case *SortedTArray[T]:
vals = v.Slice()
case *TArray[T]:
vals = v.Slice()
case []T:
vals = v
default:
interfaces := gconv.Interfaces(v)
if err := gconv.Scan(interfaces, &vals); err != nil {
panic(err)
}
}
return a.Add(vals...)
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *SortedTArray[T]) Chunk(size int) [][]T {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]T
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedTArray[T]) Rand() (value T, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
found = false
return
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns `size` items from array(no deleting).
func (a *SortedTArray[T]) Rands(size int) []T {
a.mu.RLock()
defer a.mu.RUnlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
array := make([]T, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return array
}
// Join joins array elements with a string `glue`.
func (a *SortedTArray[T]) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *SortedTArray[T]) CountValues() map[T]int {
m := make(map[T]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// Iterator is alias of IteratorAsc.
func (a *SortedTArray[T]) Iterator(f func(k int, v T) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedTArray[T]) IteratorAsc(f func(k int, v T) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *SortedTArray[T]) IteratorDesc(f func(k int, v T) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *SortedTArray[T]) String() string {
if a == nil {
return ""
}
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
buffer.WriteByte('[')
s := ""
for k, v := range a.array {
s = gconv.String(v)
if gstr.IsNumeric(s) {
buffer.WriteString(s)
} else {
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
}
if k != len(a.array)-1 {
buffer.WriteByte(',')
}
}
buffer.WriteByte(']')
return buffer.String()
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a SortedTArray[T]) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
// Note that the comparator is set as string comparator in default.
func (a *SortedTArray[T]) UnmarshalJSON(b []byte) error {
if a.comparator == nil {
a.array = make([]T, 0)
a.comparator = gutil.ComparatorTStr
}
a.mu.Lock()
defer a.mu.Unlock()
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
return err
}
if a.comparator != nil && a.array != nil {
a.getSorter()(a.array, a.comparator)
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
// Note that the comparator is set as string comparator in default.
func (a *SortedTArray[T]) UnmarshalValue(value any) (err error) {
if a.comparator == nil {
a.comparator = gutil.ComparatorTStr
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
default:
if err = gconv.Scan(value, &a.array); err != nil {
return
}
}
if a.comparator != nil && a.array != nil {
a.getSorter()(a.array, a.comparator)
}
return err
}
// FilterNil removes all nil value of the array.
func (a *SortedTArray[T]) FilterNil() *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *SortedTArray[T]) Filter(filter func(index int, value T) bool) *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if filter(i, a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *SortedTArray[T]) FilterEmpty() *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// Walk applies a user supplied function `f` to every item of array.
func (a *SortedTArray[T]) Walk(f func(value T) T) *SortedTArray[T] {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer a.getSorter()(a.array, a.getComparator())
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedTArray[T]) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (a *SortedTArray[T]) getComparator() func(a, b T) int {
if a.comparator == nil {
a.comparator = gutil.ComparatorTStr
}
return a.comparator
}
// DeepCopy implements interface for deep copy of current type.
func (a *SortedTArray[T]) DeepCopy() any {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]T, len(a.array))
for i, v := range a.array {
newSlice[i], _ = deepcopy.Copy(v).(T)
}
return NewSortedTArrayFrom[T](newSlice, a.comparator, a.mu.IsSafe())
}

View File

@ -14,12 +14,12 @@ import (
type anySortedArrayItem struct {
priority int64
value interface{}
value any
}
var (
anyArray = garray.NewArray()
anySortedArray = garray.NewSortedArray(func(a, b interface{}) int {
anySortedArray = garray.NewSortedArray(func(a, b any) int {
return int(a.(anySortedArrayItem).priority - b.(anySortedArrayItem).priority)
})
)

View File

@ -81,13 +81,13 @@ func ExampleArray_Iterator() {
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
// with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
array.Iterator(func(k int, v interface{}) bool {
array.Iterator(func(k int, v any) bool {
fmt.Println(k, v)
return true
})
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
array.IteratorDesc(func(k int, v interface{}) bool {
array.IteratorDesc(func(k int, v any) bool {
fmt.Println(k, v)
return true
})
@ -163,7 +163,7 @@ func ExampleArray_Chunk() {
}
func ExampleArray_PopLeft() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
array := garray.NewFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
@ -180,7 +180,7 @@ func ExampleArray_PopLeft() {
}
func ExampleArray_PopLefts() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
array := garray.NewFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
@ -197,7 +197,7 @@ func ExampleArray_PopLefts() {
}
func ExampleArray_PopRight() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
array := garray.NewFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
@ -214,7 +214,7 @@ func ExampleArray_PopRight() {
}
func ExampleArray_PopRights() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
array := garray.NewFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
@ -265,10 +265,10 @@ func ExampleArray_Merge() {
func ExampleArray_Filter() {
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
fmt.Printf("%#v\n", array1.Filter(func(index int, value interface{}) bool {
fmt.Printf("%#v\n", array1.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}).Slice())
fmt.Printf("%#v\n", array2.Filter(func(index int, value interface{}) bool {
fmt.Printf("%#v\n", array2.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}).Slice())

File diff suppressed because it is too large Load Diff

View File

@ -381,7 +381,7 @@ func ExampleSortedStrArray_LockFunc() {
fmt.Println(s)
// Output:
// ["a","b","GF fans"]
// ["GF fans","a","b"]
}
func ExampleSortedStrArray_RLockFunc() {

View File

@ -0,0 +1,576 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
func ExampleSortedTArray_Walk() {
var array garray.SortedTArray[string]
array.SetComparator(gutil.ComparatorT)
tables := g.SliceStr{"user", "user_detail"}
prefix := "gf_"
array.Append(tables...)
// Add prefix for given table names.
array.Walk(func(value string) string {
return prefix + value
})
fmt.Println(array.Slice())
// Output:
// [gf_user gf_user_detail]
}
func ExampleNewSortedTArray() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.Append("b")
s.Append("d")
s.Append("c")
s.Append("a")
fmt.Println(s.Slice())
// Output:
// [a b c d]
}
func ExampleNewSortedTArraySize() {
s := garray.NewSortedTArraySize[string](3, gutil.ComparatorT)
s.SetArray([]string{"b", "d", "a", "c"})
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
// Output:
// [a b c d] 4 4
}
func ExampleNewSortedTArrayFromCopy() {
s := garray.NewSortedTArrayFromCopy(g.SliceStr{"b", "d", "c", "a"}, gutil.ComparatorT)
fmt.Println(s.Slice())
// Output:
// [a b c d]
}
func ExampleSortedTArray_At() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "d", "c", "a"}, gutil.ComparatorT)
sAt := s.At(2)
fmt.Println(s)
fmt.Println(sAt)
// Output:
// ["a","b","c","d"]
// c
}
func ExampleSortedTArray_Get() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "d", "c", "a", "e"}, gutil.ComparatorT)
sGet, sBool := s.Get(3)
fmt.Println(s)
fmt.Println(sGet, sBool)
// Output:
// ["a","b","c","d","e"]
// d true
}
func ExampleSortedTArray_SetArray() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray([]string{"b", "d", "a", "c"})
fmt.Println(s.Slice())
// Output:
// [a b c d]
}
func ExampleSortedTArray_SetUnique() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray([]string{"b", "d", "a", "c", "c", "a"})
fmt.Println(s.SetUnique(true))
// Output:
// ["a","b","c","d"]
}
func ExampleSortedTArray_Sum() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray([]string{"5", "3", "2"})
fmt.Println(s)
a := s.Sum()
fmt.Println(a)
// Output:
// [2,3,5]
// 10
}
func ExampleSortedTArray_Sort() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "a", "c"})
fmt.Println(s)
a := s.Sort()
fmt.Println(a)
// Output:
// ["a","b","c","d"]
// ["a","b","c","d"]
}
func ExampleSortedTArray_Remove() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
fmt.Println(s.Slice())
s.Remove(1)
fmt.Println(s.Slice())
// Output:
// [a b c d]
// [a c d]
}
func ExampleSortedTArray_RemoveValue() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
fmt.Println(s.Slice())
s.RemoveValue("b")
fmt.Println(s.Slice())
// Output:
// [a b c d]
// [a c d]
}
func ExampleSortedTArray_PopLeft() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
r, _ := s.PopLeft()
fmt.Println(r)
fmt.Println(s.Slice())
// Output:
// a
// [b c d]
}
func ExampleSortedTArray_PopRight() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
fmt.Println(s.Slice())
r, _ := s.PopRight()
fmt.Println(r)
fmt.Println(s.Slice())
// Output:
// [a b c d]
// d
// [a b c]
}
func ExampleSortedTArray_PopRights() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.PopRights(2)
fmt.Println(r)
fmt.Println(s)
// Output:
// [g h]
// ["a","b","c","d","e","f"]
}
func ExampleSortedTArray_Rand() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r, _ := s.PopRand()
fmt.Println(r)
fmt.Println(s)
// May Output:
// b
// ["a","c","d","e","f","g","h"]
}
func ExampleSortedTArray_PopRands() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.PopRands(2)
fmt.Println(r)
fmt.Println(s)
// May Output:
// [d a]
// ["b","c","e","f","g","h"]
}
func ExampleSortedTArray_PopLefts() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.PopLefts(2)
fmt.Println(r)
fmt.Println(s)
// Output:
// [a b]
// ["c","d","e","f","g","h"]
}
func ExampleSortedTArray_Range() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.Range(2, 5)
fmt.Println(r)
// Output:
// [c d e]
}
func ExampleSortedTArray_SubSlice() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.SubSlice(3, 4)
fmt.Println(s.Slice())
fmt.Println(r)
// Output:
// [a b c d e f g h]
// [d e f g]
}
func ExampleSortedTArray_Add() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.Add("b", "d", "c", "a")
fmt.Println(s)
// Output:
// ["a","b","c","d"]
}
func ExampleSortedTArray_Append() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
fmt.Println(s)
s.Append("f", "e", "g")
fmt.Println(s)
// Output:
// ["a","b","c","d"]
// ["a","b","c","d","e","f","g"]
}
func ExampleSortedTArray_Len() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
fmt.Println(s)
fmt.Println(s.Len())
// Output:
// ["a","b","c","d","e","f","g","h"]
// 8
}
func ExampleSortedTArray_Slice() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
fmt.Println(s.Slice())
// Output:
// [a b c d e f g h]
}
func ExampleSortedTArray_Interfaces() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.Interfaces()
fmt.Println(r)
// Output:
// [a b c d e f g h]
}
func ExampleSortedTArray_Clone() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
r := s.Clone()
fmt.Println(r)
fmt.Println(s)
// Output:
// ["a","b","c","d","e","f","g","h"]
// ["a","b","c","d","e","f","g","h"]
}
func ExampleSortedTArray_Clear() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
fmt.Println(s)
fmt.Println(s.Clear())
fmt.Println(s)
// Output:
// ["a","b","c","d","e","f","g","h"]
// []
// []
}
func ExampleSortedTArray_Contains() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
fmt.Println(s.Contains("e"))
fmt.Println(s.Contains("E"))
fmt.Println(s.Contains("z"))
// Output:
// true
// false
// false
}
func ExampleSortedTArray_Search() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
fmt.Println(s)
fmt.Println(s.Search("e"))
fmt.Println(s.Search("E"))
fmt.Println(s.Search("z"))
// Output:
// ["a","b","c","d","e","f","g","h"]
// 4
// -1
// -1
}
func ExampleSortedTArray_Unique() {
s := garray.NewSortedTArray[string](gutil.ComparatorT)
s.SetArray(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"})
fmt.Println(s)
fmt.Println(s.Unique())
// Output:
// ["a","b","c","c","c","d","d"]
// ["a","b","c","d"]
}
func ExampleSortedTArray_LockFunc() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s.LockFunc(func(array []string) {
array[len(array)-1] = "GF fans"
})
fmt.Println(s)
// Output:
// ["GF fans","a","b"]
}
func ExampleSortedTArray_RLockFunc() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s.RLockFunc(func(array []string) {
array[len(array)-1] = "GF fans"
fmt.Println(array[len(array)-1])
})
fmt.Println(s)
// Output:
// GF fans
// ["a","b","GF fans"]
}
func ExampleSortedTArray_Merge() {
s1 := garray.NewSortedTArray[string](gutil.ComparatorT)
s2 := garray.NewSortedTArray[string](gutil.ComparatorT)
s1.SetArray(g.SliceStr{"b", "c", "a"})
s2.SetArray(g.SliceStr{"e", "d", "f"})
fmt.Println(s1)
fmt.Println(s2)
s1.Merge(s2)
fmt.Println(s1)
// Output:
// ["a","b","c"]
// ["d","e","f"]
// ["a","b","c","d","e","f"]
}
func ExampleSortedTArray_Chunk() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}, gutil.ComparatorT)
r := s.Chunk(3)
fmt.Println(r)
// Output:
// [[a b c] [d e f] [g h]]
}
func ExampleSortedTArray_Rands() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}, gutil.ComparatorT)
fmt.Println(s)
fmt.Println(s.Rands(3))
// May Output:
// ["a","b","c","d","e","f","g","h"]
// [h g c]
}
func ExampleSortedTArray_Join() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}, gutil.ComparatorT)
fmt.Println(s.Join(","))
// Output:
// a,b,c,d,e,f,g,h
}
func ExampleSortedTArray_CountValues() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"}, gutil.ComparatorT)
fmt.Println(s.CountValues())
// Output:
// map[a:1 b:1 c:3 d:2]
}
func ExampleSortedTArray_Iterator() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s.Iterator(func(k int, v string) bool {
fmt.Println(k, v)
return true
})
// Output:
// 0 a
// 1 b
// 2 c
}
func ExampleSortedTArray_IteratorAsc() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s.IteratorAsc(func(k int, v string) bool {
fmt.Println(k, v)
return true
})
// Output:
// 0 a
// 1 b
// 2 c
}
func ExampleSortedTArray_IteratorDesc() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s.IteratorDesc(func(k int, v string) bool {
fmt.Println(k, v)
return true
})
// Output:
// 2 c
// 1 b
// 0 a
}
func ExampleSortedTArray_String() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
fmt.Println(s.String())
// Output:
// ["a","b","c"]
}
func ExampleSortedTArray_MarshalJSON() {
type Student struct {
ID int
Name string
Levels garray.SortedTArray[string]
}
r := garray.NewSortedTArrayFrom(g.SliceStr{"b", "c", "a"}, gutil.ComparatorT)
s := Student{
ID: 1,
Name: "john",
Levels: *r,
}
b, _ := json.Marshal(s)
fmt.Println(string(b))
// Output:
// {"ID":1,"Name":"john","Levels":["a","b","c"]}
}
func ExampleSortedTArray_UnmarshalJSON() {
b := []byte(`{"Id":1,"Name":"john","Lessons":["Math","English","Sport"]}`)
type Student struct {
Id int
Name string
Lessons *garray.StrArray
}
s := Student{}
json.Unmarshal(b, &s)
fmt.Println(s)
// Output:
// {1 john ["Math","English","Sport"]}
}
func ExampleSortedTArray_UnmarshalValue() {
type Student struct {
Name string
Lessons *garray.StrArray
}
var s *Student
gconv.Struct(g.Map{
"name": "john",
"lessons": []byte(`["Math","English","Sport"]`),
}, &s)
fmt.Println(s)
var s1 *Student
gconv.Struct(g.Map{
"name": "john",
"lessons": g.SliceStr{"Math", "English", "Sport"},
}, &s1)
fmt.Println(s1)
// Output:
// &{john ["Math","English","Sport"]}
// &{john ["Math","English","Sport"]}
}
func ExampleSortedTArray_Filter() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"}, gutil.ComparatorT)
fmt.Println(s)
fmt.Println(s.Filter(func(index int, value string) bool {
return empty.IsEmpty(value)
}))
// Output:
// ["","","","a","b","c","d"]
// ["a","b","c","d"]
}
func ExampleSortedTArray_FilterEmpty() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"}, gutil.ComparatorT)
fmt.Println(s)
fmt.Println(s.FilterEmpty())
// Output:
// ["","","","a","b","c","d"]
// ["a","b","c","d"]
}
func ExampleSortedTArray_IsEmpty() {
s := garray.NewSortedTArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"}, gutil.ComparatorT)
fmt.Println(s.IsEmpty())
s1 := garray.NewSortedTArray[string](gutil.ComparatorT)
fmt.Println(s1.IsEmpty())
// Output:
// false
// true
}

View File

@ -131,7 +131,7 @@ func Test_SortedStrArray2(t *testing.T) {
func Test_SortedArray1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
array := garray.NewSortedArray(func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 10; i > -1; i-- {
@ -144,7 +144,7 @@ func Test_SortedArray1(t *testing.T) {
func Test_SortedArray2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array := garray.NewSortedArray(func1)
@ -161,7 +161,7 @@ func Test_SortedArray2(t *testing.T) {
func TestNewFromCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
a1 := []any{"100", "200", "300", "400", "500", "600"}
array1 := garray.NewFromCopy(a1)
t.AssertIN(array1.PopRands(2), a1)
t.Assert(len(array1.PopRands(1)), 1)

View File

@ -22,10 +22,10 @@ import (
func Test_Array_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{0, 1, 2, 3}
expect := []any{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
array2 := garray.NewArrayFrom(expect)
array3 := garray.NewArrayFrom([]interface{}{})
array3 := garray.NewArrayFrom([]any{})
array4 := garray.NewArrayRange(1, 5, 1)
t.Assert(array.Slice(), expect)
@ -86,10 +86,10 @@ func Test_Array_Basic(t *testing.T) {
t.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
t.Assert(array.Slice(), []any{100, 200, 2, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
t.Assert(array.Slice(), []any{100, 200, 2, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
err = array.InsertBefore(99, 9900)
t.AssertNE(err, nil)
@ -102,17 +102,17 @@ func Test_Array_Basic(t *testing.T) {
func TestArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect1 := []interface{}{0, 1, 2, 3}
expect2 := []interface{}{3, 2, 1, 0}
expect1 := []any{0, 1, 2, 3}
expect2 := []any{3, 2, 1, 0}
array := garray.NewArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 interface{}) bool {
array.SortFunc(func(v1, v2 any) bool {
return v1.(int) < v2.(int)
})
t.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 interface{}) bool {
array.SortFunc(func(v1, v2 any) bool {
return v1.(int) > v2.(int)
})
t.Assert(array.Slice(), expect2)
@ -121,20 +121,20 @@ func TestArray_Sort(t *testing.T) {
func TestArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
expect := []any{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
t.Assert(array.Unique().Slice(), []any{1, 2, 3, 4, 5})
})
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{}
expect := []any{}
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{})
t.Assert(array.Unique().Slice(), []any{})
})
}
func TestArray_PushAndPop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{0, 1, 2, 3}
expect := []any{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
t.Assert(array.Slice(), expect)
@ -147,24 +147,24 @@ func TestArray_PushAndPop(t *testing.T) {
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.AssertIN(v, []any{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.AssertIN(v, []any{1, 2})
t.Assert(ok, true)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
t.Assert(array.Slice(), []interface{}{1, 2})
t.Assert(array.Slice(), []any{1, 2})
})
}
func TestArray_PopRands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{100, 200, 300, 400, 500, 600}
a1 := []any{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
t.AssertIN(array.PopRands(2), []interface{}{100, 200, 300, 400, 500, 600})
t.AssertIN(array.PopRands(2), []any{100, 200, 300, 400, 500, 600})
})
}
@ -247,56 +247,56 @@ func TestArray_PopLeftsAndPopRights(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
value2 := []interface{}{0, 1, 2, 3, 4, 5, 6}
value1 := []any{0, 1, 2, 3, 4, 5, 6}
value2 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value2)
t.Assert(array1.PopLefts(2), []interface{}{0, 1})
t.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []interface{}{5, 6})
t.Assert(array1.Slice(), []interface{}{2, 3, 4})
t.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
t.Assert(array1.Slice(), []interface{}{})
t.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []interface{}{})
t.Assert(array1.PopLefts(2), []any{0, 1})
t.Assert(array1.Slice(), []any{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []any{5, 6})
t.Assert(array1.Slice(), []any{2, 3, 4})
t.Assert(array1.PopRights(20), []any{2, 3, 4})
t.Assert(array1.Slice(), []any{})
t.Assert(array2.PopLefts(20), []any{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []any{})
})
}
func TestArray_Range(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
value1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value1, true)
t.Assert(array1.Range(0, 1), []interface{}{0})
t.Assert(array1.Range(1, 2), []interface{}{1})
t.Assert(array1.Range(0, 2), []interface{}{0, 1})
t.Assert(array1.Range(0, 1), []any{0})
t.Assert(array1.Range(1, 2), []any{1})
t.Assert(array1.Range(0, 2), []any{0, 1})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 2), nil)
t.Assert(array2.Range(1, 3), []interface{}{1, 2})
t.Assert(array2.Range(1, 3), []any{1, 2})
})
}
func TestArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
return 1
}
i1 := []interface{}{0, 1, 2, 3}
i2 := []interface{}{4, 5, 6, 7}
i1 := []any{0, 1, 2, 3}
i2 := []any{4, 5, 6, 7}
array1 := garray.NewArrayFrom(i1)
array2 := garray.NewArrayFrom(i2)
t.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
t.Assert(array1.Merge(array2).Slice(), []any{0, 1, 2, 3, 4, 5, 6, 7})
// s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i3 := garray.NewIntArrayFrom([]int{1, 2, 3})
i4 := garray.NewArrayFrom([]interface{}{3})
i4 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewArrayFrom(i1)
@ -313,92 +313,92 @@ func TestArray_Merge(t *testing.T) {
func TestArray_Fill(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0}
a2 := []interface{}{0}
a1 := []any{0}
a2 := []any{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2, true)
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []interface{}{0, 100, 100})
t.Assert(array1.Slice(), []any{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
t.Assert(array2.Slice(), []any{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
t.Assert(array2.Slice(), []any{100, 100})
})
}
func TestArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5})
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5})
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5, 6})
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5, 6})
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestArray_Pad(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0}
a1 := []any{0}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []any{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []any{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []any{1, 0, 1, 1})
})
}
func TestArray_SubSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a1, true)
t.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
t.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
t.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
t.Assert(array1.SubSlice(0, 2), []any{0, 1})
t.Assert(array1.SubSlice(2, 2), []any{2, 3})
t.Assert(array1.SubSlice(5, 8), []any{5, 6})
t.Assert(array1.SubSlice(9, 1), nil)
t.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
t.Assert(array1.SubSlice(-2, 2), []any{5, 6})
t.Assert(array1.SubSlice(-9, 2), nil)
t.Assert(array1.SubSlice(1, -2), nil)
t.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
t.Assert(array2.SubSlice(0, 2), []any{0, 1})
})
}
func TestArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 10)
@ -406,7 +406,7 @@ func TestArray_Rand(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1)
i1, ok := a1.Rand()
t.Assert(ok, true)
@ -415,7 +415,7 @@ func TestArray_Rand(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
a1 := []any{}
array1 := garray.NewArrayFrom(a1)
rand, found := array1.Rand()
t.AssertNil(rand)
@ -423,7 +423,7 @@ func TestArray_Rand(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
a1 := []any{}
array1 := garray.NewArrayFrom(a1)
rand := array1.Rands(1)
t.AssertNil(rand)
@ -432,7 +432,7 @@ func TestArray_Rand(t *testing.T) {
func TestArray_Shuffle(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Shuffle().Len(), 7)
})
@ -440,27 +440,27 @@ func TestArray_Shuffle(t *testing.T) {
func TestArray_Reverse(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
t.Assert(array1.Reverse().Slice(), []any{6, 5, 4, 3, 2, 1, 0})
})
}
func TestArray_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, `"a"`, `\a`}
a1 := []any{0, 1, `"a"`, `\a`}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
a1 := []any{}
array1 := garray.NewArrayFrom(a1)
t.Assert(len(array1.Join(".")), 0)
})
@ -468,7 +468,7 @@ func TestArray_Join(t *testing.T) {
func TestArray_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
array1 = nil
@ -478,9 +478,9 @@ func TestArray_String(t *testing.T) {
func TestArray_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"m", "n", "p", "z", "x", "y", "d", "u"}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
a3 := []any{"m", "n", "p", "z", "x", "y", "d", "u"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Replace(a2)
t.Assert(array2.Len(), 7)
@ -497,8 +497,8 @@ func TestArray_Replace(t *testing.T) {
func TestArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
array1 := garray.NewArrayFrom(a1)
array1 = array1.SetArray(a2)
@ -510,9 +510,9 @@ func TestArray_SetArray(t *testing.T) {
func TestArray_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"a", "1", "2"}
a1 := []any{0, 1, 2, 3}
a2 := []any{"a", "b", "c"}
a3 := []any{"a", "1", "2"}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
@ -527,7 +527,7 @@ func TestArray_Sum(t *testing.T) {
func TestArray_Clone(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3}
a1 := []any{0, 1, 2, 3}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Clone()
@ -540,7 +540,7 @@ func TestArray_Clone(t *testing.T) {
func TestArray_CountValues(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "b", "c", "d", "e", "d"}
a1 := []any{"a", "b", "c", "d", "e", "d"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.CountValues()
t.Assert(len(array2), 5)
@ -551,13 +551,13 @@ func TestArray_CountValues(t *testing.T) {
func TestArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.LockFunc(func(n1 []interface{}) { // 读写锁
go a1.LockFunc(func(n1 []any) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
@ -583,13 +583,13 @@ func TestArray_LockFunc(t *testing.T) {
func TestArray_RLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 1)
// go1
go a1.RLockFunc(func(n1 []interface{}) { // 读锁
go a1.RLockFunc(func(n1 []any) { // 读锁
time.Sleep(2 * time.Second) // 暂停1秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
@ -616,7 +616,7 @@ func TestArray_RLockFunc(t *testing.T) {
func TestArray_Json(t *testing.T) {
// pointer
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s1 := []any{"a", "b", "d", "c"}
a1 := garray.NewArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
@ -635,7 +635,7 @@ func TestArray_Json(t *testing.T) {
})
// value.
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s1 := []any{"a", "b", "d", "c"}
a1 := *garray.NewArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
@ -696,26 +696,26 @@ func TestArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v interface{}) bool {
array.Iterator(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v interface{}) bool {
array.IteratorAsc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v interface{}) bool {
array.IteratorDesc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v interface{}) bool {
array.Iterator(func(k int, v any) bool {
index++
return false
})
@ -723,7 +723,7 @@ func TestArray_Iterator(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
array.IteratorAsc(func(k int, v any) bool {
index++
return false
})
@ -731,7 +731,7 @@ func TestArray_Iterator(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
array.IteratorDesc(func(k int, v any) bool {
index++
return false
})
@ -805,27 +805,27 @@ func TestArray_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewArrayFromCopy(values)
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}).Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
@ -845,7 +845,7 @@ func TestArray_FilterEmpty(t *testing.T) {
func TestArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{"1", "2"})
t.Assert(array.Walk(func(value interface{}) interface{} {
t.Assert(array.Walk(func(value any) any {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})

View File

@ -195,7 +195,7 @@ func TestIntArray_Range(t *testing.T) {
func TestIntArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
@ -204,7 +204,7 @@ func TestIntArray_Merge(t *testing.T) {
n1 := []int{0, 1, 2, 3}
n2 := []int{4, 5, 6, 7}
i1 := []interface{}{"1", "2"}
i1 := []any{"1", "2"}
s1 := []string{"a", "b", "c"}
s2 := []string{"e", "f"}
a1 := garray.NewIntArrayFrom(n1)
@ -216,7 +216,7 @@ func TestIntArray_Merge(t *testing.T) {
a6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a7 := garray.NewSortedStrArrayFrom(s1)
a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
a8 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
t.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
t.Assert(a1.Merge(a3).Len(), 10)

View File

@ -235,14 +235,14 @@ func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
value2 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(value1)
array2 := garray.NewStrArrayFrom(value2)
t.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
t.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
t.Assert(array1.PopRights(2), []interface{}{"5", "6"})
t.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
t.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
t.Assert(array1.Slice(), []interface{}{})
t.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
t.Assert(array2.Slice(), []interface{}{})
t.Assert(array1.PopLefts(2), []any{"0", "1"})
t.Assert(array1.Slice(), []any{"2", "3", "4", "5", "6"})
t.Assert(array1.PopRights(2), []any{"5", "6"})
t.Assert(array1.Slice(), []any{"2", "3", "4"})
t.Assert(array1.PopRights(20), []any{"2", "3", "4"})
t.Assert(array1.Slice(), []any{})
t.Assert(array2.PopLefts(20), []any{"0", "1", "2", "3", "4", "5", "6"})
t.Assert(array2.Slice(), []any{})
})
}
@ -251,12 +251,12 @@ func TestString_Range(t *testing.T) {
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(value1)
array2 := garray.NewStrArrayFrom(value1, true)
t.Assert(array1.Range(0, 1), []interface{}{"0"})
t.Assert(array1.Range(1, 2), []interface{}{"1"})
t.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
t.Assert(array1.Range(0, 1), []any{"0"})
t.Assert(array1.Range(1, 2), []any{"1"})
t.Assert(array1.Range(0, 2), []any{"0", "1"})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 1), nil)
t.Assert(array2.Range(0, 1), []interface{}{"0"})
t.Assert(array2.Range(0, 1), []any{"0"})
})
}
@ -268,7 +268,7 @@ func TestStrArray_Merge(t *testing.T) {
array2 := garray.NewStrArrayFrom(a21)
t.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
@ -278,9 +278,9 @@ func TestStrArray_Merge(t *testing.T) {
s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
i2 := garray.NewArrayFrom([]interface{}{3})
i2 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewStrArrayFrom(s1)

View File

@ -0,0 +1,851 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"testing"
"time"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gconv"
)
func Test_TArray_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3}
array := garray.NewTArrayFrom(expect)
array2 := garray.NewTArrayFrom(expect)
array3 := garray.NewTArrayFrom([]int{})
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
err := array.Set(0, 100) // 100, 1, 2, 3
t.AssertNil(err)
err = array.Set(100, 100)
t.AssertNE(err, nil)
t.Assert(array.IsEmpty(), false)
copyArray := array.DeepCopy()
ca := copyArray.(*garray.TArray[int])
ca.Set(0, 1)
cval, _ := ca.Get(0)
val, _ := array.Get(0)
t.AssertNE(cval, val)
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)
v, ok = array.Get(4)
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Search(100), 0)
t.Assert(array3.Search(100), -1)
t.Assert(array.Contains(100), true)
v, ok = array.Remove(0) // 1, 2, 3
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)
v, ok = array2.Remove(3) // 0 1 2
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array2.Remove(1) // 0 2
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Contains(100), false)
array.Append(4) // 1, 2, 3 ,4
t.Assert(array.Len(), 4)
array.InsertBefore(0, 100) // 100, 1, 2, 3, 4
array.InsertAfter(0, 200) // 100, 200, 1, 2, 3, 4
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
err = array.InsertBefore(99, 9900)
t.AssertNE(err, nil)
err = array.InsertAfter(99, 9900)
t.AssertNE(err, nil)
t.Assert(array.String(), "[]")
})
}
func TestTArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect1 := []any{0, 1, 2, 3}
expect2 := []any{3, 2, 1, 0}
array := garray.NewTArray[int]()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 int) bool {
return v1 < v2
})
t.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 int) bool {
return v1 > v2
})
t.Assert(array.Slice(), expect2)
})
}
func TestTArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
})
gtest.C(t, func(t *gtest.T) {
expect := []int{}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{})
})
}
func TestTArray_PushAndPop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []any{0, 1, 2, 3}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Slice(), expect)
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, []any{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []any{1, 2})
t.Assert(ok, true)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
t.Assert(array.Slice(), []any{1, 2})
})
}
func TestTArray_PopRands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
t.AssertIN(array.PopRands(2), []any{100, 200, 300, 400, 500, 600})
})
}
func TestTArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopLefts(2), g.Slice{1, 2})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{3})
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopRights(2), g.Slice{2, 3})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{1})
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
gtest.C(t, func(t *gtest.T) {
value1 := []any{0, 1, 2, 3, 4, 5, 6}
value2 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(value1)
array2 := garray.NewTArrayFrom(value2)
t.Assert(array1.PopLefts(2), []any{0, 1})
t.Assert(array1.Slice(), []any{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []any{5, 6})
t.Assert(array1.Slice(), []any{2, 3, 4})
t.Assert(array1.PopRights(20), []any{2, 3, 4})
t.Assert(array1.Slice(), []any{})
t.Assert(array2.PopLefts(20), []any{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []any{})
})
}
func TestTArray_Range(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
value1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(value1)
array2 := garray.NewTArrayFrom(value1, true)
t.Assert(array1.Range(0, 1), []any{0})
t.Assert(array1.Range(1, 2), []any{1})
t.Assert(array1.Range(0, 2), []any{0, 1})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 2), nil)
t.Assert(array2.Range(1, 3), []any{1, 2})
})
}
func TestTArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
return 1
}
i1 := []any{0, 1, 2, 3}
i2 := []any{4, 5, 6, 7}
array1 := garray.NewTArrayFrom(i1)
array2 := garray.NewTArrayFrom(i2)
t.Assert(array1.Merge(array2).Slice(), []any{0, 1, 2, 3, 4, 5, 6, 7})
// s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i3 := garray.NewIntArrayFrom([]int{1, 2, 3})
i4 := garray.NewTArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewTArrayFrom(i1)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i3).Len(), 9)
t.Assert(a1.Merge(i4).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestTArray_Fill(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0}
a2 := []any{0}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a2, true)
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []any{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []any{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []any{100, 100})
})
}
func TestTArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestTArray_Pad(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Pad(3, 1).Slice(), []any{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []any{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []any{1, 0, 1, 1})
})
}
func TestTArray_SubSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a1, true)
t.Assert(array1.SubSlice(0, 2), []any{0, 1})
t.Assert(array1.SubSlice(2, 2), []any{2, 3})
t.Assert(array1.SubSlice(5, 8), []any{5, 6})
t.Assert(array1.SubSlice(9, 1), nil)
t.Assert(array1.SubSlice(-2, 2), []any{5, 6})
t.Assert(array1.SubSlice(-9, 2), nil)
t.Assert(array1.SubSlice(1, -2), nil)
t.Assert(array2.SubSlice(0, 2), []any{0, 1})
})
}
func TestTArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
})
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1)
i1, ok := a1.Rand()
t.Assert(ok, true)
t.Assert(a1.Contains(i1), true)
t.Assert(a1.Len(), 4)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
rand, found := array1.Rand()
t.AssertNil(rand)
t.Assert(found, false)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
rand := array1.Rands(1)
t.AssertNil(rand)
})
}
func TestTArray_Shuffle(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Shuffle().Len(), 7)
})
}
func TestTArray_Reverse(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Reverse().Slice(), []any{6, 5, 4, 3, 2, 1, 0})
})
}
func TestTArray_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, `"a"`, `\a`}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
t.Assert(len(array1.Join(".")), 0)
})
}
func TestTArray_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
func TestTArray_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
a3 := []any{"m", "n", "p", "z", "x", "y", "d", "u"}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.Replace(a2)
t.Assert(array2.Len(), 7)
t.Assert(array2.Contains("b"), true)
t.Assert(array2.Contains(4), true)
t.Assert(array2.Contains("v"), false)
array3 := array1.Replace(a3)
t.Assert(array3.Len(), 7)
t.Assert(array3.Contains(4), false)
t.Assert(array3.Contains("p"), true)
t.Assert(array3.Contains("u"), false)
})
}
func TestTArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
array1 := garray.NewTArrayFrom(a1)
array1 = array1.SetArray(a2)
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("b"), true)
t.Assert(array1.Contains("5"), false)
})
}
func TestTArray_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3}
a2 := []any{"a", "b", "c"}
a3 := []any{"a", "1", "2"}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a2)
array3 := garray.NewTArrayFrom(a3)
t.Assert(array1.Sum(), 6)
t.Assert(array2.Sum(), 0)
t.Assert(array3.Sum(), 3)
})
}
func TestTArray_Clone(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.Clone()
t.Assert(array1.Len(), 4)
t.Assert(array2.Sum(), 6)
t.AssertEQ(array1, array2)
})
}
func TestTArray_CountValues(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{"a", "b", "c", "d", "e", "d"}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.CountValues()
t.Assert(len(array2), 5)
t.Assert(array2["b"], 1)
t.Assert(array2["d"], 2)
})
}
func TestTArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.LockFunc(func(n1 []any) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestTArray_RLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 1)
// go1
go a1.RLockFunc(func(n1 []any) { // 读锁
time.Sleep(2 * time.Second) // 暂停1秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertLT(t2-t1, 20) // go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains("g"), false)
})
}
func TestTArray_Json(t *testing.T) {
// pointer
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "d", "c"}
a1 := garray.NewTArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.UnmarshalUseNumber(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
})
// value.
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "d", "c"}
a1 := *garray.NewTArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.UnmarshalUseNumber(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
})
// pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores *garray.Array
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.Array
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
}
func TestTArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
}
func TestTArray_RemoveValue(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestTArray_RemoveValues(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
array.RemoveValues("a", "b", "c")
t.Assert(array.Slice(), g.Slice{"d"})
})
}
func TestTArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.Array
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[1,2,3]`),
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{1, 2, 3},
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestTArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewTArrayFromCopy(values)
t.Assert(array.FilterNil().Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewTArrayFromCopy(values)
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}).Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{1, 2, 3, 4})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{1, 2, 3, 4})
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{"1", "2"})
t.Assert(array.Walk(func(value any) any {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -24,91 +24,91 @@ import (
func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"h", "j", "i", "k"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "f", "c"}
a2 := []any{"h", "j", "i", "k"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
func2 := func(v1, v2 interface{}) int {
func2 := func(v1, v2 any) int {
return -1
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a2, func2)
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1, []any{"a", "c", "f"})
t.Assert(array2.Len(), 4)
t.Assert(array2, []interface{}{"k", "i", "j", "h"})
t.Assert(array2, []any{"k", "i", "j", "h"})
})
}
func TestNewSortedArrayFromCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
a1 := []any{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
func2 := func(v1, v2 interface{}) int {
func2 := func(v1, v2 any) int {
return -1
}
array1 := garray.NewSortedArrayFromCopy(a1, func1)
array2 := garray.NewSortedArrayFromCopy(a1, func2)
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1, []any{"a", "c", "f"})
t.Assert(array1.Len(), 3)
t.Assert(array2, []interface{}{"c", "f", "a"})
t.Assert(array2, []any{"c", "f", "a"})
})
}
func TestNewSortedArrayRange(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return gconv.Int(v1) - gconv.Int(v2)
}
array1 := garray.NewSortedArrayRange(1, 5, 1, func1)
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
t.Assert(array1, []any{1, 2, 3, 4, 5})
})
}
func TestSortedArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"e", "h", "g", "k"}
a1 := []any{"a", "f", "c"}
a2 := []any{"e", "h", "g", "k"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.SetArray(a2)
t.Assert(array1.Len(), 4)
t.Assert(array1, []interface{}{"e", "g", "h", "k"})
t.Assert(array1, []any{"e", "g", "h", "k"})
})
}
func TestSortedArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "f", "c"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.Sort()
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1, []any{"a", "c", "f"})
})
}
func TestSortedArray_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "f", "c"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -129,8 +129,8 @@ func TestSortedArray_Get(t *testing.T) {
func TestSortedArray_At(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "f", "c"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -141,8 +141,8 @@ func TestSortedArray_At(t *testing.T) {
func TestSortedArray_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -178,14 +178,14 @@ func TestSortedArray_Remove(t *testing.T) {
func TestSortedArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedArrayFrom(
[]interface{}{"a", "d", "c", "b"},
[]any{"a", "d", "c", "b"},
gutil.ComparatorString,
)
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"})
t.Assert(array1, []any{"b", "c", "d"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
@ -207,14 +207,14 @@ func TestSortedArray_PopLeft(t *testing.T) {
func TestSortedArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedArrayFrom(
[]interface{}{"a", "d", "c", "b"},
[]any{"a", "d", "c", "b"},
gutil.ComparatorString,
)
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"})
t.Assert(array1, []any{"a", "b", "c"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
@ -237,14 +237,14 @@ func TestSortedArray_PopRight(t *testing.T) {
func TestSortedArray_PopRand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
t.AssertIN(i1, []any{"a", "d", "c", "b"})
t.Assert(array1.Len(), 3)
})
@ -252,19 +252,19 @@ func TestSortedArray_PopRand(t *testing.T) {
func TestSortedArray_PopRands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRands(2)
t.Assert(len(i1), 2)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
t.AssertIN(i1, []any{"a", "d", "c", "b"})
t.Assert(array1.Len(), 2)
i2 := array1.PopRands(3)
t.Assert(len(i1), 2)
t.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
t.AssertIN(i2, []any{"a", "d", "c", "b"})
t.Assert(array1.Len(), 0)
})
@ -292,33 +292,33 @@ func TestSortedArray_Empty(t *testing.T) {
func TestSortedArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopLefts(2)
t.Assert(len(i1), 2)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.AssertIN(i1, []any{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopLefts(5)
t.Assert(len(i2), 4)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.AssertIN(i1, []any{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRights(2)
t.Assert(len(i1), 2)
t.Assert(i1, []interface{}{"e", "f"})
t.Assert(i1, []any{"e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopRights(10)
@ -329,36 +329,36 @@ func TestSortedArray_PopRights(t *testing.T) {
func TestSortedArray_Range(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a1, func1, true)
i1 := array1.Range(2, 5)
t.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(i1, []any{"c", "d", "e"})
t.Assert(array1.Len(), 6)
i2 := array1.Range(7, 5)
t.Assert(len(i2), 0)
i2 = array1.Range(-1, 2)
t.Assert(i2, []interface{}{"a", "b"})
t.Assert(i2, []any{"a", "b"})
i2 = array1.Range(4, 10)
t.Assert(len(i2), 2)
t.Assert(i2, []interface{}{"e", "f"})
t.Assert(i2, []any{"e", "f"})
t.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
t.Assert(array2.Range(1, 3), []any{"b", "c"})
})
}
func TestSortedArray_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
a2 := []interface{}{"1", "2", "3", "b", "e", "f"}
a3 := []interface{}{"4", "5", "6"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c", "b", "e", "f"}
a2 := []any{"1", "2", "3", "b", "e", "f"}
a3 := []any{"4", "5", "6"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -373,9 +373,9 @@ func TestSortedArray_Sum(t *testing.T) {
func TestSortedArray_Clone(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
a1 := []any{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -389,9 +389,9 @@ func TestSortedArray_Clone(t *testing.T) {
func TestSortedArray_Clear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
a1 := []any{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -404,66 +404,66 @@ func TestSortedArray_Clear(t *testing.T) {
func TestSortedArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e"}
a1 := []any{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Chunk(2)
t.Assert(len(i1), 3)
t.Assert(i1[0], []interface{}{"a", "b"})
t.Assert(i1[2], []interface{}{"e"})
t.Assert(i1[0], []any{"a", "b"})
t.Assert(i1[2], []any{"e"})
i1 = array1.Chunk(0)
t.Assert(len(i1), 0)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5})
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5, 6})
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5, 6})
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestSortedArray_SubSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e"}
a1 := []any{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a1, func1, true)
i1 := array1.SubSlice(2, 3)
t.Assert(len(i1), 3)
t.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(i1, []any{"c", "d", "e"})
i1 = array1.SubSlice(2, 6)
t.Assert(len(i1), 3)
t.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(i1, []any{"c", "d", "e"})
i1 = array1.SubSlice(7, 2)
t.Assert(len(i1), 0)
@ -473,25 +473,25 @@ func TestSortedArray_SubSlice(t *testing.T) {
s1 = array1.SubSlice(-9, 2)
t.Assert(s1, nil)
t.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
t.Assert(array2.SubSlice(1, 3), []any{"b", "c", "d"})
})
}
func TestSortedArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
a1 := []any{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.AssertIN(i1, []any{"a", "d", "c"})
t.Assert(array1.Len(), 3)
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
array2 := garray.NewSortedArrayFrom([]any{}, func1)
v, ok := array2.Rand()
t.Assert(ok, false)
t.Assert(v, nil)
@ -500,21 +500,21 @@ func TestSortedArray_Rand(t *testing.T) {
func TestSortedArray_Rands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
a1 := []any{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rands(2)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.AssertIN(i1, []any{"a", "d", "c"})
t.Assert(len(i1), 2)
t.Assert(array1.Len(), 3)
i1 = array1.Rands(4)
t.Assert(len(i1), 4)
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
array2 := garray.NewSortedArrayFrom([]any{}, func1)
v := array2.Rands(1)
t.Assert(v, nil)
})
@ -522,8 +522,8 @@ func TestSortedArray_Rands(t *testing.T) {
func TestSortedArray_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
a1 := []any{"a", "d", "c"}
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -532,13 +532,13 @@ func TestSortedArray_Join(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, `"a"`, `\a`}
a1 := []any{0, 1, `"a"`, `\a`}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.Join("."), `"a".0.1.\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
a1 := []any{}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.Join("."), "")
})
@ -546,7 +546,7 @@ func TestSortedArray_Join(t *testing.T) {
func TestSortedArray_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, "a", "b"}
a1 := []any{0, 1, "a", "b"}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.String(), `[0,1,"a","b"]`)
@ -557,9 +557,9 @@ func TestSortedArray_String(t *testing.T) {
func TestSortedArray_CountValues(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "c"}
a1 := []any{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
@ -573,41 +573,41 @@ func TestSortedArray_CountValues(t *testing.T) {
func TestSortedArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
a1 := []any{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.SetUnique(true)
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
t.Assert(array1, []any{1, 2, 3, 4, 5})
})
}
func TestSortedArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
a1 := []any{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
t.Assert(array1, []any{1, 2, 3, 4, 5})
array2 := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorInt)
array2 := garray.NewSortedArrayFrom([]any{}, gutil.ComparatorInt)
array2.Unique()
t.Assert(array2.Len(), 0)
t.Assert(array2, []interface{}{})
t.Assert(array2, []any{})
})
}
func TestSortedArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewSortedArrayFrom(s1, func1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.LockFunc(func(n1 []interface{}) { // 读写锁
go a1.LockFunc(func(n1 []any) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
@ -633,16 +633,16 @@ func TestSortedArray_LockFunc(t *testing.T) {
func TestSortedArray_RLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewSortedArrayFrom(s1, func1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.RLockFunc(func(n1 []interface{}) { // 读写锁
go a1.RLockFunc(func(n1 []any) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
@ -668,19 +668,19 @@ func TestSortedArray_RLockFunc(t *testing.T) {
func TestSortedArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
return 1
}
s1 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
i2 := garray.NewArrayFrom([]interface{}{3})
i2 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
@ -699,8 +699,8 @@ func TestSortedArray_Merge(t *testing.T) {
func TestSortedArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s2 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "d", "c"}
s2 := []any{"a", "b", "c", "d"}
a1 := garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
@ -720,8 +720,8 @@ func TestSortedArray_Json(t *testing.T) {
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s2 := []interface{}{"a", "b", "c", "d"}
s1 := []any{"a", "b", "d", "c"}
s2 := []any{"a", "b", "c", "d"}
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
@ -817,26 +817,26 @@ func TestSortedArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v interface{}) bool {
array.Iterator(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v interface{}) bool {
array.IteratorAsc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v interface{}) bool {
array.IteratorDesc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v interface{}) bool {
array.Iterator(func(k int, v any) bool {
index++
return false
})
@ -844,7 +844,7 @@ func TestSortedArray_Iterator(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
array.IteratorAsc(func(k int, v any) bool {
index++
return false
})
@ -852,7 +852,7 @@ func TestSortedArray_Iterator(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
array.IteratorDesc(func(k int, v any) bool {
index++
return false
})
@ -913,25 +913,25 @@ func TestSortedArray_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}).Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
t.Assert(array.Filter(func(index int, value interface{}) bool {
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
@ -939,31 +939,36 @@ func TestSortedArray_Filter(t *testing.T) {
func TestSortedArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
values := g.Slice{0, 1, 2, 3, 4, "", nil, g.Slice{}}
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
t.Assert(array.FilterNil().Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, nil, 3, 4, nil}, gutil.ComparatorInt)
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestSortedArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 0, -1, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
t.Assert(array.FilterEmpty(), g.Slice{-1, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, -1, -2, nil, []any{}, ""}
array := garray.NewSortedArrayFrom(values, gutil.ComparatorString)
t.Assert(array.FilterEmpty().Slice(), g.Slice{-1, -2, 1, 2, 3, 4})
})
}
func TestSortedArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{"1", "2"}, gutil.ComparatorString)
t.Assert(array.Walk(func(value interface{}) interface{} {
t.Assert(array.Walk(func(value any) any {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
@ -971,14 +976,14 @@ func TestSortedArray_Walk(t *testing.T) {
func TestSortedArray_IsEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorString)
array := garray.NewSortedArrayFrom([]any{}, gutil.ComparatorString)
t.Assert(array.IsEmpty(), true)
})
}
func TestSortedArray_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom([]interface{}{1, 2, 3, 4, 5}, gutil.ComparatorString)
array := garray.NewSortedArrayFrom([]any{1, 2, 3, 4, 5}, gutil.ComparatorString)
copyArray := array.DeepCopy().(*garray.SortedArray)
array.Add(6)
copyArray.Add(7)

View File

@ -573,7 +573,7 @@ func TestSortedIntArray_RLockFunc(t *testing.T) {
func TestSortedIntArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
@ -582,9 +582,9 @@ func TestSortedIntArray_Merge(t *testing.T) {
i0 := []int{1, 2, 3, 4}
s2 := []string{"e", "f"}
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
i2 := garray.NewArrayFrom([]interface{}{3})
i2 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewSortedIntArrayFrom(i0)
@ -794,8 +794,22 @@ func TestSortedIntArray_Filter(t *testing.T) {
func TestSortedIntArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, -1, 2, 3, 4, 0})
t.Assert(array.FilterEmpty(), g.SliceInt{-1, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 0, 0, 0, 0, 0, 1})
array.SetComparator(func(a, b int) int {
if a == b {
return 0
}
if a < b {
return 1
} else {
return -1
}
})
t.Assert(array.FilterEmpty(), g.SliceInt{1})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})

View File

@ -581,7 +581,7 @@ func TestSortedStrArray_RLockFunc(t *testing.T) {
func TestSortedStrArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
@ -591,9 +591,9 @@ func TestSortedStrArray_Merge(t *testing.T) {
s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
i2 := garray.NewArrayFrom([]interface{}{3})
i2 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewSortedStrArrayFrom(s1)
@ -806,9 +806,23 @@ func TestSortedStrArray_Filter(t *testing.T) {
func TestSortedStrArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "", "2", "0", ""})
t.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "", "", "2", "0", "a", "b"})
array.SetComparator(func(a, b string) int {
if a == b {
return 0
}
if a < b {
return 1
} else {
return -1
}
})
t.Assert(array.FilterEmpty(), g.SliceStr{"b", "a", "2", "0"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})

View File

@ -0,0 +1,924 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"strings"
"testing"
"time"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
func TestSortedTArray_NewSortedTArrayFrom(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
a2 := []string{"h", "j", "i", "k"}
func1 := func(v1, v2 string) int {
return strings.Compare(v1, v2)
}
func2 := func(v1, v2 string) int {
return -1
}
array1 := garray.NewSortedTArrayFrom(a1, func1)
array2 := garray.NewSortedTArrayFrom(a2, func2)
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"a", "c", "f"})
t.Assert(array2.Len(), 4)
t.Assert(array2, []string{"k", "i", "j", "h"})
})
}
func TestNewSortedTArrayFromCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
func1 := func(v1, v2 string) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
func2 := func(v1, v2 string) int {
return -1
}
array1 := garray.NewSortedTArrayFromCopy(a1, func1)
array2 := garray.NewSortedTArrayFromCopy(a1, func2)
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"a", "c", "f"})
t.Assert(array1.Len(), 3)
t.Assert(array2, []string{"c", "f", "a"})
})
}
func TestSortedTArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
a2 := []string{"e", "h", "g", "k"}
func1 := func(v1, v2 string) int {
return strings.Compare(v1, v2)
}
array1 := garray.NewSortedTArrayFrom(a1, func1)
array1.SetArray(a2)
t.Assert(array1.Len(), 4)
t.Assert(array1, []string{"e", "g", "h", "k"})
})
}
func TestSortedTArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
array1.Sort()
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"a", "c", "f"})
})
}
func TestSortedTArray_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
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)
v, ok = array1.Get(99)
t.Assert(v, nil)
t.Assert(ok, false)
})
}
func TestSortedTArray_At(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "f", "c"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
v := array1.At(2)
t.Assert(v, "f")
})
}
func TestSortedTArray_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
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)
v, ok := array1.Remove(-1)
t.Assert(v, nil)
t.Assert(ok, false)
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, 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)
})
}
func TestSortedTArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedTArrayFrom(
[]string{"a", "d", "c", "b"},
gutil.ComparatorT,
)
i1, ok := array1.PopLeft()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "a")
t.Assert(array1.Len(), 3)
t.Assert(array1, []any{"b", "c", "d"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{1, 2, 3}, gutil.ComparatorT)
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedTArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedTArrayFrom(
[]string{"a", "d", "c", "b"},
gutil.ComparatorT,
)
i1, ok := array1.PopRight()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "d")
t.Assert(array1.Len(), 3)
t.Assert(array1, []any{"a", "b", "c"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{1, 2, 3}, gutil.ComparatorT)
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedTArray_PopRand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(i1, []string{"a", "d", "c", "b"})
t.Assert(array1.Len(), 3)
})
}
func TestSortedTArray_PopRands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
i1 := array1.PopRands(2)
t.Assert(len(i1), 2)
t.AssertIN(i1, []string{"a", "d", "c", "b"})
t.Assert(array1.Len(), 2)
i2 := array1.PopRands(3)
t.Assert(len(i2), 2)
t.AssertIN(i2, []string{"a", "d", "c", "b"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedTArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArray[int](gutil.ComparatorT)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
func TestSortedTArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
i1 := array1.PopLefts(2)
t.Assert(len(i1), 2)
t.AssertIN(i1, []string{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopLefts(5)
t.Assert(len(i2), 4)
t.AssertIN(i2, []string{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedTArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
i1 := array1.PopRights(2)
t.Assert(len(i1), 2)
t.Assert(i1, []string{"e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopRights(10)
t.Assert(len(i2), 4)
t.Assert(array1.Len(), 0)
})
}
func TestSortedTArray_Range(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
array2 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT, true)
i1 := array1.Range(2, 5)
t.Assert(i1, []string{"c", "d", "e"})
t.Assert(array1.Len(), 6)
i2 := array1.Range(7, 5)
t.Assert(len(i2), 0)
i2 = array1.Range(-1, 2)
t.Assert(i2, []string{"a", "b"})
i2 = array1.Range(4, 10)
t.Assert(len(i2), 2)
t.Assert(i2, []string{"e", "f"})
t.Assert(array2.Range(1, 3), []string{"b", "c"})
})
}
func TestSortedTArray_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
a2 := []string{"1", "2", "3", "b", "e", "f"}
a3 := []string{"4", "5", "6"}
array1 := garray.NewSortedTArrayFrom(a1, gutil.ComparatorT)
array2 := garray.NewSortedTArrayFrom(a2, gutil.ComparatorT)
array3 := garray.NewSortedTArrayFrom(a3, gutil.ComparatorT)
t.Assert(array1.Sum(), 0)
t.Assert(array2.Sum(), 6)
t.Assert(array3.Sum(), 15)
})
}
func TestSortedTArray_Clone(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
array2 := array1.Clone()
t.Assert(array1, array2)
array1.Remove(1)
t.AssertNE(array1, array2)
})
}
func TestSortedTArray_Clear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e", "f"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
t.Assert(array1.Len(), 6)
array1.Clear()
t.Assert(array1.Len(), 0)
})
}
func TestSortedTArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
i1 := array1.Chunk(2)
t.Assert(len(i1), 3)
t.Assert(i1[0], []any{"a", "b"})
t.Assert(i1[2], []any{"e"})
i1 = array1.Chunk(0)
t.Assert(len(i1), 0)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int32{1, 2, 3, 4, 5}
array1 := garray.NewSortedTArrayFrom(a1, nil)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int32{1, 2, 3})
t.Assert(chunks[1], []int32{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedTArrayFrom(a1, nil)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []int{1, 2})
t.Assert(chunks[1], []int{3, 4})
t.Assert(chunks[2], []int{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedTArrayFrom(a1, nil)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int{1, 2, 3})
t.Assert(chunks[1], []int{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestSortedTArray_SubSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b", "e"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
array2 := garray.NewSortedTArrayFrom(a1, nil, true)
i1 := array1.SubSlice(2, 3)
t.Assert(len(i1), 3)
t.Assert(i1, []string{"c", "d", "e"})
i1 = array1.SubSlice(2, 6)
t.Assert(len(i1), 3)
t.Assert(i1, []string{"c", "d", "e"})
i1 = array1.SubSlice(7, 2)
t.Assert(len(i1), 0)
s1 := array1.SubSlice(1, -2)
t.Assert(s1, nil)
s1 = array1.SubSlice(-9, 2)
t.Assert(s1, nil)
t.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
})
}
func TestSortedTArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
i1, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(i1, []string{"a", "d", "c"})
t.Assert(array1.Len(), 3)
array2 := garray.NewSortedTArrayFrom([]string{}, nil)
v, ok := array2.Rand()
t.Assert(ok, false)
t.Assert(v, nil)
})
}
func TestSortedTArray_Rands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
i1 := array1.Rands(2)
t.AssertIN(i1, []string{"a", "d", "c"})
t.Assert(len(i1), 2)
t.Assert(array1.Len(), 3)
i1 = array1.Rands(4)
t.Assert(len(i1), 4)
array2 := garray.NewSortedTArrayFrom([]string{}, nil)
v := array2.Rands(1)
t.Assert(v, nil)
})
}
func TestSortedTArray_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
t.Assert(array1.Join(","), `a,c,d`)
t.Assert(array1.Join("."), `a.c.d`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", `"a"`, `\a`}
array1 := garray.NewSortedTArrayFrom(a1, nil)
t.Assert(array1.Join("."), `"a".0.1.\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{}
array1 := garray.NewSortedTArrayFrom(a1, nil)
t.Assert(array1.Join("."), "")
})
}
func TestSortedTArray_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "a", "b"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
t.Assert(array1.String(), `[0,1,"a","b"]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
func TestSortedTArray_CountValues(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "c"}
array1 := garray.NewSortedTArrayFrom(a1, nil)
m1 := array1.CountValues()
t.Assert(len(m1), 3)
t.Assert(m1["c"], 2)
t.Assert(m1["a"], 1)
})
}
func TestSortedTArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedTArrayFrom(a1, nil)
array1.SetUnique(true)
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
})
}
func TestSortedTArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedTArrayFrom(a1, nil)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
array2 := garray.NewSortedTArrayFrom([]int{}, nil)
array2.Unique()
t.Assert(array2.Len(), 0)
t.Assert(array2, []int{})
})
}
func TestSortedTArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedTArrayFrom(s1, nil, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.LockFunc(func(n1 []string) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedTArray_RLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedTArrayFrom(s1, nil, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.RLockFunc(func(n1 []string) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertLT(t2-t1, 20) // go1加的读锁所go2读的时候不会被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedTArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
i2 := garray.NewArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedTArrayFrom([]int{4, 5}, nil)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewSortedTArrayFrom(s1, nil)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i1).Len(), 9)
t.Assert(a1.Merge(i2).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestSortedTArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
s2 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedTArrayFrom(s1, nil)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedTArray[string](nil)
err1 = json.UnmarshalUseNumber(b2, &a2)
t.AssertNil(err1)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedTArray[string]
a3.SetComparator(nil)
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
s2 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedTArrayFrom(s1, nil)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedTArray[string](nil)
err1 = json.UnmarshalUseNumber(b2, &a2)
t.AssertNil(err1)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedTArray[string]
a3.SetComparator(nil)
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores *garray.SortedTArray[int]
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.AssertNE(user.Scores, nil)
t.Assert(user.Scores.Len(), 3)
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, 0)
t.Assert(ok, false)
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores *garray.SortedTArray[int]
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.AssertNE(user.Scores, nil)
t.Assert(user.Scores.Len(), 3)
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, 0)
t.Assert(ok, false)
})
}
func TestSortedTArray_Iterator(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedTArrayFrom(slice, nil)
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v string) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v string) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v string) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v string) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v string) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v string) bool {
index++
return false
})
t.Assert(index, 1)
})
}
func TestSortedTArray_RemoveValue(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedTArrayFrom(slice, nil)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestSortedTArray_RemoveValues(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedTArrayFrom(slice, nil)
gtest.C(t, func(t *gtest.T) {
array.RemoveValues("a", "b", "c")
t.Assert(array.Slice(), g.SliceStr{"d"})
})
}
func TestSortedTArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.SortedTArray[int]
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[2,3,1]`),
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.SliceInt{2, 3, 1},
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestSortedTArray_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.SliceInt{0, 1, 2, 3, 4, -1, -2}
array := garray.NewSortedTArrayFromCopy(values, nil)
t.Assert(array.Filter(func(index int, value int) bool {
return value < 0
}).Slice(), g.Slice{0, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFromCopy(g.SliceInt{-1, 1, 2, 3, 4, -2}, nil)
t.Assert(array.Filter(func(index int, value int) bool {
return value < 0
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0, 0}, nil)
t.Assert(array.Filter(func(index int, value int) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{1, 2, 3, 4}, nil)
t.Assert(array.Filter(func(index int, value int) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
}
func TestSortedTArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.SliceInt{0, 1, 2, 3, 4, -1, -2}
array := garray.NewSortedTArrayFromCopy(values, gutil.ComparatorT)
t.Assert(array.FilterNil().Slice(), g.SliceInt{-2, -1, 0, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, -1, -2, nil, []any{}, ""}
array := garray.NewSortedTArrayFromCopy(values, nil)
t.Assert(array.FilterNil().Slice(), g.Slice{"", -1, -2, 0, 1, 2, 3, 4, []any{}})
})
}
func TestSortedTArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0, 0}, nil)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceInt{1, 2, 3, 4}, nil)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceStr{"a", "", "b", "c", ""}, nil)
t.Assert(array.FilterEmpty(), g.Slice{"a", "b", "c"})
})
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, -1, -2, nil, []any{}, ""}
array := garray.NewSortedTArrayFromCopy(values, nil)
t.Assert(array.FilterEmpty().Slice(), g.Slice{-1, -2, 1, 2, 3, 4})
})
}
func TestSortedTArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom(g.SliceStr{"1", "2"}, nil)
t.Assert(array.Walk(func(value string) string {
return "key-" + value
}), g.Slice{"key-1", "key-2"})
})
}
func TestSortedTArray_IsEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom([]string{}, nil)
t.Assert(array.IsEmpty(), true)
})
}
func TestSortedTArray_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedTArrayFrom([]int{1, 2, 3, 4, 5}, nil)
copyArray := array.DeepCopy().(*garray.SortedTArray[int])
array.Add(6)
copyArray.Add(7)
cval, _ := copyArray.Get(5)
val, _ := array.Get(5)
t.AssertNE(cval, val)
})
}

View File

@ -41,7 +41,7 @@ func New(safe ...bool) *List {
// NewFrom creates and returns a list from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using list in concurrent-safety,
// which is false in default.
func NewFrom(array []interface{}, safe ...bool) *List {
func NewFrom(array []any, safe ...bool) *List {
l := list.New()
for _, v := range array {
l.PushBack(v)
@ -53,7 +53,7 @@ func NewFrom(array []interface{}, safe ...bool) *List {
}
// PushFront inserts a new element `e` with value `v` at the front of list `l` and returns `e`.
func (l *List) PushFront(v interface{}) (e *Element) {
func (l *List) PushFront(v any) (e *Element) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
@ -64,7 +64,7 @@ func (l *List) PushFront(v interface{}) (e *Element) {
}
// PushBack inserts a new element `e` with value `v` at the back of list `l` and returns `e`.
func (l *List) PushBack(v interface{}) (e *Element) {
func (l *List) PushBack(v any) (e *Element) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
@ -75,7 +75,7 @@ func (l *List) PushBack(v interface{}) (e *Element) {
}
// PushFronts inserts multiple new elements with values `values` at the front of list `l`.
func (l *List) PushFronts(values []interface{}) {
func (l *List) PushFronts(values []any) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
@ -87,7 +87,7 @@ func (l *List) PushFronts(values []interface{}) {
}
// PushBacks inserts multiple new elements with values `values` at the back of list `l`.
func (l *List) PushBacks(values []interface{}) {
func (l *List) PushBacks(values []any) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
@ -99,7 +99,7 @@ func (l *List) PushBacks(values []interface{}) {
}
// PopBack removes the element from back of `l` and returns the value of the element.
func (l *List) PopBack() (value interface{}) {
func (l *List) PopBack() (value any) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -113,7 +113,7 @@ func (l *List) PopBack() (value interface{}) {
}
// PopFront removes the element from front of `l` and returns the value of the element.
func (l *List) PopFront() (value interface{}) {
func (l *List) PopFront() (value any) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -128,7 +128,7 @@ func (l *List) PopFront() (value interface{}) {
// PopBacks removes `max` elements from back of `l`
// and returns values of the removed elements as slice.
func (l *List) PopBacks(max int) (values []interface{}) {
func (l *List) PopBacks(max int) (values []any) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -140,7 +140,7 @@ func (l *List) PopBacks(max int) (values []interface{}) {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
values = make([]any, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Back())
}
@ -150,7 +150,7 @@ func (l *List) PopBacks(max int) (values []interface{}) {
// PopFronts removes `max` elements from front of `l`
// and returns values of the removed elements as slice.
func (l *List) PopFronts(max int) (values []interface{}) {
func (l *List) PopFronts(max int) (values []any) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -162,7 +162,7 @@ func (l *List) PopFronts(max int) (values []interface{}) {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
values = make([]any, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Front())
}
@ -172,18 +172,18 @@ func (l *List) PopFronts(max int) (values []interface{}) {
// PopBackAll removes all elements from back of `l`
// and returns values of the removed elements as slice.
func (l *List) PopBackAll() []interface{} {
func (l *List) PopBackAll() []any {
return l.PopBacks(-1)
}
// PopFrontAll removes all elements from front of `l`
// and returns values of the removed elements as slice.
func (l *List) PopFrontAll() []interface{} {
func (l *List) PopFrontAll() []any {
return l.PopFronts(-1)
}
// FrontAll copies and returns values of all elements from front of `l` as slice.
func (l *List) FrontAll() (values []interface{}) {
func (l *List) FrontAll() (values []any) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
@ -191,7 +191,7 @@ func (l *List) FrontAll() (values []interface{}) {
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
values = make([]any, length)
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
values[i] = e.Value
}
@ -200,7 +200,7 @@ func (l *List) FrontAll() (values []interface{}) {
}
// BackAll copies and returns values of all elements from back of `l` as slice.
func (l *List) BackAll() (values []interface{}) {
func (l *List) BackAll() (values []any) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
@ -208,7 +208,7 @@ func (l *List) BackAll() (values []interface{}) {
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
values = make([]any, length)
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
values[i] = e.Value
}
@ -217,7 +217,7 @@ func (l *List) BackAll() (values []interface{}) {
}
// FrontValue returns value of the first element of `l` or nil if the list is empty.
func (l *List) FrontValue() (value interface{}) {
func (l *List) FrontValue() (value any) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
@ -230,7 +230,7 @@ func (l *List) FrontValue() (value interface{}) {
}
// BackValue returns value of the last element of `l` or nil if the list is empty.
func (l *List) BackValue() (value interface{}) {
func (l *List) BackValue() (value any) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
@ -362,7 +362,7 @@ func (l *List) PushFrontList(other *List) {
// InsertAfter inserts a new element `e` with value `v` immediately after `p` and returns `e`.
// If `p` is not an element of `l`, the list is not modified.
// The `p` must not be nil.
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
func (l *List) InsertAfter(p *Element, v any) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -375,7 +375,7 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
// InsertBefore inserts a new element `e` with value `v` immediately before `p` and returns `e`.
// If `p` is not an element of `l`, the list is not modified.
// The `p` must not be nil.
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
func (l *List) InsertBefore(p *Element, v any) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -388,7 +388,7 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
// Remove removes `e` from `l` if `e` is an element of list `l`.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List) Remove(e *Element) (value interface{}) {
func (l *List) Remove(e *Element) (value any) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
@ -522,7 +522,7 @@ func (l *List) UnmarshalJSON(b []byte) error {
if l.list == nil {
l.list = list.New()
}
var array []interface{}
var array []any
if err := json.UnmarshalUseNumber(b, &array); err != nil {
return err
}
@ -531,13 +531,13 @@ func (l *List) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for list.
func (l *List) UnmarshalValue(value interface{}) (err error) {
func (l *List) UnmarshalValue(value any) (err error) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
var array []any
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
@ -549,7 +549,7 @@ func (l *List) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (l *List) DeepCopy() interface{} {
func (l *List) DeepCopy() any {
if l == nil {
return nil
}
@ -562,7 +562,7 @@ func (l *List) DeepCopy() interface{} {
}
var (
length = l.list.Len()
values = make([]interface{}, length)
values = make([]any, length)
)
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {

View File

@ -198,7 +198,7 @@ func TestList(t *testing.T) {
})
}
func checkList(t *gtest.T, l *List, es []interface{}) {
func checkList(t *gtest.T, l *List, es []any) {
if !checkListLen(t, l, len(es)) {
return
}
@ -244,36 +244,36 @@ func TestExtending(t *testing.T) {
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
checkList(t, l3, []any{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
checkList(t, l3, []any{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
checkList(t, l3, []any{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
checkList(t, l3, []any{1, 2, 3, 4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
checkList(t, l1, []any{1, 2, 3})
checkList(t, l2, []any{4, 5})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
checkList(t, l3, []any{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
checkList(t, l3, []any{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
checkList(t, l3, []any{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
checkList(t, l3, []any{1, 2, 3, 1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l1, []any{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l1, []any{1, 2, 3})
})
}
@ -371,19 +371,19 @@ func TestZeroList(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
checkList(t, l1, []any{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
checkList(t, l2, []any{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
checkList(t, l3, []any{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
checkList(t, l4, []any{1})
})
}
@ -395,7 +395,7 @@ func TestInsertBeforeUnknownMark(t *testing.T) {
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
checkList(t, l, []any{1, 2, 3})
})
}
@ -407,7 +407,7 @@ func TestInsertAfterUnknownMark(t *testing.T) {
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
checkList(t, l, []any{1, 2, 3})
})
}
@ -421,12 +421,12 @@ func TestMoveUnknownMark(t *testing.T) {
e2 := l2.PushBack(2)
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
checkList(t, l1, []any{1})
checkList(t, l2, []any{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
checkList(t, l1, []any{1})
checkList(t, l2, []any{2})
})
}
@ -435,58 +435,58 @@ func TestList_RemoveAll(t *testing.T) {
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
checkList(t, l, []any{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
checkList(t, l, []any{2})
})
}
func TestList_PushFronts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2}
a1 := []any{1, 2}
l.PushFronts(a1)
checkList(t, l, []interface{}{2, 1})
a1 = []interface{}{3, 4, 5}
checkList(t, l, []any{2, 1})
a1 = []any{3, 4, 5}
l.PushFronts(a1)
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
checkList(t, l, []any{5, 4, 3, 2, 1})
})
}
func TestList_PushBacks(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2}
a1 := []any{1, 2}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2})
a1 = []interface{}{3, 4, 5}
checkList(t, l, []any{1, 2})
a1 = []any{3, 4, 5}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
checkList(t, l, []any{1, 2, 3, 4, 5})
})
}
func TestList_PopBacks(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a2 := []interface{}{"a", "c", "b", "e"}
a1 := []any{1, 2, 3, 4}
a2 := []any{"a", "c", "b", "e"}
l.PushFronts(a1)
i1 := l.PopBacks(2)
t.Assert(i1, []interface{}{1, 2})
t.Assert(i1, []any{1, 2})
l.PushBacks(a2) // 4.3,a,c,b,e
i1 = l.PopBacks(3)
t.Assert(i1, []interface{}{"e", "b", "c"})
t.Assert(i1, []any{"e", "b", "c"})
})
}
func TestList_PopFronts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFronts(2)
t.Assert(i1, []interface{}{4, 3})
t.Assert(i1, []any{4, 3})
t.Assert(l.Len(), 2)
})
}
@ -494,10 +494,10 @@ func TestList_PopFronts(t *testing.T) {
func TestList_PopBackAll(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopBackAll()
t.Assert(i1, []interface{}{1, 2, 3, 4})
t.Assert(i1, []any{1, 2, 3, 4})
t.Assert(l.Len(), 0)
})
}
@ -505,10 +505,10 @@ func TestList_PopBackAll(t *testing.T) {
func TestList_PopFrontAll(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFrontAll()
t.Assert(i1, []interface{}{4, 3, 2, 1})
t.Assert(i1, []any{4, 3, 2, 1})
t.Assert(l.Len(), 0)
})
}
@ -516,10 +516,10 @@ func TestList_PopFrontAll(t *testing.T) {
func TestList_FrontAll(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontAll()
t.Assert(i1, []interface{}{4, 3, 2, 1})
t.Assert(i1, []any{4, 3, 2, 1})
t.Assert(l.Len(), 4)
})
}
@ -527,10 +527,10 @@ func TestList_FrontAll(t *testing.T) {
func TestList_BackAll(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackAll()
t.Assert(i1, []interface{}{1, 2, 3, 4})
t.Assert(i1, []any{1, 2, 3, 4})
t.Assert(l.Len(), 4)
})
}
@ -539,7 +539,7 @@ func TestList_FrontValue(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontValue()
t.Assert(gconv.Int(i1), 4)
@ -554,7 +554,7 @@ func TestList_BackValue(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackValue()
t.Assert(gconv.Int(i1), 1)
@ -568,7 +568,7 @@ func TestList_BackValue(t *testing.T) {
func TestList_Back(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
t.Assert(e1.Value, 1)
@ -579,7 +579,7 @@ func TestList_Back(t *testing.T) {
func TestList_Size(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
t.Assert(l.Size(), 4)
l.PopFront()
@ -590,7 +590,7 @@ func TestList_Size(t *testing.T) {
func TestList_Removes(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
l.Removes([]*Element{e1})
@ -599,25 +599,25 @@ func TestList_Removes(t *testing.T) {
e2 := l.Back()
l.Removes([]*Element{e2})
t.Assert(l.Len(), 2)
checkList(t, l, []interface{}{4, 3})
checkList(t, l, []any{4, 3})
})
}
func TestList_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
l := NewFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
t.Assert(l.PopBack(), 9)
t.Assert(l.PopBacks(2), []interface{}{8, 7})
t.Assert(l.PopBacks(2), []any{8, 7})
t.Assert(l.PopFront(), 1)
t.Assert(l.PopFronts(2), []interface{}{2, 3})
t.Assert(l.PopFronts(2), []any{2, 3})
})
}
func TestList_Clear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
l.Clear()
t.Assert(l.Len(), 0)
@ -627,22 +627,22 @@ func TestList_Clear(t *testing.T) {
func TestList_IteratorAsc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 5, 6, 3, 4}
a1 := []any{1, 2, 5, 6, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
return gconv.Int(e1.Value) > 2
}
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
checkList(t, l, []any{4, 3, 6, 5, 2, 1})
l.IteratorAsc(fun1)
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
checkList(t, l, []any{4, 3, 6, 5, 2, 1})
})
}
func TestList_IteratorDesc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a1 := []any{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
@ -650,28 +650,28 @@ func TestList_IteratorDesc(t *testing.T) {
}
l.IteratorDesc(fun1)
t.Assert(l.Len(), 4)
checkList(t, l, []interface{}{4, 3, 2, 1})
checkList(t, l, []any{4, 3, 2, 1})
})
}
func TestList_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{"a", "b", "c", "d", "e"}
a1 := []any{"a", "b", "c", "d", "e"}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
return gconv.String(e1.Value) > "c"
}
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
checkList(t, l, []any{"e", "d", "c", "b", "a"})
l.Iterator(fun1)
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
checkList(t, l, []any{"e", "d", "c", "b", "a"})
})
}
func TestList_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
l := NewFrom([]any{1, 2, "a", `"b"`, `\c`})
t.Assert(l.Join(","), `1,2,a,"b",\c`)
t.Assert(l.Join("."), `1.2.a."b".\c`)
})
@ -679,7 +679,7 @@ func TestList_Join(t *testing.T) {
func TestList_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
l := NewFrom([]any{1, 2, "a", `"b"`, `\c`})
t.Assert(l.String(), `[1,2,a,"b",\c]`)
})
}
@ -687,7 +687,7 @@ func TestList_String(t *testing.T) {
func TestList_Json(t *testing.T) {
// Marshal
gtest.C(t, func(t *gtest.T) {
a := []interface{}{"a", "b", "c"}
a := []any{"a", "b", "c"}
l := New()
l.PushBacks(a)
b1, err1 := json.Marshal(l)
@ -697,7 +697,7 @@ func TestList_Json(t *testing.T) {
})
// Unmarshal
gtest.C(t, func(t *gtest.T) {
a := []interface{}{"a", "b", "c"}
a := []any{"a", "b", "c"}
l := New()
b, err := json.Marshal(a)
t.AssertNil(err)
@ -708,7 +708,7 @@ func TestList_Json(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
var l List
a := []interface{}{"a", "b", "c"}
a := []any{"a", "b", "c"}
b, err := json.Marshal(a)
t.AssertNil(err)
@ -726,30 +726,30 @@ func TestList_UnmarshalValue(t *testing.T) {
// JSON
gtest.C(t, func(t *gtest.T) {
var tlist *TList
err := gconv.Struct(map[string]interface{}{
err := gconv.Struct(map[string]any{
"name": "john",
"list": []byte(`[1,2,3]`),
}, &tlist)
t.AssertNil(err)
t.Assert(tlist.Name, "john")
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
t.Assert(tlist.List.FrontAll(), []any{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var tlist *TList
err := gconv.Struct(map[string]interface{}{
err := gconv.Struct(map[string]any{
"name": "john",
"list": []interface{}{1, 2, 3},
"list": []any{1, 2, 3},
}, &tlist)
t.AssertNil(err)
t.Assert(tlist.Name, "john")
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
t.Assert(tlist.List.FrontAll(), []any{1, 2, 3})
})
}
func TestList_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
l := NewFrom([]any{1, 2, "a", `"b"`, `\c`})
copyList := l.DeepCopy()
cl := copyList.(*List)
cl.PopBack()

View File

@ -24,7 +24,7 @@ func New(safe ...bool) *Map {
// there might be some concurrent-safe issues when changing the map outside.
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewFrom(data map[interface{}]interface{}, safe ...bool) *Map {
func NewFrom(data map[any]any, safe ...bool) *Map {
return NewAnyAnyMapFrom(data, safe...)
}
@ -40,6 +40,6 @@ func NewHashMap(safe ...bool) *Map {
// there might be some concurrent-safe issues when changing the map outside.
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewHashMapFrom(data map[interface{}]interface{}, safe ...bool) *Map {
func NewHashMapFrom(data map[any]any, safe ...bool) *Map {
return NewAnyAnyMapFrom(data, safe...)
}

View File

@ -17,10 +17,10 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
// AnyAnyMap wraps map type `map[interface{}]interface{}` and provides more map features.
// AnyAnyMap wraps map type `map[any]any` and provides more map features.
type AnyAnyMap struct {
mu rwmutex.RWMutex
data map[interface{}]interface{}
data map[any]any
}
// NewAnyAnyMap creates and returns an empty hash map.
@ -29,14 +29,14 @@ type AnyAnyMap struct {
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.Create(safe...),
data: make(map[interface{}]interface{}),
data: make(map[any]any),
}
}
// NewAnyAnyMapFrom creates and returns a hash map from given map `data`.
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
func NewAnyAnyMapFrom(data map[any]any, safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.Create(safe...),
data: data,
@ -45,7 +45,7 @@ func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap
// Iterator iterates the hash map readonly with custom callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
func (m *AnyAnyMap) Iterator(f func(k any, v any) bool) {
for k, v := range m.Map() {
if !f(k, v) {
break
@ -61,13 +61,13 @@ func (m *AnyAnyMap) Clone(safe ...bool) *AnyAnyMap {
// Map returns the underlying data map.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (m *AnyAnyMap) Map() map[interface{}]interface{} {
func (m *AnyAnyMap) Map() map[any]any {
m.mu.RLock()
defer m.mu.RUnlock()
if !m.mu.IsSafe() {
return m.data
}
data := make(map[interface{}]interface{}, len(m.data))
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
@ -75,21 +75,21 @@ func (m *AnyAnyMap) Map() map[interface{}]interface{} {
}
// MapCopy returns a shallow copy of the underlying data of the hash map.
func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
func (m *AnyAnyMap) MapCopy() map[any]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[interface{}]interface{}, len(m.data))
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *AnyAnyMap) MapStrAny() map[string]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
@ -120,17 +120,17 @@ func (m *AnyAnyMap) FilterNil() {
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
func (m *AnyAnyMap) Set(key any, value any) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
m.data = make(map[any]any)
}
m.data[key] = value
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
func (m *AnyAnyMap) Sets(data map[any]any) {
m.mu.Lock()
if m.data == nil {
m.data = data
@ -144,7 +144,7 @@ func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
// Search searches the map with given `key`.
// Second return parameter `found` is true if key was found, otherwise false.
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
func (m *AnyAnyMap) Search(key any) (value any, found bool) {
m.mu.RLock()
if m.data != nil {
value, found = m.data[key]
@ -154,7 +154,7 @@ func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
}
// Get returns the value by given `key`.
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
func (m *AnyAnyMap) Get(key any) (value any) {
m.mu.RLock()
if m.data != nil {
value = m.data[key]
@ -164,7 +164,7 @@ func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
}
// Pop retrieves and deletes an item from the map.
func (m *AnyAnyMap) Pop() (key, value interface{}) {
func (m *AnyAnyMap) Pop() (key, value any) {
m.mu.Lock()
defer m.mu.Unlock()
for key, value = range m.data {
@ -176,7 +176,7 @@ func (m *AnyAnyMap) Pop() (key, value interface{}) {
// Pops retrieves and deletes `size` items from the map.
// It returns all items if size == -1.
func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
func (m *AnyAnyMap) Pops(size int) map[any]any {
m.mu.Lock()
defer m.mu.Unlock()
if size > len(m.data) || size == -1 {
@ -187,7 +187,7 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
}
var (
index = 0
newMap = make(map[interface{}]interface{}, size)
newMap = make(map[any]any, size)
)
for k, v := range m.data {
delete(m.data, k)
@ -209,16 +209,16 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
// and its return value will be set to the map with `key`.
//
// It returns value with given `key`.
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
func (m *AnyAnyMap) doSetWithLockCheck(key any, value any) any {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
m.data = make(map[any]any)
}
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
if f, ok := value.(func() any); ok {
value = f()
}
if value != nil {
@ -229,7 +229,7 @@ func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) inter
// GetOrSet returns the value by key,
// or sets value with given `value` if it does not exist and then returns this value.
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
func (m *AnyAnyMap) GetOrSet(key any, value any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
@ -240,7 +240,7 @@ func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
// GetOrSetFunc returns the value by key,
// or sets value with returned value of callback function `f` if it does not exist
// and then returns this value.
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
func (m *AnyAnyMap) GetOrSetFunc(key any, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
@ -254,7 +254,7 @@ func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interfac
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
// with mutex.Lock of the hash map.
func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
func (m *AnyAnyMap) GetOrSetFuncLock(key any, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
@ -264,31 +264,31 @@ func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) inte
// GetVar returns a Var with the value by given `key`.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
func (m *AnyAnyMap) GetVar(key any) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a Var with result from GetOrSet.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
func (m *AnyAnyMap) GetVarOrSet(key any, value any) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
func (m *AnyAnyMap) GetVarOrSetFunc(key any, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
func (m *AnyAnyMap) GetVarOrSetFuncLock(key any, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
func (m *AnyAnyMap) SetIfNotExist(key any, value any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
@ -298,7 +298,7 @@ func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
func (m *AnyAnyMap) SetIfNotExistFunc(key any, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
@ -311,7 +311,7 @@ func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) boo
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
// it executes function `f` with mutex.Lock of the hash map.
func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
func (m *AnyAnyMap) SetIfNotExistFuncLock(key any, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
@ -320,7 +320,7 @@ func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{})
}
// Remove deletes value from map by given `key`, and return this deleted value.
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
func (m *AnyAnyMap) Remove(key any) (value any) {
m.mu.Lock()
if m.data != nil {
var ok bool
@ -333,7 +333,7 @@ func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
}
// Removes batch deletes values of the map by keys.
func (m *AnyAnyMap) Removes(keys []interface{}) {
func (m *AnyAnyMap) Removes(keys []any) {
m.mu.Lock()
if m.data != nil {
for _, key := range keys {
@ -344,11 +344,11 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
}
// Keys returns all keys of the map as a slice.
func (m *AnyAnyMap) Keys() []interface{} {
func (m *AnyAnyMap) Keys() []any {
m.mu.RLock()
defer m.mu.RUnlock()
var (
keys = make([]interface{}, len(m.data))
keys = make([]any, len(m.data))
index = 0
)
for key := range m.data {
@ -359,11 +359,11 @@ func (m *AnyAnyMap) Keys() []interface{} {
}
// Values returns all values of the map as a slice.
func (m *AnyAnyMap) Values() []interface{} {
func (m *AnyAnyMap) Values() []any {
m.mu.RLock()
defer m.mu.RUnlock()
var (
values = make([]interface{}, len(m.data))
values = make([]any, len(m.data))
index = 0
)
for _, value := range m.data {
@ -375,7 +375,7 @@ func (m *AnyAnyMap) Values() []interface{} {
// Contains checks whether a key exists.
// It returns true if the `key` exists, or else false.
func (m *AnyAnyMap) Contains(key interface{}) bool {
func (m *AnyAnyMap) Contains(key any) bool {
var ok bool
m.mu.RLock()
if m.data != nil {
@ -402,26 +402,26 @@ func (m *AnyAnyMap) IsEmpty() bool {
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *AnyAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[interface{}]interface{})
m.data = make(map[any]any)
m.mu.Unlock()
}
// Replace the data of the map with given `data`.
func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
func (m *AnyAnyMap) Replace(data map[any]any) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
func (m *AnyAnyMap) LockFunc(f func(m map[any]any)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
func (m *AnyAnyMap) RLockFunc(f func(m map[any]any)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
@ -431,7 +431,7 @@ func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
func (m *AnyAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[interface{}]interface{}, len(m.data))
n := make(map[any]any, len(m.data))
for k, v := range m.data {
n[v] = k
}
@ -475,9 +475,9 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
m.data = make(map[any]any)
}
var data map[string]interface{}
var data map[string]any
if err := json.UnmarshalUseNumber(b, &data); err != nil {
return err
}
@ -488,11 +488,11 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
func (m *AnyAnyMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
m.data = make(map[any]any)
}
for k, v := range gconv.Map(value) {
m.data[k] = v
@ -501,14 +501,14 @@ func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *AnyAnyMap) DeepCopy() interface{} {
func (m *AnyAnyMap) DeepCopy() any {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[interface{}]interface{}, len(m.data))
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = deepcopy.Copy(v)
}
@ -540,7 +540,7 @@ func (m *AnyAnyMap) IsSubOf(other *AnyAnyMap) bool {
// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
func (m *AnyAnyMap) Diff(other *AnyAnyMap) (addedKeys, removedKeys, updatedKeys []interface{}) {
func (m *AnyAnyMap) Diff(other *AnyAnyMap) (addedKeys, removedKeys, updatedKeys []any) {
m.mu.RLock()
defer m.mu.RUnlock()
other.mu.RLock()

View File

@ -18,10 +18,10 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
// IntAnyMap implements map[int]interface{} with RWMutex that has switch.
// IntAnyMap implements map[int]any with RWMutex that has switch.
type IntAnyMap struct {
mu rwmutex.RWMutex
data map[int]interface{}
data map[int]any
}
// NewIntAnyMap returns an empty IntAnyMap object.
@ -30,14 +30,14 @@ type IntAnyMap struct {
func NewIntAnyMap(safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.Create(safe...),
data: make(map[int]interface{}),
data: make(map[int]any),
}
}
// NewIntAnyMapFrom creates and returns a hash map from given map `data`.
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
func NewIntAnyMapFrom(data map[int]any, safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.Create(safe...),
data: data,
@ -46,7 +46,7 @@ func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
// Iterator iterates the hash map readonly with custom callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
func (m *IntAnyMap) Iterator(f func(k int, v any) bool) {
for k, v := range m.Map() {
if !f(k, v) {
break
@ -62,23 +62,23 @@ func (m *IntAnyMap) Clone() *IntAnyMap {
// Map returns the underlying data map.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (m *IntAnyMap) Map() map[int]interface{} {
func (m *IntAnyMap) Map() map[int]any {
m.mu.RLock()
defer m.mu.RUnlock()
if !m.mu.IsSafe() {
return m.data
}
data := make(map[int]interface{}, len(m.data))
data := make(map[int]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntAnyMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *IntAnyMap) MapStrAny() map[string]any {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
@ -87,10 +87,10 @@ func (m *IntAnyMap) MapStrAny() map[string]interface{} {
}
// MapCopy returns a copy of the underlying data of the hash map.
func (m *IntAnyMap) MapCopy() map[int]interface{} {
func (m *IntAnyMap) MapCopy() map[int]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[int]interface{}, len(m.data))
data := make(map[int]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
@ -121,17 +121,17 @@ func (m *IntAnyMap) FilterNil() {
}
// Set sets key-value to the hash map.
func (m *IntAnyMap) Set(key int, val interface{}) {
func (m *IntAnyMap) Set(key int, val any) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]interface{})
m.data = make(map[int]any)
}
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *IntAnyMap) Sets(data map[int]interface{}) {
func (m *IntAnyMap) Sets(data map[int]any) {
m.mu.Lock()
if m.data == nil {
m.data = data
@ -145,7 +145,7 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) {
// Search searches the map with given `key`.
// Second return parameter `found` is true if key was found, otherwise false.
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
func (m *IntAnyMap) Search(key int) (value any, found bool) {
m.mu.RLock()
if m.data != nil {
value, found = m.data[key]
@ -155,7 +155,7 @@ func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
}
// Get returns the value by given `key`.
func (m *IntAnyMap) Get(key int) (value interface{}) {
func (m *IntAnyMap) Get(key int) (value any) {
m.mu.RLock()
if m.data != nil {
value = m.data[key]
@ -165,7 +165,7 @@ func (m *IntAnyMap) Get(key int) (value interface{}) {
}
// Pop retrieves and deletes an item from the map.
func (m *IntAnyMap) Pop() (key int, value interface{}) {
func (m *IntAnyMap) Pop() (key int, value any) {
m.mu.Lock()
defer m.mu.Unlock()
for key, value = range m.data {
@ -177,7 +177,7 @@ func (m *IntAnyMap) Pop() (key int, value interface{}) {
// Pops retrieves and deletes `size` items from the map.
// It returns all items if size == -1.
func (m *IntAnyMap) Pops(size int) map[int]interface{} {
func (m *IntAnyMap) Pops(size int) map[int]any {
m.mu.Lock()
defer m.mu.Unlock()
if size > len(m.data) || size == -1 {
@ -188,7 +188,7 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
}
var (
index = 0
newMap = make(map[int]interface{}, size)
newMap = make(map[int]any, size)
)
for k, v := range m.data {
delete(m.data, k)
@ -210,16 +210,16 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
// and its return value will be set to the map with `key`.
//
// It returns value with given `key`.
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
func (m *IntAnyMap) doSetWithLockCheck(key int, value any) any {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
m.data = make(map[int]any)
}
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
if f, ok := value.(func() any); ok {
value = f()
}
if value != nil {
@ -230,7 +230,7 @@ func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
// GetOrSet returns the value by key,
// or sets value with given `value` if it does not exist and then returns this value.
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
func (m *IntAnyMap) GetOrSet(key int, value any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
@ -240,7 +240,7 @@ func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
// GetOrSetFunc returns the value by key,
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
func (m *IntAnyMap) GetOrSetFunc(key int, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
@ -253,7 +253,7 @@ func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
// with mutex.Lock of the hash map.
func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
@ -269,25 +269,25 @@ func (m *IntAnyMap) GetVar(key int) *gvar.Var {
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
func (m *IntAnyMap) GetVarOrSet(key int, value any) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
func (m *IntAnyMap) SetIfNotExist(key int, value any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
@ -297,7 +297,7 @@ func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
@ -310,7 +310,7 @@ func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
// it executes function `f` with mutex.Lock of the hash map.
func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
@ -330,7 +330,7 @@ func (m *IntAnyMap) Removes(keys []int) {
}
// Remove deletes value from map by given `key`, and return this deleted value.
func (m *IntAnyMap) Remove(key int) (value interface{}) {
func (m *IntAnyMap) Remove(key int) (value any) {
m.mu.Lock()
if m.data != nil {
var ok bool
@ -358,10 +358,10 @@ func (m *IntAnyMap) Keys() []int {
}
// Values returns all values of the map as a slice.
func (m *IntAnyMap) Values() []interface{} {
func (m *IntAnyMap) Values() []any {
m.mu.RLock()
var (
values = make([]interface{}, len(m.data))
values = make([]any, len(m.data))
index = 0
)
for _, value := range m.data {
@ -401,26 +401,26 @@ func (m *IntAnyMap) IsEmpty() bool {
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *IntAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[int]interface{})
m.data = make(map[int]any)
m.mu.Unlock()
}
// Replace the data of the map with given `data`.
func (m *IntAnyMap) Replace(data map[int]interface{}) {
func (m *IntAnyMap) Replace(data map[int]any) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
func (m *IntAnyMap) LockFunc(f func(m map[int]any)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
func (m *IntAnyMap) RLockFunc(f func(m map[int]any)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
@ -430,7 +430,7 @@ func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
func (m *IntAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]interface{}, len(m.data))
n := make(map[int]any, len(m.data))
for k, v := range m.data {
n[gconv.Int(v)] = k
}
@ -476,7 +476,7 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
m.data = make(map[int]any)
}
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
return err
@ -485,11 +485,11 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
func (m *IntAnyMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
m.data = make(map[int]any)
}
switch value.(type) {
case string, []byte:
@ -503,13 +503,13 @@ func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *IntAnyMap) DeepCopy() interface{} {
func (m *IntAnyMap) DeepCopy() any {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[int]interface{}, len(m.data))
data := make(map[int]any, len(m.data))
for k, v := range m.data {
data[k] = deepcopy.Copy(v)
}

View File

@ -70,10 +70,10 @@ func (m *IntIntMap) Map() map[int]int {
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntIntMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *IntIntMap) MapStrAny() map[string]any {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
@ -453,7 +453,7 @@ func (m *IntIntMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
func (m *IntIntMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
@ -471,7 +471,7 @@ func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *IntIntMap) DeepCopy() interface{} {
func (m *IntIntMap) DeepCopy() any {
if m == nil {
return nil
}

View File

@ -70,10 +70,10 @@ func (m *IntStrMap) Map() map[int]string {
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntStrMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *IntStrMap) MapStrAny() map[string]any {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
@ -453,7 +453,7 @@ func (m *IntStrMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
func (m *IntStrMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
@ -471,7 +471,7 @@ func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *IntStrMap) DeepCopy() interface{} {
func (m *IntStrMap) DeepCopy() any {
if m == nil {
return nil
}

View File

@ -18,10 +18,10 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
// StrAnyMap implements map[string]interface{} with RWMutex that has switch.
// StrAnyMap implements map[string]any with RWMutex that has switch.
type StrAnyMap struct {
mu rwmutex.RWMutex
data map[string]interface{}
data map[string]any
}
// NewStrAnyMap returns an empty StrAnyMap object.
@ -30,14 +30,14 @@ type StrAnyMap struct {
func NewStrAnyMap(safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.Create(safe...),
data: make(map[string]interface{}),
data: make(map[string]any),
}
}
// NewStrAnyMapFrom creates and returns a hash map from given map `data`.
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
func NewStrAnyMapFrom(data map[string]any, safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.Create(safe...),
data: data,
@ -46,7 +46,7 @@ func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
// Iterator iterates the hash map readonly with custom callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
func (m *StrAnyMap) Iterator(f func(k string, v any) bool) {
for k, v := range m.Map() {
if !f(k, v) {
break
@ -62,29 +62,29 @@ func (m *StrAnyMap) Clone() *StrAnyMap {
// Map returns the underlying data map.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (m *StrAnyMap) Map() map[string]interface{} {
func (m *StrAnyMap) Map() map[string]any {
m.mu.RLock()
defer m.mu.RUnlock()
if !m.mu.IsSafe() {
return m.data
}
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrAnyMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *StrAnyMap) MapStrAny() map[string]any {
return m.Map()
}
// MapCopy returns a copy of the underlying data of the hash map.
func (m *StrAnyMap) MapCopy() map[string]interface{} {
func (m *StrAnyMap) MapCopy() map[string]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
@ -115,17 +115,17 @@ func (m *StrAnyMap) FilterNil() {
}
// Set sets key-value to the hash map.
func (m *StrAnyMap) Set(key string, val interface{}) {
func (m *StrAnyMap) Set(key string, val any) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]interface{})
m.data = make(map[string]any)
}
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *StrAnyMap) Sets(data map[string]interface{}) {
func (m *StrAnyMap) Sets(data map[string]any) {
m.mu.Lock()
if m.data == nil {
m.data = data
@ -139,7 +139,7 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) {
// Search searches the map with given `key`.
// Second return parameter `found` is true if key was found, otherwise false.
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
func (m *StrAnyMap) Search(key string) (value any, found bool) {
m.mu.RLock()
if m.data != nil {
value, found = m.data[key]
@ -149,7 +149,7 @@ func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
}
// Get returns the value by given `key`.
func (m *StrAnyMap) Get(key string) (value interface{}) {
func (m *StrAnyMap) Get(key string) (value any) {
m.mu.RLock()
if m.data != nil {
value = m.data[key]
@ -159,7 +159,7 @@ func (m *StrAnyMap) Get(key string) (value interface{}) {
}
// Pop retrieves and deletes an item from the map.
func (m *StrAnyMap) Pop() (key string, value interface{}) {
func (m *StrAnyMap) Pop() (key string, value any) {
m.mu.Lock()
defer m.mu.Unlock()
for key, value = range m.data {
@ -171,7 +171,7 @@ func (m *StrAnyMap) Pop() (key string, value interface{}) {
// Pops retrieves and deletes `size` items from the map.
// It returns all items if size == -1.
func (m *StrAnyMap) Pops(size int) map[string]interface{} {
func (m *StrAnyMap) Pops(size int) map[string]any {
m.mu.Lock()
defer m.mu.Unlock()
if size > len(m.data) || size == -1 {
@ -182,7 +182,7 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
}
var (
index = 0
newMap = make(map[string]interface{}, size)
newMap = make(map[string]any, size)
)
for k, v := range m.data {
delete(m.data, k)
@ -204,16 +204,16 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
// and its return value will be set to the map with `key`.
//
// It returns value with given `key`.
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
func (m *StrAnyMap) doSetWithLockCheck(key string, value any) any {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
m.data = make(map[string]any)
}
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
if f, ok := value.(func() any); ok {
value = f()
}
if value != nil {
@ -224,7 +224,7 @@ func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{
// GetOrSet returns the value by key,
// or sets value with given `value` if it does not exist and then returns this value.
func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
func (m *StrAnyMap) GetOrSet(key string, value any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
@ -235,7 +235,7 @@ func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
// GetOrSetFunc returns the value by key,
// or sets value with returned value of callback function `f` if it does not exist
// and then returns this value.
func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
func (m *StrAnyMap) GetOrSetFunc(key string, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
@ -249,7 +249,7 @@ func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
// with mutex.Lock of the hash map.
func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
@ -265,25 +265,25 @@ func (m *StrAnyMap) GetVar(key string) *gvar.Var {
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
func (m *StrAnyMap) GetVarOrSet(key string, value any) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
func (m *StrAnyMap) SetIfNotExist(key string, value any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
@ -293,7 +293,7 @@ func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
@ -306,7 +306,7 @@ func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
// it executes function `f` with mutex.Lock of the hash map.
func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f)
return true
@ -326,7 +326,7 @@ func (m *StrAnyMap) Removes(keys []string) {
}
// Remove deletes value from map by given `key`, and return this deleted value.
func (m *StrAnyMap) Remove(key string) (value interface{}) {
func (m *StrAnyMap) Remove(key string) (value any) {
m.mu.Lock()
if m.data != nil {
var ok bool
@ -354,10 +354,10 @@ func (m *StrAnyMap) Keys() []string {
}
// Values returns all values of the map as a slice.
func (m *StrAnyMap) Values() []interface{} {
func (m *StrAnyMap) Values() []any {
m.mu.RLock()
var (
values = make([]interface{}, len(m.data))
values = make([]any, len(m.data))
index = 0
)
for _, value := range m.data {
@ -397,26 +397,26 @@ func (m *StrAnyMap) IsEmpty() bool {
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *StrAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[string]interface{})
m.data = make(map[string]any)
m.mu.Unlock()
}
// Replace the data of the map with given `data`.
func (m *StrAnyMap) Replace(data map[string]interface{}) {
func (m *StrAnyMap) Replace(data map[string]any) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
func (m *StrAnyMap) LockFunc(f func(m map[string]any)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
func (m *StrAnyMap) RLockFunc(f func(m map[string]any)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
@ -426,7 +426,7 @@ func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
func (m *StrAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[string]interface{}, len(m.data))
n := make(map[string]any, len(m.data))
for k, v := range m.data {
n[gconv.String(v)] = k
}
@ -472,7 +472,7 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
m.data = make(map[string]any)
}
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
return err
@ -481,7 +481,7 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
func (m *StrAnyMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.Map(value)
@ -489,13 +489,13 @@ func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *StrAnyMap) DeepCopy() interface{} {
func (m *StrAnyMap) DeepCopy() any {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[k] = deepcopy.Copy(v)
}

View File

@ -71,11 +71,11 @@ func (m *StrIntMap) Map() map[string]int {
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrIntMap) MapStrAny() map[string]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *StrIntMap) MapStrAny() map[string]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
@ -457,7 +457,7 @@ func (m *StrIntMap) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
func (m *StrIntMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
@ -475,7 +475,7 @@ func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
}
// DeepCopy implements interface for deep copy of current type.
func (m *StrIntMap) DeepCopy() interface{} {
func (m *StrIntMap) DeepCopy() any {
if m == nil {
return nil
}

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