Compare commits

...

514 Commits

Author SHA1 Message Date
da2ec21571 fix issue 2020-10-27 10:41:18 +08:00
ebb8d8a2f7 fix issue in 2020-10-27 10:40:47 +08:00
9706a9c768 version updates 2020-10-26 21:20:34 +08:00
cee67a8d4e improve urlencoding handling for parameters posted along with file uploading 2020-10-26 20:21:09 +08:00
87650557fd remove debugging codes from package gtime 2020-10-26 19:06:27 +08:00
d3bf52f12f fix issue in unit testing case for package gi18n 2020-10-26 19:00:11 +08:00
6b13a4849b improve package gi18n 2020-10-25 17:33:14 +08:00
8e380c0d9d improve package gtime/gconv for map converting 2020-10-25 11:33:30 +08:00
0caf4bfcec improve gconv.StructDeep 2020-10-25 10:47:47 +08:00
9c3b978b50 improve package ghttp and internal/structs 2020-10-22 15:16:31 +08:00
ab689a7792 improve gutil.Dump 2020-10-22 09:24:57 +08:00
846646d92d improve json validation rule for package gvalid 2020-10-22 09:11:38 +08:00
2eb2b89432 improve gconv.Struct* by doing the converting using json.Unmarshal if given params is json string/bytes 2020-10-21 14:09:16 +08:00
43441a8218 allow custom validation rule validate empty or nil values 2020-10-21 00:08:36 +08:00
561a541fa1 add custom CreatedAt/UpdatedAt/DeletedAt filed name configuration for package gdb 2020-10-20 21:01:39 +08:00
ffe9ecc141 improve package internal/empty 2020-10-20 14:07:01 +08:00
77f7884604 add function gutil.Try/g.Try;improve error string for gconv.Struct 2020-10-20 13:36:43 +08:00
0a203d1e22 fix issue in struct converting for ghttp.Request 2020-10-19 11:29:41 +08:00
f4f4550483 improve package gerror 2020-10-18 20:18:55 +08:00
e87226a092 improve package gerror 2020-10-18 11:29:09 +08:00
391a3ec9bd version update 2020-10-18 11:26:19 +08:00
dd5cd31ef5 add automatic data key to field name mapping feature for package gdb 2020-10-17 18:16:13 +08:00
de92e804fe Merge branch 'master' of https://github.com/gogf/gf 2020-10-17 17:17:27 +08:00
7725d9aaaf add Current/Next function for package gerror 2020-10-17 17:17:10 +08:00
bd3e25adea Merge pull request #946 from yuancjun/patch-1
Update ghttp_server_config.go
2020-10-15 20:41:07 +08:00
0b1d49af4b upgrade fsnotify to v1.4.9 2020-10-15 20:14:35 +08:00
7efa9e351e remove default initialization function and add lazy initialization feature for package gfsnotify 2020-10-15 20:07:21 +08:00
0b0141954b Update ghttp_server_config.go 2020-10-15 18:04:32 +08:00
82b531fbfb add domain support for domain of ghttp.Server 2020-10-14 21:18:11 +08:00
67fb626339 add configuration SessionCookieOutput for ghttp.Server 2020-10-14 20:52:26 +08:00
9044d5f05d fix issue in custom session id lost for ghttp.Request 2020-10-13 20:34:31 +08:00
47663aa1f1 fix issue of EOF in ghttp.Request when no data posted using form-data 2020-10-13 20:12:54 +08:00
63c0aab19c improve package gtime 2020-10-12 23:32:32 +08:00
261216f5e4 improve structure sql for package gdb 2020-10-12 23:22:56 +08:00
f88b799d67 add more unit testing case for package gdb 2020-10-11 22:21:03 +08:00
9c48dd172c add auto-json support for slice/struct attribute for data inserting of package gdb 2020-10-10 17:29:38 +08:00
09ce105eee improve gdb.Model.Fields/FieldsEx for package gdb 2020-10-10 14:00:10 +08:00
651aa895f8 improve function addWordBoundariesToNumbers for package gstr 2020-10-10 13:38:28 +08:00
0509e41198 recover number for unit testing case of package gconv 2020-10-10 00:04:55 +08:00
1b40d6a53a do tx.Rollback if there's panic in gdb.Transaction 2020-10-09 23:42:33 +08:00
f9189d48d1 remove type ValueFunc from package gcache 2020-10-09 23:36:39 +08:00
849874a247 improve adapter definition for package gcache 2020-10-09 20:59:49 +08:00
3f6510bae7 fix issue in gconv.Struct 2020-09-29 23:47:37 +08:00
a585a26c39 improve custom rule function type for package gvalid 2020-09-29 23:25:20 +08:00
9943966a86 improva function formatSql for package gdb 2020-09-29 22:53:44 +08:00
3617e51c01 improve gdb.Model.ScanList 2020-09-28 23:52:02 +08:00
c931032f08 comment update for gdb.Model.Unscoped 2020-09-27 23:37:40 +08:00
37b286eaa4 improve performance for gconv.Maps;comment update for package gcache 2020-09-27 22:35:02 +08:00
619287c273 improve cache feature for package gdb 2020-09-27 00:15:11 +08:00
aeb9b68298 improve adapter feature for package gcache 2020-09-26 22:44:07 +08:00
bae8f6315b comment updat 2020-09-26 21:13:09 +08:00
a6f70f8935 comment updat 2020-09-26 21:00:28 +08:00
acca6f4009 add adapter feature for package gcache 2020-09-26 20:47:29 +08:00
727fdd2391 improve unit testing case for association feature for package gdb 2020-09-25 08:33:22 +08:00
1d174e00c0 improve package internal/intlog 2020-09-24 23:46:19 +08:00
a5e3e2f5ba change g.SetDebug function to control the debugging information for framework 2020-09-24 23:40:44 +08:00
da43c2d52f improve example for package gview 2020-09-22 20:12:34 +08:00
262f27748c improve custom validation rule feature for package gvalid 2020-09-22 08:45:22 +08:00
a29aef7e1c improve package gvalid for custom rule 2020-09-21 23:51:30 +08:00
1671391195 comment updates 2020-09-21 22:56:48 +08:00
28b0d59c61 improve package gcfg/gjson for data loading 2020-09-21 21:30:58 +08:00
86433cef25 improve gtime.New, gconv.String 2020-09-18 23:59:49 +08:00
e96ccd5f71 v1.13.6 2020-09-17 23:49:03 +08:00
50a087bb3d Merge branch 'master' of https://github.com/gogf/gf 2020-09-17 23:41:23 +08:00
1ec049c52f improve gf server for routes check while starting 2020-09-17 23:27:10 +08:00
ec38805542 Merge pull request #908 from cai1111/patch-2
Update gdb_driver_mssql.go
2020-09-17 19:23:25 +08:00
4c8517d075 Update gdb_driver_mssql.go 2020-09-17 11:06:24 +08:00
edf06da6ea add struct which implements Interfaces function parameter support for gdb.Model.Insert/Save 2020-09-13 11:21:10 +08:00
eb43a2040e add ghttp.Client.RedirectLimit and gfile.ScanDirFileFunc 2020-09-09 22:26:46 +08:00
9d0ecc7d3e add more unit testing case for package gdb 2020-09-07 20:25:59 +08:00
ad943c5e02 improve gjson.New by using gconv.MapDeep for map/struct 2020-09-07 19:44:11 +08:00
bdb4fd0d25 donator updates 2020-09-07 19:10:04 +08:00
2440e05457 fix issue in limit operations for database driver oracle 2020-09-03 22:38:07 +08:00
1337c6c0d1 add sub query sql support for join functions 2020-09-03 21:57:58 +08:00
957689e07c up 2020-09-02 23:22:16 +08:00
3952d74f87 up 2020-09-02 21:36:21 +08:00
6dc4b81693 add max recursive depth for directory scanning for function gfile.Scan* 2020-09-02 21:25:02 +08:00
9cd953b7be improve function FieldsEx by filtering fields from custom fields specified by function Fields for package gdb 2020-09-02 20:37:02 +08:00
631810dda2 add function String for package gmap 2020-09-02 19:53:58 +08:00
8c12bc5506 change panic to internal logging for package glog 2020-09-01 21:22:19 +08:00
d4091a4826 improve some transaction operations by directly calling model operations, making their implements logic the same 2020-08-31 15:57:04 +08:00
a7c269886b improve support for dynamic database configurations in codes 2020-08-31 15:39:27 +08:00
f54593037b improve CURD functions for package gdb 2020-08-31 00:59:42 +08:00
0415cf6a08 fix issue in nil gtime attribute for model entity for package gdb 2020-08-31 00:39:12 +08:00
87c22d32b0 version updates 2020-08-26 14:49:53 +08:00
27dd15b403 improve initialization for logger of package glog 2020-08-26 14:37:02 +08:00
dd452c19ce add example for custom uploading file name for ghttp.Server 2020-08-24 23:05:30 +08:00
acc0846cf3 fix issue in unit testing case of package gvalid 2020-08-24 22:26:12 +08:00
5a6738841f travis ci update 2020-08-22 00:21:24 +08:00
bea451c9d6 improve date type support for package gdb; improve datetime string for gtime.StrToTime; improve function gtime.New for more types 2020-08-21 23:41:12 +08:00
676e904ec6 improve package gvalid 2020-08-21 00:00:00 +08:00
49aa5c61bc improve package gvalid 2020-08-20 23:37:02 +08:00
a2272b852c improve package gvalid 2020-08-20 23:28:58 +08:00
1fa77630f9 improve package gvalid 2020-08-20 23:28:10 +08:00
a841c4cc05 improve package gvalid 2020-08-20 23:25:36 +08:00
1874808e3b improve unit testing case for package gcache 2020-08-19 21:37:26 +08:00
149b67916b README updates 2020-08-19 21:10:40 +08:00
7fb6f58162 README updates 2020-08-19 21:09:25 +08:00
a309114a18 donator updates 2020-08-15 21:58:03 +08:00
35cbde9530 comment update for package gstr 2020-08-15 21:28:24 +08:00
b9211b182a comment update for function ghttp.Request. 2020-08-15 12:35:56 +08:00
81ec499ae9 Merge pull request #867 from pingyeaa/master
Solve the problem of invalid modification of request parameters
2020-08-15 12:33:24 +08:00
f2e276eabd add function Test_Params_Modify for ghttp_unit_param_test.go 2020-08-14 20:02:52 +08:00
f6dbaba1f8 add function ReloadParam for ghttp_request.go 2020-08-14 18:34:43 +08:00
65dcff052a add function ReloadParam for ghttp_request.go 2020-08-14 18:19:48 +08:00
e3861567c7 add function ReloadParam for ghttp_request.go 2020-08-14 16:22:37 +08:00
e89a20c725 add ReloadParam func to ghttp_request.go 2020-08-14 15:19:56 +08:00
a8acc6bd28 update unit testing case for package gdb 2020-08-14 00:51:22 +08:00
eb5efc735e improve unit testing case of ScanList for package gdb 2020-08-13 23:45:22 +08:00
737af527cd improve *Struct functions for ghttp.Request 2020-08-13 23:29:00 +08:00
875d2b7e63 donator updates 2020-08-13 19:25:51 +08:00
bf1cb0e1bd donator updates 2020-08-13 19:23:31 +08:00
2bfeb1b06c README updates 2020-08-13 18:59:13 +08:00
820e4302b7 improve function *Struct for ghttp.Request 2020-08-13 18:51:59 +08:00
84d761b418 add function AddWithRecover for package grpool 2020-08-12 23:53:05 +08:00
e5e27f2ac4 add function GetRouterMap for ghtt.Request 2020-08-12 21:01:17 +08:00
efca9b18a8 improve content type checks for package gjson 2020-08-12 20:53:47 +08:00
607821ecbc add function LoadContentType for package gjson 2020-08-12 20:38:48 +08:00
7cc1b239d4 add functions Update/UpdateExpire/GetExpire for package gcache 2020-08-12 20:13:13 +08:00
efa8de34da Merge pull request #860 from XWR940711/master
add func gcache.SetVar/gcache.SetExpire/gcacge.GetExpire.
2020-08-12 19:19:19 +08:00
bcf45e3c5a CI-glog失败重测 2020-08-12 10:32:40 +08:00
0a3cd1d2ab 修正方法代码.使用Benchmark测试. 2020-08-12 09:57:47 +08:00
3e621856c8 change the port range for unit testing cases of ghttp.Server 2020-08-12 09:10:50 +08:00
32e4d64ddb improve package gjson for automatic content type checking 2020-08-12 00:11:25 +08:00
46e45ca84b improve package gjson for automatic content type checking 2020-08-11 23:46:21 +08:00
fcb13bd8ee improve package gjson for automatic content type checking 2020-08-11 23:36:40 +08:00
eacad9b453 improve package gjson for automatic content type checking 2020-08-11 23:22:09 +08:00
ed479e2a13 improve package gjson for automatic content type checking 2020-08-11 23:20:22 +08:00
19937cb75d improve MapDeep for package gconv 2020-08-11 20:13:47 +08:00
a95093222c add GetRemoteIp for ghttp.Request; add *Var feature for ghttp.Client 2020-08-11 15:23:42 +08:00
7b599d1882 Merge branch 'master' of https://github.com/gogf/gf 2020-08-10 23:04:28 +08:00
4d501fd2f4 improve function Set for package gjson 2020-08-10 23:03:35 +08:00
c3d3053ded improve GetOrSetFunc for package gcache 2020-08-09 21:28:31 +08:00
14d5fd3e11 add translation format feature for package gi18n 2020-08-08 16:46:52 +08:00
9f79453334 improve ghttp.Server by closing the request and response body to release the file descriptor in time 2020-08-08 11:09:58 +08:00
c7f1c881c0 Merge pull request #850 from coderzhuang/master
fix: Access-Control-Request-Headers
2020-08-08 09:59:45 +08:00
c39c032b4e Merge pull request #844 from qinyuguang/scan
gcmd.Scan supports read line that contains whitespace
2020-08-08 09:58:32 +08:00
fa96c18308 add function MapOmitEmpty for package gutil 2020-08-08 09:53:32 +08:00
d90145a47f add goroutine id retrieving feature for package gdebug 2020-08-08 09:42:24 +08:00
4871f86346 adjust TestCache_Expire_SetVar Test Code. 2020-08-07 16:26:26 +08:00
919eaf1e9a adjust TestCache_Expire_SetVar Test Code. 2020-08-07 14:05:21 +08:00
8a84ca16d1 add func gcache.SetVar/gcache.SetExpire/gcacge.GetExpire.
add test TestCache_Expire_SetVar.
2020-08-07 13:24:41 +08:00
12b4fdd692 fix: Access-Control-Request-Headers 2020-08-04 11:57:42 +08:00
3d8451d5d0 comment update for gdb.Model.Page 2020-08-04 09:22:21 +08:00
cf88f28519 gcmd.Scanf supports read line that contains whitespace 2020-08-03 21:00:02 +08:00
15d99eee46 add more unit testing case for ghttp.Server 2020-08-03 20:49:19 +08:00
6d68277db8 improve cookie feature for ghttp.Server 2020-08-03 20:00:00 +08:00
3e3b5557f7 readme updates 2020-08-03 18:58:43 +08:00
ba1a9d9f8e gcmd.Scan supports read line that contains whitespace 2020-08-01 02:13:42 +08:00
3e1a7953ec Merge branch 'master' of https://github.com/gogf/gf 2020-07-31 09:57:45 +08:00
073fb2d717 improve sql logging structure for package gdb 2020-07-31 09:57:26 +08:00
7f33021184 Merge pull request #825 from chenall/gjson-support-utf8-with-bom
fix configfile with UTF8-BOM issue
2020-07-31 00:08:05 +08:00
b396096721 improve dry-run feature by adding global dry-run variable reading from environment or command options 2020-07-30 23:08:27 +08:00
0a5c6d832f add configration group name for logging content for package gdb 2020-07-30 23:00:20 +08:00
d279566114 Merge pull request #769 from fxk2006/master 2020-07-30 22:55:39 +08:00
2693cbb136 add more unit testing case for ghttp.Server 2020-07-30 22:45:50 +08:00
f7a9be4292 improve package glog for rotation feature 2020-07-30 21:09:45 +08:00
a926033b66 move Separator from const to variable for package gfile 2020-07-30 20:57:08 +08:00
dbcdd06b19 Merge branch 'master' of https://github.com/gogf/gf 2020-07-30 20:49:32 +08:00
ff5dab5c70 improve package glog for rotation feature 2020-07-30 20:49:11 +08:00
84b576418f Merge pull request #835 from XWR940711/master
add func gtimer.Entry.Reset()
2020-07-30 20:20:50 +08:00
253b124903 fix issue in database configuration for package gind 2020-07-30 20:18:18 +08:00
33dc5ddf79 fix issue in database configuration for package gind 2020-07-30 18:52:27 +08:00
a456fa537c fix logger configure issue for package gdb 2020-07-30 17:16:29 +08:00
9e1fb93e08 adjust gtimer.Entry.Reset() Test Code. 2020-07-30 11:00:19 +08:00
5d24f702be adjust gtimer.Entry.Reset() Test Code. 2020-07-30 10:37:48 +08:00
ca8e2c2986 version updates 2020-07-29 23:19:59 +08:00
2f0e5a45c5 improve package gcron 2020-07-29 23:03:31 +08:00
c4b28b0bc4 improve gtime.ParseDuration 2020-07-29 11:44:58 +08:00
6cc4747965 add func gtimer.Entry.Reset() 2020-07-29 11:22:46 +08:00
91cd34b26a improve multiple seperator chars handling for router of ghttp.Server 2020-07-29 00:32:54 +08:00
e35a2f028c add timeout feature for Do/Receive functions of package gredis 2020-07-28 23:48:12 +08:00
9cff2bd10f add function ParseDuration and time unit 'd' support for package gtime 2020-07-28 22:46:15 +08:00
6d406498db improve result data type converting for package gdb 2020-07-28 20:31:50 +08:00
939ae37baf example codes update for package gdb 2020-07-28 19:55:13 +08:00
4d6c8744e5 upgrade mxj 2020-07-25 15:31:51 +08:00
e252d8b740 add function ListItemValuesUnique for package gdb 2020-07-25 15:05:08 +08:00
9b8d63e21b improve graceful reload feature for ghttp.Server 2020-07-25 14:09:03 +08:00
437fc04620 improve testCfg_With_UTF8_BOM unit_test 2020-07-25 14:07:33 +08:00
04dee090a3 improve graceful reload feature for ghttp.Server 2020-07-25 13:50:04 +08:00
245c6d24a1 improve ghttp.Client 2020-07-25 11:24:35 +08:00
e92fd05f9f Merge pull request #815 from tikrgo/camelcaseConvert 2020-07-25 11:07:51 +08:00
937f8e6919 fix configfile with UTF8-BOM issue 2020-07-25 10:57:40 +08:00
f489e6273e fix issue 819 2020-07-25 10:54:48 +08:00
4f99bdbc87 Merge branch 'master' of https://github.com/gogf/gf into camelcaseConvert 2020-07-24 09:49:53 +08:00
1250b33220 将正则移到全局提高效率 2020-07-24 09:48:51 +08:00
b57aee4595 Merge pull request #800 from chenghonour/master
Add database method
2020-07-23 21:30:24 +08:00
d9da51933d improve gconv.Struct 2020-07-23 21:15:54 +08:00
9fca93e7d8 Merge branch 'master' of https://github.com/gogf/gf 2020-07-23 21:01:37 +08:00
854b2ed185 improve function convertValue for package gdb 2020-07-23 21:01:16 +08:00
cfac03bc40 Merge pull request #814 from xbkaishui/support-redis-tls
support redis tls
2020-07-22 19:49:29 +08:00
a3cb4a6ae8 驼峰转换下划线时,不连续大写字母首位加下划线规范 2020-07-22 16:15:06 +08:00
2798fa4444 revert unit test 2020-07-22 15:27:00 +08:00
8bac0614f5 format code 2020-07-22 15:13:40 +08:00
646280a6a9 remove tls unit test case 2020-07-22 15:08:32 +08:00
208bdffdf7 update comment 2020-07-22 14:02:21 +08:00
9e7291903f support redis tls 2020-07-22 13:28:45 +08:00
d56835fc00 go fmt 2020-07-21 13:28:25 +08:00
1d5e717a80 update err return 2020-07-21 12:40:13 +08:00
2f44d9ae18 add unit test 2020-07-21 12:37:04 +08:00
0627ab81d6 Merge pull request #810 from chenall/master
fix ghttp_client upload filename issue #809
2020-07-21 10:52:39 +08:00
8167a398fc add function GetHeader for ghttp.Request 2020-07-21 10:17:31 +08:00
6291751014 fix ghttp_client upload filename issue #809 2020-07-21 09:57:13 +08:00
ee5ddaab52 fix place holder for pgsql 2020-07-20 22:47:12 +08:00
207476be1f Merge pull request #807 from tiancai45/master
fix issue pq: syntax error at or near $
2020-07-20 22:15:20 +08:00
b9b470c2ae Merge branch 'master' into master 2020-07-20 22:14:29 +08:00
52b6e8ef9d fix place holder for mssql 2020-07-20 21:55:34 +08:00
48c84bf74a fix place holder for mssql 2020-07-20 21:48:44 +08:00
5be30b3684 fix issue in logging file rotation 2020-07-20 21:32:28 +08:00
54a2b13825 Merge pull request #802 from csrgxtu/bug/log-file-rotate
bug/log-file-rotate: fix big file even with rotate-by-size and rotate…
2020-07-20 21:30:52 +08:00
d44ddae3dc debug postgre 2020-07-20 19:13:15 +08:00
7bbc2459ba postgre 报错 pq: syntax error at or near $ 2020-07-19 23:24:35 +08:00
0cb77caa2a Merge pull request #803 from housemecn/master
update tencent site
2020-07-18 10:40:18 +08:00
3f5f76458d Update README_ZH.MD
update tencent site
2020-07-18 10:36:48 +08:00
835e07e8de Update README.MD
update tencent site
2020-07-18 10:34:32 +08:00
3fc5e43abe readme update 2020-07-18 10:21:17 +08:00
9c8cb26bd6 readme update 2020-07-18 10:17:05 +08:00
540f4d2d0c donation update 2020-07-18 10:06:26 +08:00
51be255821 remove gfcache usage from repo 2020-07-18 09:44:15 +08:00
7d278fea25 users update in readme 2020-07-18 09:40:39 +08:00
ca72d3b23a remove gfcache usage from repo 2020-07-18 08:32:35 +08:00
534cd3be1c add table field method 2020-07-17 14:28:50 +08:00
78536de1b5 add database method 2020-07-17 11:28:47 +08:00
edc67d9ec3 bug/log-file-rotate: fix big file even with rotate-by-size and rotate-back-expire 2020-07-17 10:39:14 +08:00
4dd12434b7 add file cache feature for package gfile and remove package gfcache 2020-07-16 12:31:13 +08:00
f654bb2eda comment update for package gtree 2020-07-16 11:46:11 +08:00
205f98cfeb improve gconv.Struct for interface UnmarshalValue 2020-07-15 23:30:07 +08:00
69fa5bf464 improve package gtime for nil gtime.Time object 2020-07-15 20:37:13 +08:00
e7dc58ac6c unit testing case update for package gdb 2020-07-15 20:21:04 +08:00
2033299632 improve placeholder for pgsql 2020-07-15 20:15:09 +08:00
639d34d5d9 fix issue in incorrect debug sql information for package gdb 2020-07-15 20:10:54 +08:00
605181da32 fix issue in gins.Database 2020-07-15 19:17:42 +08:00
183f631190 improve Model.One for package gdb 2020-07-15 09:15:03 +08:00
64c99b9871 add configuration updating feature for package gcfg 2020-07-15 00:07:07 +08:00
d29b0a27ff Merge pull request #760 from kirileec/master
add a SetProxy function and a chaining function Proxy for ghttp.Clien…
2020-07-14 23:09:06 +08:00
aaa726e6dc add limit 1 for function One of package gdb 2020-07-14 21:42:28 +08:00
05cc0c4644 improve gconv.Struct for nil attribute interface 2020-07-14 21:34:29 +08:00
e1c0a92e60 improve the performance when converting struct that implements UnmarshalValue 2020-07-13 23:51:36 +08:00
c770e4779a Merge pull request #758 from wnstar/log
log added function name display
2020-07-13 23:21:49 +08:00
c135122ca1 improve package gconv for detailed handling of interface attributes 2020-07-13 23:13:50 +08:00
1d87df2afe add more unit testing case for package ghttp 2020-07-13 22:45:20 +08:00
8efc0ca0ea comment update for package gvalid 2020-07-12 10:33:24 +08:00
001d524ff7 readme update 2020-07-12 10:25:15 +08:00
5c5dce9dc3 comment update for admin controller of package ghttp 2020-07-12 09:56:07 +08:00
293256c2ca improve package gmode 2020-07-12 09:34:43 +08:00
4e027c1de3 improve argument handling for empty slice for package gdb 2020-07-11 09:53:16 +08:00
6712a33164 fix issue 785 in function Unique for packge garray 2020-07-10 22:26:02 +08:00
98531143a6 donator updates 2020-07-10 21:32:53 +08:00
71127ba308 improve package gins 2020-07-10 00:20:46 +08:00
0f6f571ccb remove the space trimming of the validation value for package gvalid 2020-07-08 20:53:56 +08:00
9b6936a4fb deprecate function DB.Table; improve the configuration node name case-insensitive and ignoring special chars 2020-07-08 20:48:29 +08:00
8b78609412 add more unit testing case for package gdb 2020-07-08 19:12:48 +08:00
4a31082f8c improve function Struct/StructDeep for package gconv 2020-07-08 11:32:35 +08:00
3643a69d8d improve function Struct/StructDeep for package gconv 2020-07-08 10:52:45 +08:00
adbaa4aa89 donator updates 2020-07-07 22:06:35 +08:00
0e025eda1b improve function Struct for package gconv 2020-07-06 22:37:37 +08:00
7c44bf8e94 Merge branch 'develop' of https://github.com/gogf/gf into develop 2020-07-06 21:18:51 +08:00
e6718f1113 improve function Struct for package gconv 2020-07-06 21:18:25 +08:00
f3f6adb03a read Client's timeout for net.Dialer
make sure Client.Transport is *http.Transport
2020-07-05 20:52:33 +08:00
977827e453 add examples for SetProxy function and Proxy chaining function 2020-07-05 20:52:33 +08:00
bcc9153991 add a SetProxy function and a chaining function Proxy for ghttp.Client to do HTTP request via proxy. #285 2020-07-05 20:52:32 +08:00
c04be14cd9 add custom validation rule feature for package gvalid 2020-07-05 18:55:38 +08:00
de8f29751d improve function ScanList for package gdb 2020-07-05 12:17:56 +08:00
12d58e4d08 add ScanList feature for gdb.Model 2020-07-05 11:54:37 +08:00
3ae44185f4 add ScanList feature for gdb.Result 2020-07-05 11:23:39 +08:00
1290f42f75 improving package gdb 2020-07-04 08:50:52 +08:00
17b91dcad7 add ListItemValues/ListItemValuesUnique functions and associated unit testing cases for package gutil/gvar 2020-07-02 23:40:34 +08:00
72da1642ee add more unit testing case for ghttp.Client 2020-07-02 19:12:01 +08:00
76d93b3a61 donator updates 2020-07-01 23:05:39 +08:00
7be0ee9566 donator updates 2020-07-01 23:02:07 +08:00
326e1f8da5 donator updates 2020-07-01 22:53:34 +08:00
e4e44ddd19 revert gvar.Var from interface to struct 2020-06-29 17:23:49 +08:00
46bdde9265 revert gvar.Var from interface to struct 2020-06-29 13:40:19 +08:00
09eba58927 add more unit testing case for ghttp.Client 2020-06-28 23:23:09 +08:00
0e884c78f5 fix issue of https://github.com/gogf/gf/issues/765 2020-06-28 23:03:41 +08:00
2f44721086 fix issue in reload feature in ghttp.Server 2020-06-28 20:52:33 +08:00
6e08eebcbe 修改sql打印debug信息,增加数据库配置的group名称,用于区分sql来源,尤其是多数据库配置的时候 2020-06-28 16:58:43 +08:00
d9422d00ac 修改sql打印debug信息,增加数据库配置的group名称,用于区分sql来源,尤其是多数据库配置的时候 2020-06-28 16:50:13 +08:00
efc0501548 Merge branch 'master' of https://github.com/gogf/gf 2020-06-22 21:56:29 +08:00
bb07c60838 rename CopySlice to SliceCopy for package gutil 2020-06-22 21:56:10 +08:00
330ea05ca3 Merge pull request #755 from wnstar/master
add SetRedirectLimit function to do HTTP client. #714
2020-06-22 21:21:05 +08:00
1f534b48a3 Merge pull request #757 from leoleoasd/patch-1
Fix typo in git ignore
2020-06-22 20:30:35 +08:00
aceae5eee3 add todo for package gpool 2020-06-22 20:08:27 +08:00
d4d66fd529 improve package gtime 2020-06-22 19:46:39 +08:00
b7686d6d37 log added function name display 2020-06-22 11:04:57 +08:00
Leo
b97e397354 Fix typo in gitignore 2020-06-22 09:41:02 +08:00
ab8fbf171e add SetRedirectLimit function to client 2020-06-21 19:17:39 +08:00
2c0cfa24b0 fix issue in package gjson for struct to map converting 2020-06-18 20:40:24 +08:00
f28a76d470 merge develop 2020-06-17 23:59:20 +08:00
f3525c84a3 fix issue in debug infor for soft deleting feature of package gdb 2020-06-17 23:47:47 +08:00
abe9b54112 embarrassing jsoniter of another incompatibility with stdlib json 2020-06-17 21:43:51 +08:00
b2aa59d893 import third-party library json-iterator to improve the performance for json operations 2020-06-17 21:16:25 +08:00
9e9865afa7 author updates 2020-06-17 18:58:24 +08:00
ecc439d4e8 author update 2020-06-17 14:44:16 +08:00
452f0fc99e add Scan/ScanDeep for package gvar 2020-06-17 11:40:37 +08:00
54f47845f6 entity feature developing for package gdb 2020-06-17 11:37:45 +08:00
cb238cd6d9 add todo for gconv.Convert 2020-06-16 20:44:57 +08:00
897a9f4584 Merge branch 'develop' 2020-06-16 20:42:26 +08:00
55f9b121de Merge pull request #738 from fxk2006/fix_SessionRedis 2020-06-16 20:41:53 +08:00
386f38af5e improve gconv.Struct 2020-06-16 20:18:40 +08:00
65cea430c2 improva gconv.Struct 2020-06-16 19:30:35 +08:00
4d38b508a3 improve gvar by changig gvar.Var from type struct to interface 2020-06-16 17:38:05 +08:00
5c774fd391 improve function Structs for package gconv 2020-06-16 11:37:00 +08:00
f6d760e90f 1、修复redis存储session时,最大过期时间问题: ttl.Seconds()返回float64在redis中报错 2020-06-16 11:19:08 +08:00
c3ffa40bad improve Response/ResponseWriter by implementing http.Flusher for package ghttp 2020-06-15 23:36:20 +08:00
5ce5d0e593 improve auto marshal for struct slice for package gredis 2020-06-15 23:19:38 +08:00
b83f1efde8 remove panic for stdout error for package glog 2020-06-15 22:57:27 +08:00
ca5f14c366 comment updates for package gtcp 2020-06-15 22:51:44 +08:00
95a8b51fb4 improve map slice converting for package gconv 2020-06-15 18:59:18 +08:00
2b64979730 Merge branch 'master' of https://github.com/gogf/gf 2020-06-15 16:48:17 +08:00
508cb7db88 add and improve Scan/ScanDeep feature for package gdb/gvar/gjson/gconv 2020-06-15 16:46:48 +08:00
e8d6d96883 fix issue in error field name lost for package gvalid 2020-06-14 17:28:48 +08:00
9378a6e7bf readme update 2020-06-13 16:22:49 +08:00
1d609fc5c7 fix issue in unit testing case for package ghttp 2020-06-13 14:52:36 +08:00
49d1e3dbaf fix issue of attribute name lost in package gvalid 2020-06-12 22:25:58 +08:00
b0e9334852 go.mod updates 2020-06-12 14:10:33 +08:00
95bd369959 improve function GetClientIp for ghttp.Request 2020-06-11 18:47:56 +08:00
a433890097 Merge pull request #721 from kuiye/master
add get client realIp
2020-06-11 18:45:42 +08:00
c9c3be517c fix issue in example of package glist 2020-06-11 18:43:35 +08:00
89e122cd31 marge getip func 2020-06-11 09:49:16 +08:00
27c2a03ea8 add example for package gset 2020-06-10 23:07:07 +08:00
b56eb3a948 example update for package garray 2020-06-10 19:50:22 +08:00
fb1b0bfd88 example update for package g/garray/gmap/gset/ghttp 2020-06-10 19:47:25 +08:00
dd10167ec2 example updates for package garray 2020-06-10 19:09:14 +08:00
3138ad10ec add get client realIp 2020-06-10 10:44:41 +08:00
080ca82605 release updates 2020-06-09 21:29:28 +08:00
0290de2360 improve dump feature for ghttp.Request 2020-06-09 20:46:04 +08:00
9b330adc1d Merge pull request #718 from sanrentai/master
improve mssql TableFields func
2020-06-09 19:04:56 +08:00
f3ff1ae08b improve example for package gmap 2020-06-09 16:24:54 +08:00
4f699af051 improve pointer type convertion for package gconv 2020-06-09 14:19:23 +08:00
7ebc7259cb improve mssql TableFields func 2020-06-09 14:02:43 +08:00
9d22edbdb8 donator updates 2020-06-08 23:08:58 +08:00
7e95058cb5 donator updates 2020-06-08 22:34:48 +08:00
f33753e0cd add example for package ghttp/glog 2020-06-08 20:21:35 +08:00
ac71e1d753 rename Dump to RawDump for ghttp.ClientResponse 2020-06-08 19:26:14 +08:00
8151b6efd6 add more example for package gredis 2020-06-08 19:17:24 +08:00
94de306c93 add zero time filtering for package gdb 2020-06-08 13:46:45 +08:00
51f8ea26de add example for package gredis 2020-06-06 18:12:42 +08:00
29d9bb17cd improve function Unscoped for package gdb 2020-06-06 16:28:16 +08:00
30501a882d improve function Struct for package gconv 2020-06-06 15:31:04 +08:00
f2f98e1d16 rename testing file names for package gdb 2020-06-06 14:38:05 +08:00
cbf465eeea improve body interface implements for ghttp.Reqest/Response 2020-06-06 13:34:58 +08:00
79c400e912 Merge branch 'master' of https://github.com/gogf/gf 2020-06-06 10:10:05 +08:00
cc4c49b5b0 add unit testing case for pointer attribute mapping for function Parse 2020-06-06 10:09:19 +08:00
96ea2c911d Merge pull request #696 from sanrentai/master
the sqlserver type money  should be converted to float64
2020-06-05 21:08:49 +08:00
131b11680a readme update 2020-06-05 20:36:05 +08:00
3c6ab96283 fix issue in function Walk and add more example for package gset 2020-06-04 21:46:22 +08:00
d2c4fa921a add more example for package gmap 2020-06-04 20:45:18 +08:00
dbb51a9328 improve package glist 2020-06-04 20:13:33 +08:00
55bfc3fa73 add more examples for package garray 2020-06-04 18:28:33 +08:00
d9c5182d1a gitignore updates 2020-06-04 17:56:32 +08:00
899fcbf2da add mor example for package garray 2020-06-04 17:55:43 +08:00
6e2c0d8706 comment and readme update 2020-06-04 17:29:16 +08:00
c4e599ce63 improve random bytes buffer hanlding for package grand 2020-06-04 14:45:56 +08:00
9cad9e4467 add more unit testing cases for package gstr 2020-06-04 00:03:30 +08:00
48019444ea README and DONATOR update 2020-06-03 23:49:12 +08:00
76d31a7fbb improve slice result handling by treating it as string slice for package gredis 2020-06-03 23:44:21 +08:00
facb9d93c0 fix issue of multiple slice arguments handling in function where 2020-06-03 21:36:16 +08:00
1d6bd46c5e README updates 2020-06-03 20:33:21 +08:00
8646dc1825 Merge pull request #702 from misitebao/master
更新 readme 中文版
2020-06-03 18:49:35 +08:00
2992bdeed7 修改之前的捐赠名称 2020-06-03 11:38:21 +08:00
c5601e6c88 更新 readme 中文版 2020-06-03 10:59:20 +08:00
ef1d9a561c improve the route feature for ghttp.Server 2020-06-03 00:09:51 +08:00
2d3b32c94a money 类型转换错误 2020-05-29 15:41:37 +08:00
269378aa0d fix issue in unit testing case for package gsession; version updates 2020-05-28 20:28:07 +08:00
6889542801 improve package guid 2020-05-27 11:26:05 +08:00
8e6f1f1740 improve performance of random buffer usage for package grand 2020-05-25 23:39:09 +08:00
b6ab1a992c improve empty checks for common interfaces implementer 2020-05-25 14:26:08 +08:00
a5a267567c comment update for package garray 2020-05-22 12:04:58 +08:00
f05f855c07 improve default max header bytes from 1KB to 10KB for ghttp.Server 2020-05-21 20:10:38 +08:00
bc5f773ba6 make WriteTimeout default to 0 for ghttp.Server 2020-05-19 19:16:47 +08:00
55308cc37c improve package gipv4 by formating the value retrieving fuctions to Get* 2020-05-18 23:50:31 +08:00
4d9db6edf0 improve package gconv for map converting 2020-05-18 20:58:19 +08:00
38111a64e3 improve files listing for ghttp.Server 2020-05-18 20:09:00 +08:00
bd8d3fca08 improve domain rule for package gvalid 2020-05-18 19:44:15 +08:00
89ccaa3915 improve function Do for package gredis, adding auto marshal feature for arguments 2020-05-18 19:18:53 +08:00
7c2cff7d99 add function GetVar for package gcache 2020-05-17 18:16:26 +08:00
788e15dbb6 comment update for package guid 2020-05-17 15:40:24 +08:00
a0172d9d7e README updates 2020-05-17 15:16:13 +08:00
bc1a7a1644 README updates 2020-05-17 15:11:07 +08:00
c98234d3e6 improve session feature by allowing custom session id creating function for ghttp.Server 2020-05-17 14:33:27 +08:00
45a94d23d5 improve session feature by allowing custom session id creating function for ghttp.Server 2020-05-17 14:33:21 +08:00
351de5ee6a fix concurrent issue in buffer handling of package grand 2020-05-17 12:46:28 +08:00
3559436d1e improve package guid 2020-05-17 08:50:37 +08:00
189f4c1637 improve package gsession 2020-05-16 22:08:16 +08:00
caead810e2 add package guid; improve performance of package grand 2020-05-16 21:56:31 +08:00
ebdc5d9c9d comment update for package grand 2020-05-16 16:08:30 +08:00
4164059211 add function B for package grand; improve package grand 2020-05-16 16:05:31 +08:00
bd27258c46 donator updates 2020-05-16 15:43:16 +08:00
ddf0605bc4 go mod tidy 2020-05-16 15:36:04 +08:00
991dbe50e0 add more functions for package gipv4; move package guuid to standalone package of gogf 2020-05-16 15:35:21 +08:00
8050efb835 improve package gfile 2020-05-16 14:06:35 +08:00
9355bc73a2 improve package gvalid 2020-05-16 13:31:24 +08:00
acc2a6a353 improve copy feature for package gfile 2020-05-16 00:50:01 +08:00
09e83e7b8d improve version functions for package gstr 2020-05-16 00:12:23 +08:00
c0df8a3d80 improve version functions for package gstr 2020-05-16 00:11:08 +08:00
33e890d225 improve version functions for package gstr 2020-05-16 00:10:12 +08:00
750e5df962 improve gstr.CompareVersion 2020-05-15 23:42:17 +08:00
74c65439fc add function ScanDirFunc for package gfile 2020-05-15 21:51:03 +08:00
f290bd7170 fix issue in http.File implements for package gres 2020-05-14 21:25:54 +08:00
d398b749d4 improve support for types of database pgsql 2020-05-14 21:15:44 +08:00
2fd5a1574a improve package gres 2020-05-14 20:32:01 +08:00
a7504f0629 add more unit testing cases for package gdb 2020-05-13 23:21:11 +08:00
3c35bb85ee improve unit testing cases for package gi18n/ghttp 2020-05-13 19:35:56 +08:00
6621edb04c improve i18n of chinese for package gvalid 2020-05-13 19:10:38 +08:00
b0c722e297 improve package gvalid 2020-05-10 22:34:16 +08:00
47a6284274 improve package gvalid 2020-05-10 22:32:10 +08:00
150b8edb70 improve package gvalid 2020-05-10 17:49:23 +08:00
aa5d3285eb improve package gvalid 2020-05-10 16:48:00 +08:00
993d6e3076 improve package gvalid 2020-05-10 10:56:11 +08:00
78ad9d8c2d improve error message of package gvalid 2020-05-10 09:05:52 +08:00
feff3ddce3 comment update for package ghttp 2020-05-09 19:19:42 +08:00
80fddad64d fix issue in PopRight function for package garray; improve json.Marshal for package garray 2020-05-08 21:08:06 +08:00
1e6dd0be02 readme updates 2020-05-08 19:16:52 +08:00
22ecf4f1b6 add context feature for package glog 2020-05-08 17:12:37 +08:00
5d21148657 comment updates for package ghttp 2020-05-07 23:05:33 +08:00
edb745ed92 version updates 2020-05-07 11:31:34 +08:00
61f4e0da6f improve package gcache 2020-05-06 21:09:02 +08:00
767afa3106 improve package gcache 2020-05-06 19:06:49 +08:00
16779359ee remove package gchan 2020-05-06 18:43:19 +08:00
770653fafa enable the route dump for unit testing of logging feature for ghttp.Server 2020-05-05 00:09:39 +08:00
0c021b6da7 add unit testing case of status handler feature for ghttp.Server 2020-05-05 00:01:09 +08:00
ec92b08f25 add function Maps for package gvar; add function GetMaps for package gjson; improve MapToMap* functions for package gconv 2020-05-04 23:42:51 +08:00
13e2353729 fix lock issue in function search for package garray 2020-05-03 23:08:18 +08:00
90d19a84ce improve package garray/gset, adding function ContainsI 2020-05-03 22:52:04 +08:00
b4c0b95d8a add support for old version of gres 2020-05-03 22:16:12 +08:00
b7e8255066 add support for old version of gre 2020-05-03 22:08:08 +08:00
5f8e6ad9ed comment update for package ghttp 2020-05-01 03:31:04 +08:00
c8d253eb56 change binary content from hex string to base64 string for package gres 2020-05-01 02:35:24 +08:00
4814624cff change binary content from hex string to base64 string for package gres 2020-05-01 02:16:42 +08:00
cc67f3d388 improve package gcompress 2020-05-01 01:47:02 +08:00
f7c2a51c9f fix issue in zip feature for package gcompress; improve package gres 2020-05-01 00:18:45 +08:00
3db83e1159 improve package gtimer 2020-04-30 22:22:35 +08:00
3bb002796c donator update 2020-04-30 20:46:27 +08:00
45170bc53e add ClientMaxBodySize configuration for ghttp.Server 2020-04-30 20:37:09 +08:00
b79ff84c6f add struct slice conversion for request parameters for ghttp.Request; improve package gconv 2020-04-30 16:53:47 +08:00
938c46fec9 readme update 2020-04-29 19:33:14 +08:00
8a13d94526 improve Record.Struct for package gdb 2020-04-29 09:12:13 +08:00
1e844d505a comment update for package ghttp/gconv; readme update 2020-04-29 00:14:29 +08:00
a123a2c086 improve struct conversion of empty result/record for package gdb 2020-04-28 21:03:25 +08:00
1eeeeb853e improve unit testing case of error logging for ghttp.Server 2020-04-28 15:21:17 +08:00
6e7224e306 improve error handling for gconv.Struct/ghttp.Server; add NewSkip/NewfSkip function for package gerror 2020-04-28 15:04:07 +08:00
9e064e2651 donator updates 2020-04-27 23:34:22 +08:00
8d9dd17eac add Walk function for package gset; improve fields handling feature for package gdb 2020-04-27 21:18:42 +08:00
cf1d3d3d2b improve package gdebug; add more unit testing case for package gdb 2020-04-27 17:56:04 +08:00
9480ffcdc0 improve function SetPath/AddPath for package gview 2020-04-27 17:07:00 +08:00
5db10add4a fix issue in unnacessary quoting of fields in select statement of gdb.Model 2020-04-27 16:30:53 +08:00
fa66bf5d9d improve cache feature of package gdb.Model 2020-04-26 21:31:55 +08:00
f69da3ace1 add function Transaction for package gdb 2020-04-26 17:47:19 +08:00
e01bfa05c3 listening ports change for unit testing cases of ghttp.Server 2020-04-26 17:13:48 +08:00
231238c157 improve parameter handing for ghttp.Request 2020-04-26 17:08:07 +08:00
7edec099ab improve raw request/response content dump for ghttp.Client 2020-04-24 00:00:52 +08:00
83eb8be064 Merge pull request #609 from kirileec/master
add Raw* method in ClientResponse to get request and response string
2020-04-23 22:58:04 +08:00
7af30df494 Merge branch 'master' into master 2020-04-23 22:57:23 +08:00
f026686fda fix issue in dupicated expiration handling in response cookies og ghttp.Client 2020-04-23 21:12:32 +08:00
35a50b9c6c readme update 2020-04-23 21:06:42 +08:00
010e2f951a downgrade the required golang version from v1.13 to v1.11 2020-04-23 20:38:25 +08:00
f7f86ad65a downgrade the required golang version from v1.13 to v1.11 2020-04-23 20:25:59 +08:00
1e19f447d1 improve ghttp.Client in context handling 2020-04-23 20:23:23 +08:00
8cc378331d add one unit testing case for ghttp.Server 2020-04-23 20:10:10 +08:00
4721f68fd8 add performance testing result 2020-04-23 19:51:08 +08:00
0d11c0a1f8 add performance testing result 2020-04-23 19:41:34 +08:00
5076613a8f add performance testing result 2020-04-23 17:25:11 +08:00
c5a44daa65 add performance testing result 2020-04-23 17:23:57 +08:00
520970b71f add performance testing result 2020-04-23 17:19:08 +08:00
ebfb08ee3f add performance testing result 2020-04-23 17:18:15 +08:00
9e38b2cb90 add performance testing result 2020-04-23 17:14:11 +08:00
71b1f00dc5 improve package gdb/gstr/gvalid 2020-04-20 22:36:28 +08:00
59d6830ab1 version update 2020-04-18 13:42:06 +08:00
8779a3f211 add function Walk for package garray; improve comment for package garray 2020-04-18 13:30:49 +08:00
c10149baa0 fix issue in stack trace for package gdebug; improve package gsmtp 2020-04-18 10:17:54 +08:00
4f87668780 improve retry feature for ghttp.Client 2020-04-16 15:43:21 +08:00
63f33d1d8c improve package gdb supporting gtime.Time parameter for Where condition 2020-04-15 18:02:32 +08:00
734aa5a6fe improve create_at,update_at,delete_at feature for package gdb 2020-04-15 12:56:41 +08:00
371aef224d donator updates 2020-04-15 11:41:44 +08:00
362e380ada improve SetConfigWithMap function for package ghttp/glog/gview; reveal some logger functions for default logger of package glog 2020-04-15 11:04:39 +08:00
e995bd8c9a add unit testing cases of https feature for ghttp.Server 2020-04-15 09:55:44 +08:00
81b211dd1a improve select operation details handling for package gdb 2020-04-15 09:37:46 +08:00
0515fc94cb add MapMerge/MapMargeCopy functions for package gutil; improve template view feature for indefinite parameters 2020-04-14 21:11:12 +08:00
46ee070f0a fix issue in package gfsnotify when gset changes 2020-04-14 00:20:39 +08:00
b17e3a6804 improve package gset 2020-04-13 23:44:43 +08:00
9160bee1af change comments 2020-04-11 12:16:53 +08:00
8e6018cfff improve soft deleting feature for package gdb 2020-04-11 10:14:49 +08:00
c08739b5a3 add function Having for gdb.Model 2020-04-11 10:11:52 +08:00
385a503d31 add soft deleting feature, improve chars handling for package gdb 2020-04-11 09:09:25 +08:00
ef286b0c15 add unit testing case for auto time and soft deleting features for package gdb 2020-04-09 22:48:21 +08:00
53aba2d4b8 add unit testing case of cache feature for package gdb 2020-04-09 22:00:02 +08:00
f22da4ba3a improve gconv.MapDeep with anonymous checks for attribute struct field 2020-04-09 15:34:23 +08:00
23c2f12672 improve MapDeep function for package gconv; improve gjson.New function for loading struct parameter 2020-04-09 13:37:27 +08:00
7fd53673ce add automatic time and soft deleting features: create_at/update_at/delete_at for package gdb; improve schema changing feature for package gdb 2020-04-08 21:26:14 +08:00
e64fd088b9 fix when no response 2020-04-08 20:11:06 +08:00
15672e7a09 #591 add Raw* method in ClientResponse to get request and response string 2020-04-08 19:24:03 +08:00
2ea1d2c7b2 update build-in template function substr/strlimit for package gview 2020-04-07 23:53:17 +08:00
90d751f98d add function SubStrRune/StrLimitRune/PosRune/PosIRune/PosRRune/PosRIRune for package gstr; remove unicode support in function SubStr/StrLimit for package gstr 2020-04-07 23:51:48 +08:00
25a91c732c improve String/Map converting for package gconv 2020-04-07 21:29:41 +08:00
01995f5501 improve function Marge for package garray using interface feature; improve slice converting using interface feature for package gconv 2020-04-07 21:25:52 +08:00
68abb3cf3d improve package glist for var initilalization feature 2020-04-07 20:58:58 +08:00
77cc323d0e improve package gset/gmap for initialization 2020-04-07 20:41:49 +08:00
c7a9c03495 improve package garray/gmap for initialization 2020-04-07 20:06:26 +08:00
f7850e3ed3 improve package garray 2020-04-07 11:29:42 +08:00
82125416a2 comment update for package ghttp 2020-04-06 22:31:45 +08:00
2c1e2155e3 improve function Server.Handler for package ghttp 2020-04-06 20:29:35 +08:00
2d30a53c3a remove function From for package gdb; add function g.Table; add ServeHTTP interface implements for ghttp.Server 2020-04-04 22:46:52 +08:00
ccceeae29c change third party package for yaml parsing to gopkg.in/yaml.v3 2020-04-03 19:51:33 +08:00
f22b98456f add default value of MaxActive configuration for package gredis 2020-04-03 15:18:35 +08:00
e7f1bd692b remove Content-Type header set in Response.WriteStatus for package ghttp 2020-04-03 14:16:26 +08:00
f82f7ab199 change default value of safe from false to true for gdb.Model 2020-04-03 10:16:57 +08:00
4d33b527b6 readme update 2020-04-03 09:32:04 +08:00
7bcc596308 add GetVar function for package genv; add DryRun feature for package gdb 2020-04-02 20:52:37 +08:00
be2d4b080e Merge pull request #601 from bdliyq/master
Get ghttp a chance to add custom host.
2020-04-01 19:55:25 +08:00
c96e5f5a9e readme update 2020-04-01 18:08:20 +08:00
3c23766674 readme update 2020-04-01 18:05:17 +08:00
718089fc11 Get ghttp a chance to add custom host. 2020-03-31 23:28:21 +08:00
8ab44dcb44 Get ghttp a chance to add custom host. 2020-03-31 21:50:15 +08:00
490 changed files with 24385 additions and 8465 deletions

View File

@ -512,16 +512,6 @@ func mapToStruct() {
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
}
}
func main() {
db.PingMaster()

View File

@ -1,8 +1,14 @@
# MySQL数据库配置
# MySQL.
[database]
# debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
MaxOpen = 100
# Redis.
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
#[database]
# [[database.default]]

View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
)
@ -11,11 +10,19 @@ func main() {
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
r, e := db.Table("test").Order("id asc").All()
r, e := db.GetAll("SELECT * from `user` where id in(?)", g.Slice{})
if e != nil {
fmt.Println(e)
}
if r != nil {
fmt.Println(r.List())
fmt.Println(r)
}
return
//r, e := db.Table("user").Where("id in(?)", g.Slice{}).All()
//if e != nil {
// fmt.Println(e)
//}
//if r != nil {
// fmt.Println(r.List())
//}
}

View File

@ -3,6 +3,7 @@ package main
import (
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/util/gutil"
"time"
)
func main() {
@ -10,7 +11,7 @@ func main() {
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "123456",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
@ -20,19 +21,20 @@ func main() {
if err != nil {
panic(err)
}
//db.GetCache().SetAdapter(adapter.NewRedis(g.Redis()))
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
// 执行2次查询并将查询结果缓存3秒并可执行缓存名称(可选)
for i := 0; i < 2; i++ {
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
for i := 0; i < 3; i++ {
r, _ := db.Table("user").Cache(3000*time.Second).Where("id=?", 1).One()
gutil.Dump(r.Map())
}
// 执行更新操作,并清理指定名称的查询缓存
db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("uid=?", 1).Update()
//db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("id=?", 1).Update()
// 再次执行查询,启用查询缓存特性
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
//r, _ := db.Table("user").Cache(300000*time.Second, "vip-user").Where("id=?", 1).One()
//gutil.Dump(r.Map())
}

View File

@ -8,9 +8,6 @@ import (
func main() {
db := g.DB()
db.SetMaxIdleConnCount(10)
db.SetMaxOpenConnCount(10)
db.SetMaxConnLifetime(time.Minute)
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
@ -19,7 +16,7 @@ func main() {
for i := 0; i < 10; i++ {
go db.Table("user").All()
}
time.Sleep(time.Second)
time.Sleep(time.Millisecond * 100)
}
}

View File

@ -8,5 +8,5 @@ func main() {
db := g.DB()
db.SetDebug(true)
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
db.Table("user").Data("num=num+1").Where("id", 8).Update()
}

View File

@ -513,16 +513,6 @@ func mapToStruct() {
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
}
}
func main() {
db.PingMaster()

View File

@ -1,16 +1,68 @@
package main
import (
"archive/zip"
"fmt"
"github.com/gogf/gf/encoding/gcompress"
"io"
"os"
"path/filepath"
"strings"
)
func main() {
err := gcompress.ZipPath(
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
"my-dir",
)
fmt.Println(err)
// srcFile could be a single file or a directory
func Zip(srcFile string, destZip string) error {
zipfile, err := os.Create(destZip)
if err != nil {
return err
}
defer zipfile.Close()
archive := zip.NewWriter(zipfile)
defer archive.Close()
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
// header.Name = path
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
}
return err
})
return err
}
func main() {
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
fmt.Println(gcompress.ZipPath(src, dst))
//fmt.Println(Zip(src, dst))
}

View File

@ -2,25 +2,14 @@ package main
import (
"fmt"
"github.com/gogf/gf/i18n/gi18n"
"github.com/gogf/gf/frame/g"
)
func main() {
t := gi18n.New()
t.SetPath("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/i18n/gi18n/i18n")
t.SetLanguage("en")
fmt.Println(t.Translate(`hello`))
fmt.Println(t.Translate(`{#hello}{#world}!`))
t.SetLanguage("ja")
fmt.Println(t.Translate(`hello`))
fmt.Println(t.Translate(`{#hello}{#world}!`))
t.SetLanguage("ru")
fmt.Println(t.Translate(`hello`))
fmt.Println(t.Translate(`{#hello}{#world}!`))
fmt.Println(t.Translate(`hello`, "zh-CN"))
fmt.Println(t.Translate(`{#hello}{#world}!`, "zh-CN"))
var (
orderId = 865271654
orderAmount = 99.8
)
fmt.Println(g.I18n().Tfl(`en`, `{#OrderPaid}`, orderId, orderAmount))
fmt.Println(g.I18n().Tfl(`zh-CN`, `{#OrderPaid}`, orderId, orderAmount))
}

View File

@ -1,3 +1 @@
hello = "Hello"
world = "World"
OrderPaid = "You have successfully complete order #%d payment, paid amount: ¥%0.2f."

View File

@ -1,2 +1 @@
hello = "你好"
world = "世界"
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"

View File

@ -9,7 +9,7 @@ import (
func Upload(r *ghttp.Request) {
saveDirPath := "/tmp/"
files := r.GetUploadFiles("upload-file")
if err := files.Save(saveDirPath); err != nil {
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit("upload successfully")

View File

@ -2,12 +2,34 @@ package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"path/filepath"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func SendXmlFile(gameId int, areaName string, filePath string) error {
path := filepath.FromSlash(filePath)
fmt.Println(path)
data := g.Map{
"gameName": gameId,
"area": areaName,
"file": "@file:" + path,
"contentType": "json",
}
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
panic(err)
} else {
defer r.Close()
fmt.Println("ok")
}
return nil
}
func main() {
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
return
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
if e != nil {

View File

@ -8,8 +8,8 @@ import (
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
saveDirPath := "/tmp/"
files := r.GetUploadFiles("upload-file")
if err := files.Save(saveDirPath); err != nil {
files := r.GetUploadFiles("file")
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit("upload successfully")

View File

@ -3,15 +3,21 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
type GetById struct {
Id *g.Var `p:"id" v:"required|integer#id不能为空|id必须为整数"`
}
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.BindHandler("/", func(r *ghttp.Request) {
glog.Println(r.Header)
r.Response.Write("hello world")
var idInfo *GetById
if err := r.Parse(&idInfo); err != nil {
r.Response.Write(err)
}
r.Response.Write("ok")
})
s.SetPort(8999)
s.Run()

View File

@ -5,21 +5,18 @@ import (
"github.com/gogf/gf/net/ghttp"
)
type User struct {
}
type User struct{}
func (c *User) Index(r *ghttp.Request) {
r.Response.Write("Index")
}
// 不符合规范,不会被注册
func (c *User) Test(r *ghttp.Request, value interface{}) {
func (c *User) Test(r *ghttp.Request) {
r.Response.Write("Test")
}
func main() {
s := g.Server()
s.BindObject("/user", new(User))
u := new(User)
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/db-{table}/{id}", u, "Test")
})
s.SetPort(8199)
s.Run()
}

View File

@ -1,12 +1,13 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s.EnablePProf()
s := g.Server()
s.Domain("localhost").EnablePProf()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln("哈喽世界!")
})

View File

@ -6,12 +6,8 @@ import (
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{"Graceful": true})
s.EnableAdmin()
//s.BindHookHandler("/admin/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
// if !r.BasicAuth("admin", "123", "") {
// r.Exit()
// }
//})
s.SetPort(8199)
s.Run()
}

View File

@ -1,29 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/gogf/gf/os/gfcache"
"github.com/gogf/gf/os/gfile"
)
func main() {
s := 0
r := ""
path := gfile.TempDir() + gfile.Separator + "temp"
gfile.PutContents(path, "hello")
s = gfcache.GetSize()
r = gfcache.GetContents(path)
fmt.Println(s, r)
gfile.PutContentsAppend(path, " john")
// 等待1秒以便gfsnotify回调能处理完成
time.Sleep(time.Second)
s = gfcache.GetSize()
r = gfcache.GetContents(path)
fmt.Println(s, r)
}

View File

@ -0,0 +1,14 @@
package main
import (
"context"
"github.com/gogf/gf/os/glog"
)
func main() {
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
glog.Ctx(ctx).Print(1, 2, 3)
}

View File

@ -2,19 +2,18 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
"github.com/gogf/gf/net/ghttp"
)
type Controller struct {
gmvc.Controller
}
func (c *Controller) Test() {
c.View.Display("layout.html")
}
func main() {
s := g.Server()
s.BindControllerMethod("/", new(Controller), "Test")
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.WriteTpl("layout.html", g.Map{
"header": "This is header",
"container": "This is container",
"footer": "This is footer",
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -1,3 +1,3 @@
{{define "container"}}
<h1>CONTAINER</h1>
<h1>{{.container}}</h1>
{{end}}

View File

@ -1,3 +1,3 @@
{{define "footer"}}
<h1>FOOTER</h1>
<h1>{{.footer}}</h1>
{{end}}

View File

@ -1,3 +1,3 @@
{{define "header"}}
<h1>HEADER</h1>
<h1>{{.header}}</h1>
{{end}}

View File

@ -2,14 +2,14 @@
<html>
<head>
<title>GoFrame Layout</title>
{{template "header"}}
{{template "header" .}}
</head>
<body>
<div class="container">
{{template "container"}}
{{template "container" .}}
</div>
<div class="footer">
{{template "footer"}}
{{template "footer" .}}
</div>
</body>
</html>

View File

@ -9,11 +9,13 @@ func main() {
s := g.Server()
s.BindHandler("/main1", func(r *ghttp.Request) {
r.Response.WriteTpl("layout.html", g.Map{
"name": "smith",
"mainTpl": "main/main1.html",
})
})
s.BindHandler("/main2", func(r *ghttp.Request) {
r.Response.WriteTpl("layout.html", g.Map{
"name": "john",
"mainTpl": "main/main2.html",
})
})

View File

@ -1,18 +0,0 @@
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
# Logger.
[logger]
Path = "/tmp/log/gf-app"
Level = "all"
Stdout = true
[viewer]
delimiters = ["${", "}"]
autoencode = true

View File

@ -1,21 +0,0 @@
package main
import (
"fmt"
)
func Test() (int, int) {
return 1, 1
}
func Assert(v1, v2, v3 interface{}) {
fmt.Println(v1)
}
func F(v ...interface{}) []interface{} {
return v
}
func main() {
Assert(F(Test()), 2, 3)
}

View File

@ -1,11 +0,0 @@
package main
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
)
func main() {
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
fmt.Println(r)
}

View File

@ -8,6 +8,9 @@ import (
func main() {
for i := 0; i < 100; i++ {
fmt.Println(grand.Rand(0, 99999))
fmt.Println(grand.S(16))
}
for i := 0; i < 100; i++ {
fmt.Println(grand.N(0, 99999999))
}
}

View File

@ -0,0 +1,13 @@
package main
import (
"fmt"
"github.com/gogf/gf/util/guid"
)
func main() {
for i := 0; i < 100; i++ {
s := guid.S()
fmt.Println(s, len(s))
}
}

View File

@ -0,0 +1,20 @@
package main
import (
"fmt"
"math"
"strconv"
)
func main() {
// 36*36^2+36*36+36
var s string
fmt.Println(strconv.ParseUint("zzz", 36, 3))
fmt.Println(1 << 1)
// MaxInt64
s = strconv.FormatUint(math.MaxUint64, 16)
fmt.Println(s, len(s))
// PID
s = strconv.FormatInt(1000000, 36)
fmt.Println(s, len(s))
}

View File

@ -0,0 +1,23 @@
package main
import (
"fmt"
"github.com/gogf/gf/util/guid"
)
func main() {
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"))
fmt.Println(s, len(s))
}
fmt.Println()
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"), []byte("456"))
fmt.Println(s, len(s))
}
fmt.Println()
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
fmt.Println(s, len(s))
}
}

View File

@ -11,7 +11,7 @@ func main() {
fmt.Println(1)
gutil.Throw("error")
fmt.Println(2)
}, func(err interface{}) {
}, func(err error) {
fmt.Println(err)
})
}

View File

@ -0,0 +1,13 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gvalid"
)
func main() {
g.I18n().SetLanguage("cn")
err := gvalid.Check("", "required", nil)
fmt.Println(err.String())
}

View File

@ -0,0 +1,14 @@
"gf.gvalid.required" = "字段不能为空"

3
.gitignore vendored
View File

@ -6,7 +6,7 @@
.idea/
.settings/
.vscode/
vender/
vendor/
composer.lock
gitpush.sh
pkg/
@ -15,3 +15,4 @@ cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/

View File

@ -1,8 +1,11 @@
language: go
go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "1.14.x"
- "1.15.x"
branches:
only:

View File

@ -1,13 +1,17 @@
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
We currently accept donation by [Wechat](https://goframe.org/images/donate.png) / [Alipay](https://goframe.org/images/donate.png) / [Gitee](https://gitee.com/johng/gf),
please note your github/gitee account in your payment bill. All the donations will be used only for `GoFrame` project development and its community construction.
> If you cannot see the donation image, please click [here](https://goframe.org/images/donate.png).
<img src="https://goframe.org/images/donate.png?20200718"/>
> If you cannot view the donation image, please click [here](https://goframe.org/images/donate.png).
| Name | Channel | Amount | Comment
|---|---|--- | ---
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00 |
|[ireadx](https://github.com/ireadx)|alipay|¥301.00 |
|[ireadx](https://github.com/ireadx)|alipay|¥501.00 |
|[mg91](https://gitee.com/mg91)|gitee|¥10.00 |
|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00 |
|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00 |
@ -46,7 +50,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|struggler|alipay|¥18.80|
|*铁|wechat|¥0.01|
|C*e|wechat|¥66.66| GF越来越好棒👍
|(佚名)|wechat|¥6.66| (名字打不出来也没备注捐赠时间2020-02-21 14:24:34)
|---P คิดถึง|wechat|¥6.66|
|[王飞](https://gitee.com/wang_2018)|gitee|¥20.00| 感谢您的开源项目!
|[Zeroing-ZY](https://gitee.com/yunjieg)|gitee|¥20.00| 感谢您的开源项目!
|[katydid酱](https://gitee.com/katydid2005)|gitee|¥50.00| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
@ -56,7 +60,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|🚶|wechat|¥6.88| 喝杯冰阔落
|a*l|wechat|¥10.00| gf
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|*包|wechat|¥9.99|
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|重庆宝尔威科技|wechat|¥6.66|
|琦玉-QPT|wechat|¥6.66|
|sailsea|wechat|¥11.00|
@ -64,11 +68,59 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|*华|wechat|¥6.66| 感谢郭强的热心
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|北京京纬互动科技|alipay|¥200.00|
|米司特包|wechat|¥99.99|
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|金毛|alipay|¥100.00|
|1*1x|wechat|¥100.00|
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|如果🍋|alipay|¥100.00| 错过的奶茶^_^
|蔡蔡|wechat|¥666.00| gf真强大让项目省心
|jack|wechat|¥100.00|
|sbilly|wechat|¥100.00| 祝好!
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|faddei|qq|¥9.99|
|*天|wechat|¥10.00|
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|李勇|wechat|¥50.00|
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|千年|wechat|¥1.08|
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|伟客互联|wechat|¥66.66|
|[sbilly](https://github.com/sbilly)|wechat|¥99.00|
|**阳|alipay|¥100.01|
|**亮|alipay|¥10.00|
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|Fly的狐狸|alipay|¥100.00|
|六七 ·|wechat|¥88.88| gf越来越好
|Tom|wechat|¥500.00| 一点心意 希望GF越来越好
|Chao|wechat|¥166.00|
|汤sir|wechat|¥10.00| 强哥,喝杯咖啡☕️
|秋叶、|wechat|¥66.66| GF带我一起玩
||wechat|¥20.00|
|程凤明|wechat|¥18.80|
|Glowworm|wechat|¥8.88| 感谢大佬
|徒行|wechat|¥10.00| 目前项目在转用这个框架,鼓励一下大佬吧👍👍👍
|产品设计.软件开发|wechat|¥10.00| 祝愿走向更成功!
|[charlieccGuo](https://github.com/charlieccGuo)|wechat|¥10.00|
|yiran|wechat|¥20.00| 赏给大佬喝茶🍵
|长夏朔酒|wechat|¥20.00| 请大佬喝茶
|💥聪จุ๊บ 🇨🇳|wechat|¥66.66| 请大佬喝茶
|Even_|wechat|¥10.00| 日照市民发来贺电!
|智慧人生|wechat|¥50.00| 智慧人生
|一滴水|wechat|¥10.00| goframe 越来越强大
|[wenzi1](https://github.com/wenzi1)|alipay|¥100.00|
|[hyuant](https://github.com/hyuant)|alipay|¥66.66|
|*庆|alipay|¥9.99| 支持一下gf越来越好
|**君|alipay|¥10.00| 加油
|向回走的闹钟|wechat|¥20.00| 越来越好
|金毛|wechat|¥100.00|
|莫失莫忘|wechat|¥100.00|
|**航|alipay|¥20.00|
<img src="https://goframe.org/images/donate.png"/>

112
README.MD
View File

@ -9,13 +9,13 @@
English | [简体中文](README_ZH.MD)
`GF(GoFrame)` is a modular, loose-coupled, full-featured and production-ready application development framework of golang,
providing a series of core components and dozens of practical modules,
such as: memcache, configure, validator, logging, array/queue/set/map containers,
timer/timing tasks, file/memory lock, object pool, database ORM, etc,
supporting web server integrated with router, cookie, session, middleware, logger,
template, https, hooks, rewrites and many more features.
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
of golang. Providing a series of core components and dozens of practical modules, such as:
cache, logging, containers, timer, resource, validator, database orm, etc.
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
template, https, hooks, rewrites and many more features.
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
# Installation
```
@ -28,13 +28,81 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.13
golang version >= 1.11
```
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=12"/>
</div>
# Packages
1. **Primary Package**
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
1. **Community Package**
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
# Performance
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
## Environment
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## Testing Tool
`ab`: Apache HTTP server benchmarking tool.
Command:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
The concurrency starts from `100` to `10000`.
> Run `5` times for each case of each project and pick up the best testing result.
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# Documentation
* [APIDoc](https://godoc.org/github.com/gogf/gf)
* [中文文档](https://goframe.org)
* 中文官网: https://goframe.org
* GoDoc API: https://godoc.org/github.com/gogf/gf
# Discussion
- QQ Group[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
@ -43,20 +111,29 @@ golang version >= 1.13
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# License
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Part Of Users
- [Tencent](https://www.tencent.com/)
- [ZTE](https://www.zte.com.cn/china/)
- [Ant Financial Services](https://www.antfin.com/)
- [MedLinker](https://www.medlinker.com/)
- [KuCoin](https://www.kucoin.io/)
- [LeYouJia](https://www.leyoujia.com/)
- [IGG](https://igg.com)
- [XiMaLaYa](https://www.ximalaya.com)
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168).
# Contributors
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
@ -65,6 +142,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
# Thanks
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
@ -75,3 +153,7 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i

View File

@ -15,16 +15,17 @@
并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
# 特点
* 模块化、松耦合设计;
* 模块丰富开箱即用;
* 简便易用易于维护;
* 社区活跃,大牛谦逊低调脾气好;
* 模块丰富开箱即用;
* 简便易用易于维护;
* 高代码质量、高单元测试覆盖率;
* 社区活跃,大牛谦逊低调脾气好;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 更适合企业及团队使用;
* 设计为团队及企业使用;
# 地址
- **主库**https://github.com/gogf/gf
@ -41,33 +42,112 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.13
golang版本 >= 1.11
```
# 架构
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
<img src="https://goframe.org/images/arch.png?v=12"/>
</div>
# 模块
1. **核心模块**
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
1. **社区模块**
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
# 性能
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
## 环境:
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## 工具
`ab`: Apache HTTP server benchmarking tool.
测试命令:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
并发客户端数量从 `100` 递增到 `10000`。
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# 文档
开发文档:[https://goframe.org](https://goframe.org)
开发文档https://goframe.org
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
接口文档https://godoc.org/github.com/gogf/gf
# 帮助
- QQ交流群[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
- WX交流群微信添加`389961817`备注`GF`
- 主库ISSUEhttps://github.com/gogf/gf/issues
> 建议通过阅读`Gorame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
# 协议
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
# 用户
- [腾讯科技](https://www.tencent.com/)
- [中兴科技](https://www.zte.com.cn/china/)
- [蚂蚁金服](https://www.antfin.com/)
- [医联科技](https://www.medlinker.com/)
- [库币科技](https://www.kucoin.io/)
- [乐有家](https://www.leyoujia.com/)
- [IGG](https://igg.com)
- [喜马拉雅](https://www.ximalaya.com)
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。
# 贡献
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
@ -84,4 +164,5 @@ golang版本 >= 1.13
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
# 感谢
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>

View File

@ -1,3 +1,213 @@
# `v1.13.1` (2020-06-10)
# GoFrame
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链提供了常用的基础开发模块缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
## 特点
* 模块化、松耦合设计;
* 模块丰富、开箱即用;
* 简便易用、易于维护;
* 高代码质量、高单元测试覆盖率;
* 社区活跃,大牛谦逊低调脾气好;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 设计为团队及企业使用;
## 发展
`GoFrame`开始得比较早,`2011`年始于北京一个智能物联网平台项目,那时还没有这么多物联网的现行标准,`Go`的标准库以及生态也未如此丰富。`2017`年的时候`GF`才开始发布测试版,`2018`年`1024`程序员节日的时候才发布`v1.0`正式版,为`Go`生态发展添砖加瓦。开源以来快速迭代、发展成长,广受开发者和企业的青睐,也有许多的开发者加入了贡献行列。`GF`原本是为开发团队设计的,因此她的开发效率和可维护性做得非常好,有着很高的代码质量以及丰富的单元测试和示例,并且`GF`是目前中文化文档做的最好的`Golang`开发框架。
# Change Log
1. 应多数开发者的要求,框架要求的最低`Golang`运行版本降级为了`v1.11`。
1. 新增`GoFrame`视频教程地址:
- bilibilihttps://www.bilibili.com/video/av94410029
- 西瓜视频: https://www.ixigua.com/pseries/6809291194665796100/
1. 将不常用的`guuid`模块迁移到 github.com/gogf/guuid 作为社区模块维护,保持`gf`主仓库的轻量级。
1. 新增`guid`模块用于高效轻量级的唯一字符串生成https://goframe.org/util/guid/index
## `tool chain`
1. 工具链更新https://goframe.org/toolchain/cli
1. 新增`gf env`命令,更优雅地查看当前`Golang`环境变量信息。
1. 新增`gf mod path`命令,用于将当前`go modules`包拷贝到`GOPATH`中,以便使用原始的`GOPATH`方式开发项目。
1. 对现有`cli`命令进行了一些改进,提高使用体验;预编译二进制版本在部分平台下提供了`upx`压缩,使得下载的文件更小。
## `container`
1. `garray`
- https://goframe.org/container/garray/index
- 简化数组使用方式,支持类似于`var garray.Array`的变量定义使用方式;
- 增加`Walk`方法,用于自定义的数组元素处理方法;
- 增加`ContainsI`方法,用于大小写忽略匹配的数组元素项存在性查找;
- 完善单元测试,代码覆盖率`94%`
- 代码改进,提高性能;
- 修复一些问题;
1. `gchan`
- 由于该封装包实际意义不是很大,因此从主框架中删除;
1. `glist`
- https://goframe.org/container/glist/index
- 简化链表使用方式,支持类似于`var glist.List`的变量定义使用方式;
- 完善单元测试,代码覆盖率`99%`
1. `gmap`
- https://goframe.org/container/gmap/index
- 简化`Map`使用方式,支持类似于`var gmap.Map`的变量定义使用方式;
- 完善单元测试,代码覆盖率`81%`
- 代码改进,提高性能;
1. `gset`
- https://goframe.org/container/gset/index
- 简化集合使用方式,支持类似于`var gset.Set`的变量定义使用方式;
- 增加`Walk`方法,用于自定义的集合元素处理方法;
- 完善单元测试,代码覆盖率`90%`
- 代码改进,提高性能;
1. `gtree`
- https://goframe.org/container/gtree/index
- 简化树型使用方式,支持类似于`var gtree.BTree`的变量定义使用方式;
- 完善单元测试,代码覆盖率`90%`
1. `gvar`
- https://goframe.org/container/gvar/index
- 完善单元测试,代码覆盖率`69%`
- 代码组织结构调整,提高维护性;
- 代码改进,提高性能;
## `database`
1. `gdb`
- 增加`Transaction(f func(tx *TX) error) (err error)`接口方法用于通过闭包实现事务封装处理https://goframe.org/database/gdb/transaction
- 去掉不常用的`From`接口方法,改进`Table`及`Model`方法的参数为不定参数并支持通过不定参数传递表别名https://goframe.org/database/gdb/chaining/select
- 增加`DryRun`特性,支持空跑时只执行查询不执行写入/更新/删除操作https://goframe.org/database/gdb/senior
- 增加`create_at`, `update_at`写入时间、更新时间字段自动填充特性https://goframe.org/database/gdb/chaining/auto-time
- 增加`delete_at`软删除特性https://goframe.org/database/gdb/chaining/auto-time
- 增加`Having`链式操作方法,用于`having`条件查询https://goframe.org/database/gdb/chaining/select
- `Result`结果对象增加`Chunk`方法用于自定义的数据分批处理https://goframe.org/database/gdb/result
- 改进`Schema`数据库运行时切换特性;
- 改进对`pgsql`, `mssql`, `sqlite`, `oracle`数据库字段类型的支持;
- 进一步完善单元测试;
- 代码组织结构调整,提高维护性;
- 代码改进,提高性能;
1. `gredis`
- 增加`MaxActive`连接池参数默认配置为`100`,限制默认的连接数量;
- 改进`Conn`连接对象的`Do`方法,支持对`map/slice/struct`类型进行自动的`json.Marshal`处理,注意获取数据时使用`DoVar`方法获取https://goframe.org/database/gredis/usage
- 完善单元测试,代码覆盖率`72%`
## `net`
1. `ghttp`
- 增加`Prefix`及`Retry`客户端链式操作方法;
- 增加客户端原始请求打印特性https://goframe.org/net/ghttp/client/demo/dump
- 增加`ClientMaxBodySize`的服务端配置,用于限制客户端提交的`Body`大小,默认为`8MB`在涉及到上传的Server中需要增加该配置的大小在配置文件中指定对应的大小即可如`ClientMaxBodySize="100MB"`https://goframe.org/net/ghttp/config
- 改进`SessionId`生成的随机性,提高`Session`安全性https://goframe.org/os/gsession/index
- 改进`ghttp.Server`实现了标准库的`http.Handler`接口,便于与其他第三方的服务如`Prometheus`进行代码集成;
- 其他大量的代码细节改进工作,提高性能及持久维护性;
- 完善单元测试,代码覆盖率`61%`
1. `gipv4`
- 增加`GetIpArray`方法用于获取当前主机的所有IPv4地址
- 增加`GetMacArray`及`GetMac`方法,用于获取当前主机的`MAC`地址信息;
- 修改`IntranetIP`方法名称为`GetIntranetIp`,修改`IntranetIPArray`方法名称为`GetIntranetIpArray`
## `encoding`
1. `gjson`
- 新增`GetMaps`获取`JSON`内部节点变量方法;
- 改进`NewWithTag`方法对`map/struct`的处理;
- 完善单元测试,代码覆盖率`77%`
1. `gyaml`
- 升级依赖的第三方`yaml`解析包,解决了`map[interface{}]interface{}`转换问题;
## `error`
1. `gerror`
- 新增`NewfSkip`方法,用于创建`skip`指定堆栈的错误对象;
- 放开框架所有的堆栈链路打印,展示错误时真实的链路调用详情;
## `os`
1. `gcache`
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
- 标记`Removes`方法废弃,改进`Remove`方法参数为不定参数,统一使用`Remove`方法删除单个/多个键值对;
- 完善单元测试,代码覆盖率`96%`
1. `genv`
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
1. `gfile`
- 改进`CopyDir/CopyFile`复制目录/文件方法;
- 新增`ScanDirFunc`方法,用于支持自定义处理回调的目录检索;
- 完善单元测试,代码覆盖率`64%`
1. `glog`
- 增加支持`Context`上下文变量的日志打印特性https://goframe.org/os/glog/context
1. `gres`
- 改进打包特性增强生成二进制文件及Go文件的压缩比比旧版本增加`20%`压缩率,使得编译生成的二进制文件体积更小;
- 代码结构改进,提高执行效率及可持久维护性;
1. `gsession`
- 改进`SessionId`默认生成方法,采用`guid.S`方法生成;
- 增加`SetId`及`SetIdFunc`方法,用于自定义`SessionId`及自定义的`SessionId`生成方法;
## `frame`
1. `g`
- 新增`g.Table`方法,用于快速创建数据库模型操作对象;
## `i18n`
1. `gi18n`
- 新增`GetContent`方法,用于获取指定`i18n`关键字为转译内容;
- 改进代码细节,提高性能和持久可维护性;
- 完善单元测试,代码覆盖率`74%`
## `test`
1. `gtest`
- 增加`AssertNQ`断言方法,用于强类型的不相等判断;
## `text`
1. `gstr`
- 增加`SubStrRune`方法,用于支持`unicode`的字符串截取;
- 增加`StrLimitRune`方法,用于支持`unicode`的字符串截断隐藏;
- 增加`LenRune`方法,用于替换`RuneLen`方法,统一方法命名风格;
- 增加`PosRune/PosIRune/PosRRune/PosRIRune`方法,用于支持`unicode`的字符串左右位置查找;
- 增加`CompareVersionGo`方法,用于`Golang`风格的版本号大小比较;
- 完善单元测试,代码覆盖率`75%`
## `util`
1. `gconv`
- 改进`Convert`转换方法,支持常见`map`类型的转换;
- 改进类型转换过程中异常错误的捕获,通过`error`返回;
- 其他一些细节改进;
- 完善单元测试,代码覆盖率`63%`
1. `grand`
- 增加`B`方法,用于获得随机的二进制数据;
- 改进代码底层实现,部分接口性能提高`50%`
- 完善单元测试,代码覆盖率`74%`
1. `guid`
- 新增`guid`模块用于高效轻量级的唯一字符串生成https://goframe.org/util/guid/index
1. `gutil`
- 增加`MapContains`方法用于判断map中是否包含指定键名
- 增加`MapDelete`方法用于删除map中指定的键名可以为多个键名
- 增加`MapMerge`方法用于合并两个map
- 增加`MapMergeCopy`方法用于拷贝多个map
- 增加`MapContainsPossibleKey`方法,用于查找指定键名,忽略大小写及字符`'-'/'_'/'.'/' '`
1. `gvalid`
- 所有默认的错误提示改为了英文;
- 错误提示的配置改为了通过`i18n`来配置实现以便支持国际化https://goframe.org/util/gvalid/message
- 身份证号规则名称从`id-number`改为了`resident-id `
- 银行卡号规则名称从`luhn`改为了`bank-card`
- 完善单元测试,代码覆盖率`96%`
## Bug Fix
1. 修复`gcompress`的多文件`zip`压缩问题;
1. 修复`ghttp.Client`获取返回的过期`Cookie`的问题;
1. 修复`gres.File`对于`http.File`接口的实现细节;
1. 修复`garray.Pop*`方法的边界问题;
1. 修复`gres`中`Readdir`方法参数为`0`时报错的问题;
1. 其他一些修复https://github.com/gogf/gf/issues?q=is%3Aissue+label%3Abug
# `v1.12.1` (2020-03-31)
大家好啊!久等啦!

View File

@ -4,5 +4,5 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package garray provides concurrent-safe/unsafe arrays.
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
package garray

View File

@ -8,6 +8,12 @@ package garray
import "strings"
// apiInterfaces is used for type assert api for Interfaces.
type apiInterfaces interface {
Interfaces() []interface{}
}
// defaultComparatorInt for int comparison.
func defaultComparatorInt(a, b int) int {
if a < b {
return -1
@ -18,6 +24,7 @@ func defaultComparatorInt(a, b int) int {
return 0
}
// defaultComparatorStr for string comparison.
func defaultComparatorStr(a, b string) int {
return strings.Compare(a, b)
}

View File

@ -8,10 +8,10 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -22,8 +22,10 @@ import (
)
// Array 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.
type Array struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []interface{}
}
@ -44,7 +46,7 @@ func NewArray(safe ...bool) *Array {
// which is false in default.
func NewArraySize(size int, cap int, safe ...bool) *Array {
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]interface{}, size, cap),
}
}
@ -79,7 +81,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
// which is false in default.
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -91,7 +93,7 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -288,7 +290,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return nil, false
}
value = a.array[index]
@ -473,18 +475,7 @@ func (a *Array) Contains(value interface{}) bool {
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) int {
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
return result
}
func (a *Array) doSearch(value interface{}) int {
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
@ -499,12 +490,15 @@ func (a *Array) doSearch(value interface{}) int {
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}
@ -533,23 +527,7 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
// 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 {
switch v := array.(type) {
case *Array:
a.Append(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *StrArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Interfaces(v.Slice())...)
default:
a.Append(gconv.Interfaces(array)...)
}
return a
return a.Append(gconv.Interfaces(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -668,6 +646,9 @@ func (a *Array) Reverse() *Array {
func (a *Array) 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))
@ -694,7 +675,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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) {
a.mu.RLock()
@ -706,7 +687,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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 *Array) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
@ -741,7 +722,8 @@ func (a *Array) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *Array) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a Array) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
@ -749,8 +731,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *Array) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]interface{}, 0)
}
a.mu.Lock()
@ -763,9 +744,6 @@ 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 {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -806,6 +784,16 @@ func (a *Array) FilterEmpty() *Array {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *Array) IsEmpty() bool {
return a.Len() == 0

View File

@ -8,9 +8,9 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/json"
"math"
"sort"
@ -20,8 +20,10 @@ import (
)
// IntArray is a golang int array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type IntArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []int
}
@ -37,7 +39,7 @@ func NewIntArray(safe ...bool) *IntArray {
// which is false in default.
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]int, size, cap),
}
}
@ -62,7 +64,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
// which is false in default.
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -74,7 +76,7 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -264,7 +266,7 @@ func (a *IntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return 0, false
}
value = a.array[index]
@ -489,6 +491,10 @@ func (a *IntArray) Contains(value int) bool {
// or returns -1 if not exists.
func (a *IntArray) Search(value int) int {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
result := -1
for index, v := range a.array {
if v == value {
@ -496,17 +502,19 @@ func (a *IntArray) Search(value int) int {
break
}
}
a.mu.RUnlock()
return result
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}
@ -535,23 +543,7 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
// 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 {
switch v := array.(type) {
case *Array:
a.Append(gconv.Ints(v.Slice())...)
case *IntArray:
a.Append(gconv.Ints(v.Slice())...)
case *StrArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Ints(v.Slice())...)
default:
a.Append(gconv.Ints(array)...)
}
return a
return a.Append(gconv.Ints(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -670,6 +662,9 @@ func (a *IntArray) Reverse() *IntArray {
func (a *IntArray) 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))
@ -696,7 +691,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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 *IntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
@ -708,7 +703,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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 *IntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
@ -726,7 +721,8 @@ func (a *IntArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *IntArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a IntArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
@ -734,8 +730,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *IntArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]int, 0)
}
a.mu.Lock()
@ -748,9 +743,6 @@ 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 {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -776,6 +768,16 @@ func (a *IntArray) FilterEmpty() *IntArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *IntArray) Walk(f func(value int) int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *IntArray) IsEmpty() bool {
return a.Len() == 0

View File

@ -8,9 +8,9 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -22,8 +22,10 @@ import (
)
// StrArray is a golang string array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type StrArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []string
}
@ -39,7 +41,7 @@ func NewStrArray(safe ...bool) *StrArray {
// which is false in default.
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]string, size, cap),
}
}
@ -49,7 +51,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
// which is false in default.
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -61,7 +63,7 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
newArray := make([]string, len(array))
copy(newArray, array)
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -252,7 +254,7 @@ func (a *StrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return "", false
}
value = a.array[index]
@ -473,13 +475,30 @@ func (a *StrArray) Contains(value string) bool {
return a.Search(value) != -1
}
// 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 *StrArray) ContainsI(value string) bool {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return false
}
for _, v := range a.array {
if strings.EqualFold(v, value) {
return true
}
}
return false
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *StrArray) Search(value string) int {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if strings.Compare(v, value) == 0 {
@ -487,17 +506,19 @@ func (a *StrArray) Search(value string) int {
break
}
}
a.mu.RUnlock()
return result
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *StrArray) Unique() *StrArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}
@ -526,23 +547,7 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
// 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 {
switch v := array.(type) {
case *Array:
a.Append(gconv.Strings(v.Slice())...)
case *IntArray:
a.Append(gconv.Strings(v.Slice())...)
case *StrArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Strings(v.Slice())...)
default:
a.Append(gconv.Strings(array)...)
}
return a
return a.Append(gconv.Strings(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -661,6 +666,9 @@ func (a *StrArray) Reverse() *StrArray {
func (a *StrArray) 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)
@ -687,7 +695,7 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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 *StrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
@ -699,7 +707,7 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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 *StrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
@ -728,7 +736,8 @@ func (a *StrArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *StrArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a StrArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)
@ -736,8 +745,7 @@ func (a *StrArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *StrArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]string, 0)
}
a.mu.Lock()
@ -750,9 +758,6 @@ 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 {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -778,6 +783,16 @@ func (a *StrArray) FilterEmpty() *StrArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *StrArray) Walk(f func(value string) string) *StrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *StrArray) IsEmpty() bool {
return a.Len() == 0

View File

@ -8,26 +8,28 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gutil"
"math"
"sort"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
)
// SortedArray is a golang sorted array with rich features.
// It's using increasing order in default.
// 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 SortedArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
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)
}
@ -46,8 +48,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
// which is false in default.
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
return &SortedArray{
mu: rwmutex.New(safe...),
unique: gtype.NewBool(),
mu: rwmutex.Create(safe...),
array: make([]interface{}, 0, cap),
comparator: comparator,
}
@ -75,7 +76,7 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
a := NewSortedArraySize(0, comparator, safe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
return a
}
@ -95,19 +96,19 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
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
// Resort the array if comparator is changed.
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
}
@ -118,13 +119,19 @@ func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
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...)
}
// 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
}
@ -132,7 +139,7 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -220,7 +227,7 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return nil, false
}
value = a.array[index]
@ -390,7 +397,7 @@ func (a *SortedArray) Len() int {
// 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{} {
array := ([]interface{})(nil)
var array []interface{}
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
@ -427,20 +434,20 @@ func (a *SortedArray) Search(value interface{}) (index int) {
// 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 len(a.array) == 0 {
return -1, -2
}
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 = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -457,8 +464,8 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -477,7 +484,7 @@ func (a *SortedArray) Unique() *SortedArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
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++
@ -509,6 +516,12 @@ func (a *SortedArray) Clear() *SortedArray {
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)
return a
}
@ -526,23 +539,7 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedArray) Merge(array interface{}) *SortedArray {
switch v := array.(type) {
case *Array:
a.Add(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *StrArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Interfaces(v.Slice())...)
default:
a.Add(gconv.Interfaces(array)...)
}
return a
return a.Add(gconv.Interfaces(array)...)
}
// Chunk splits an array into multiple arrays,
@ -596,6 +593,9 @@ func (a *SortedArray) Rands(size int) []interface{} {
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))
@ -622,7 +622,7 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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()
@ -634,7 +634,7 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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()
@ -669,19 +669,18 @@ func (a *SortedArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedArray) MarshalJSON() ([]byte, error) {
// 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)
}
// 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.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]interface{}, 0)
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
@ -698,11 +697,9 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
}
// 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.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
@ -764,7 +761,30 @@ func (a *SortedArray) FilterEmpty() *SortedArray {
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)
}
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
}

View File

@ -8,23 +8,25 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/json"
"math"
"sort"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
)
// SortedIntArray is a golang sorted int array with rich features.
// It's using increasing order in default.
// 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 SortedIntArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
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)
}
@ -48,9 +50,8 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
// which is false in default.
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
return &SortedIntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]int, 0, cap),
unique: gtype.NewBool(),
comparator: defaultComparatorInt,
}
}
@ -94,7 +95,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Ints(a.array)
quickSortInt(a.array, a.getComparator())
return a
}
@ -104,12 +105,18 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Ints(a.array)
quickSortInt(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 *SortedIntArray) Add(values ...int) *SortedIntArray {
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
}
@ -117,7 +124,7 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -205,7 +212,7 @@ func (a *SortedIntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return 0, false
}
value = a.array[index]
@ -424,20 +431,20 @@ func (a *SortedIntArray) Search(value int) (index int) {
// 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 len(a.array) == 0 {
return -1, -2
}
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 = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -454,8 +461,8 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -474,7 +481,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
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++
@ -523,23 +530,7 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
switch v := array.(type) {
case *Array:
a.Add(gconv.Ints(v.Slice())...)
case *IntArray:
a.Add(gconv.Ints(v.Slice())...)
case *StrArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Ints(v.Slice())...)
default:
a.Add(gconv.Ints(array)...)
}
return a
return a.Add(gconv.Ints(array)...)
}
// Chunk splits an array into multiple arrays,
@ -593,6 +584,9 @@ func (a *SortedIntArray) Rands(size int) []int {
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))
@ -619,7 +613,7 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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()
@ -631,7 +625,7 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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()
@ -649,7 +643,8 @@ func (a *SortedIntArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
// 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)
@ -657,10 +652,8 @@ func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]int, 0)
a.unique = gtype.NewBool()
a.comparator = defaultComparatorInt
}
a.mu.Lock()
@ -676,10 +669,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
if a.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = defaultComparatorInt
}
a.mu.Lock()
@ -717,7 +707,30 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
return a
}
// 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)
}
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
}

View File

@ -8,23 +8,26 @@ package garray
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
"strings"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
)
// SortedStrArray is a golang sorted string array with rich features.
// It's using increasing order in default.
// 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 SortedStrArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
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)
}
@ -48,9 +51,8 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
// which is false in default.
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
return &SortedStrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]string, 0, cap),
unique: gtype.NewBool(),
comparator: defaultComparatorStr,
}
}
@ -61,7 +63,7 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
a := NewSortedStrArraySize(0, safe...)
a.array = array
quickSortStr(a.array, a.comparator)
quickSortStr(a.array, a.getComparator())
return a
}
@ -79,7 +81,7 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
quickSortStr(a.array, a.comparator)
quickSortStr(a.array, a.getComparator())
return a
}
@ -89,12 +91,18 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
func (a *SortedStrArray) Sort() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
quickSortStr(a.array, a.comparator)
quickSortStr(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 *SortedStrArray) Add(values ...string) *SortedStrArray {
return a.Append(values...)
}
// 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
}
@ -102,7 +110,7 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -190,7 +198,7 @@ func (a *SortedStrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return "", false
}
value = a.array[index]
@ -394,6 +402,22 @@ func (a *SortedStrArray) Contains(value string) bool {
return a.Search(value) != -1
}
// 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.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return false
}
for _, v := range a.array {
if strings.EqualFold(v, value) {
return true
}
}
return false
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedStrArray) Search(value string) (index int) {
@ -409,20 +433,20 @@ func (a *SortedStrArray) Search(value string) (index int) {
// 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 len(a.array) == 0 {
return -1, -2
}
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 = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -439,8 +463,8 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
// 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.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -459,7 +483,7 @@ func (a *SortedStrArray) Unique() *SortedStrArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
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++
@ -508,23 +532,7 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
// 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 {
switch v := array.(type) {
case *Array:
a.Add(gconv.Strings(v.Slice())...)
case *IntArray:
a.Add(gconv.Strings(v.Slice())...)
case *StrArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Strings(v.Slice())...)
default:
a.Add(gconv.Strings(array)...)
}
return a
return a.Add(gconv.Strings(array)...)
}
// Chunk splits an array into multiple arrays,
@ -578,6 +586,9 @@ func (a *SortedStrArray) Rands(size int) []string {
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)
@ -604,7 +615,7 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <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()
@ -616,7 +627,7 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <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()
@ -645,7 +656,8 @@ func (a *SortedStrArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
// 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)
@ -653,10 +665,8 @@ func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]string, 0)
a.unique = gtype.NewBool()
a.comparator = defaultComparatorStr
}
a.mu.Lock()
@ -672,10 +682,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
if a.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = defaultComparatorStr
}
a.mu.Lock()
@ -713,7 +720,30 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
return a
}
// 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)
}
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
}

View File

@ -0,0 +1,281 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/garray"
)
func ExampleNew() {
// A normal array.
a := garray.New()
// Adding items.
for i := 0; i < 10; i++ {
a.Append(i)
}
// Print the array length.
fmt.Println(a.Len())
// Print the array items.
fmt.Println(a.Slice())
// Retrieve item by index.
fmt.Println(a.Get(6))
// Check item existence.
fmt.Println(a.Contains(6))
fmt.Println(a.Contains(100))
// Insert item before specified index.
a.InsertAfter(9, 11)
// Insert item after specified index.
a.InsertBefore(10, 10)
fmt.Println(a.Slice())
// Modify item by index.
a.Set(0, 100)
fmt.Println(a.Slice())
// Search item and return its index.
fmt.Println(a.Search(5))
// Remove item by index.
a.Remove(0)
fmt.Println(a.Slice())
// Empty the array, removes all items of it.
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6 true
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
// [100 1 2 3 4 5 6 7 8 9 10 11]
// 5
// [1 2 3 4 5 6 7 8 9 10 11]
// [1 2 3 4 5 6 7 8 9 10 11]
// []
}
func ExampleArray_Iterator() {
array := garray.NewArrayFrom(g.Slice{"a", "b", "c"})
// 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 {
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 {
fmt.Println(k, v)
return true
})
// Output:
// 0 a
// 1 b
// 2 c
// 2 c
// 1 b
// 0 a
}
func ExampleArray_Reverse() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Reverse makes array with elements in reverse order.
fmt.Println(array.Reverse().Slice())
// Output:
// [9 8 7 6 5 4 3 2 1]
}
func ExampleArray_Shuffle() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Shuffle randomly shuffles the array.
fmt.Println(array.Shuffle().Slice())
}
func ExampleArray_Rands() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
// It does not delete the items from array.
fmt.Println(array.Rands(2))
// Randomly pick and return one item from the array.
// It deletes the picked up item from array.
fmt.Println(array.PopRand())
}
func ExampleArray_PopRand() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
// It does not delete the items from array.
fmt.Println(array.Rands(2))
// Randomly pick and return one item from the array.
// It deletes the picked up item from array.
fmt.Println(array.PopRand())
}
func ExampleArray_Join() {
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
fmt.Println(array.Join(","))
// Output:
// a,b,c,d
}
func ExampleArray_Chunk() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// 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.
fmt.Println(array.Chunk(2))
// Output:
// [[1 2] [3 4] [5 6] [7 8] [9]]
}
func ExampleArray_PopLeft() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_PopLefts() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_PopRight() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_PopRights() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_Contains() {
var array garray.StrArray
array.Append("a")
fmt.Println(array.Contains("a"))
fmt.Println(array.Contains("A"))
fmt.Println(array.ContainsI("A"))
// Output:
// true
// false
// true
}
func ExampleArray_Merge() {
array1 := garray.NewFrom(g.Slice{1, 2})
array2 := garray.NewFrom(g.Slice{3, 4})
slice1 := g.Slice{5, 6}
slice2 := []int{7, 8}
slice3 := []string{"9", "0"}
fmt.Println(array1.Slice())
array1.Merge(array1)
array1.Merge(array2)
array1.Merge(slice1)
array1.Merge(slice2)
array1.Merge(slice3)
fmt.Println(array1.Slice())
// Output:
// [1 2]
// [1 2 1 2 3 4 5 6 7 8 9 0]
}
func ExampleArray_FilterEmpty() {
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.FilterNil().Slice())
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
// Output:
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
// []interface {}{1, 2, "john"}
}
func ExampleArray_FilterNil() {
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.FilterNil().Slice())
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
// Output:
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
// []interface {}{1, 2, "john"}
}

View File

@ -0,0 +1,28 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
)
func ExampleStrArray_Walk() {
var array garray.StrArray
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]
}

View File

@ -1,132 +0,0 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/garray"
)
func Example_basic() {
// A normal array.
a := garray.New()
// Adding items.
for i := 0; i < 10; i++ {
a.Append(i)
}
// Print the array length.
fmt.Println(a.Len())
// Print the array items.
fmt.Println(a.Slice())
// Retrieve item by index.
fmt.Println(a.Get(6))
// Check item existence.
fmt.Println(a.Contains(6))
fmt.Println(a.Contains(100))
// Insert item before specified index.
a.InsertAfter(9, 11)
// Insert item after specified index.
a.InsertBefore(10, 10)
fmt.Println(a.Slice())
// Modify item by index.
a.Set(0, 100)
fmt.Println(a.Slice())
// Search item and return its index.
fmt.Println(a.Search(5))
// Remove item by index.
a.Remove(0)
fmt.Println(a.Slice())
// Empty the array, removes all items of it.
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6 true
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
// [100 1 2 3 4 5 6 7 8 9 10 11]
// 5
// [1 2 3 4 5 6 7 8 9 10 11]
// [1 2 3 4 5 6 7 8 9 10 11]
// []
}
func Example_rand() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
// It does not delete the items from array.
fmt.Println(array.Rands(2))
// Randomly pick and return one item from the array.
// It deletes the picked up item from array.
fmt.Println(array.PopRand())
}
func Example_popItem() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func Example_mergeArray() {
array1 := garray.NewFrom([]interface{}{1, 2})
array2 := garray.NewFrom([]interface{}{3, 4})
slice1 := []interface{}{5, 6}
slice2 := []int{7, 8}
slice3 := []string{"9", "0"}
fmt.Println(array1.Slice())
array1.Merge(array1)
array1.Merge(array2)
array1.Merge(slice1)
array1.Merge(slice2)
array1.Merge(slice3)
fmt.Println(array1.Slice())
// Output:
// [1 2]
// [1 2 1 2 3 4 5 6 7 8 9 0]
}
func Example_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.FilterNil().Slice())
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
// Output:
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
// []interface {}{1, 2, "john"}
}

View File

@ -9,6 +9,7 @@
package garray_test
import (
"github.com/gogf/gf/util/gutil"
"strings"
"testing"
@ -17,6 +18,55 @@ import (
"github.com/gogf/gf/util/gconv"
)
func Test_Array_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var array garray.Array
expect := []int{2, 3, 1}
array.Append(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.IntArray
expect := []int{2, 3, 1}
array.Append(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.StrArray
expect := []string{"b", "a"}
array.Append("b", "a")
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedArray
array.SetComparator(gutil.ComparatorInt)
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedIntArray
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedStrArray
expect := []string{"a", "b", "c"}
array.Add("c", "a", "b")
t.Assert(array.Slice(), expect)
})
}
func Test_SortedIntArray_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var array garray.SortedIntArray
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
}
func Test_IntArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 2, 3, 4, 5, 6}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -95,9 +95,9 @@ func TestArray_Sort(t *testing.T) {
func TestArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{1, 1, 2, 3}
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
})
}
@ -137,6 +137,65 @@ func TestArray_PopRands(t *testing.T) {
})
}
func TestArray_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 TestArray_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 TestArray_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 TestArray_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 TestArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
@ -501,6 +560,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"}
a1 := garray.NewArrayFrom(s1)
@ -519,7 +579,26 @@ func TestArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// value.
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
a1 := *garray.NewArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.Unmarshal(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -532,6 +611,25 @@ func TestArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
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.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
@ -650,3 +748,12 @@ func TestArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
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{} {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -87,9 +87,9 @@ func TestIntArray_Sort(t *testing.T) {
func TestIntArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 1, 2, 3}
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewIntArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{1, 2, 3})
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
})
}
@ -230,6 +230,65 @@ func TestIntArray_Fill(t *testing.T) {
})
}
func TestIntArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{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 TestIntArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{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 TestIntArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{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 TestIntArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{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 TestIntArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
@ -546,6 +605,7 @@ func TestIntArray_RLockFunc(t *testing.T) {
}
func TestIntArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
a1 := garray.NewIntArrayFrom(s1)
@ -563,7 +623,25 @@ func TestIntArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
a1 := *garray.NewIntArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewIntArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s1)
var a3 garray.IntArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -576,6 +654,25 @@ func TestIntArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.IntArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
@ -683,3 +780,12 @@ func TestIntArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}
func TestIntArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2})
t.Assert(array.Walk(func(value int) int {
return 10 + value
}), g.Slice{11, 12})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"strings"
"testing"
"time"
@ -64,6 +64,16 @@ func Test_StrArray_Basic(t *testing.T) {
})
}
func TestStrArray_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := garray.NewStrArray()
s.Append("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestStrArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect1 := []string{"0", "1", "2", "3"}
@ -81,7 +91,7 @@ func TestStrArray_Sort(t *testing.T) {
func TestStrArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []string{"1", "1", "2", "3"}
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array := garray.NewStrArrayFrom(expect)
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})
@ -119,6 +129,65 @@ func TestStrArray_PushAndPop(t *testing.T) {
})
}
func TestStrArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"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 TestStrArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"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 TestStrArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"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 TestStrArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"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 TestStrArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArray()
@ -535,6 +604,7 @@ func TestStrArray_LockFunc(t *testing.T) {
}
func TestStrArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
a1 := garray.NewStrArrayFrom(s1)
@ -552,7 +622,25 @@ func TestStrArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
a1 := *garray.NewStrArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewStrArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s1)
var a3 garray.StrArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -565,6 +653,25 @@ func TestStrArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.StrArray
}
data := g.Map{
"Name": "john",
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
@ -671,3 +778,12 @@ func TestStrArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}
func TestStrArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
t.Assert(array.Walk(func(value string) string {
return "key-" + value
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gutil"
"strings"
"testing"
@ -148,34 +148,62 @@ func TestSortedArray_Remove(t *testing.T) {
func TestSortedArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1 := garray.NewSortedArrayFrom(
[]interface{}{"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"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
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 TestSortedArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1 := garray.NewSortedArrayFrom(
[]interface{}{"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"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
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 TestSortedArray_PopRand(t *testing.T) {
@ -249,7 +277,6 @@ func TestSortedArray_PopLefts(t *testing.T) {
t.Assert(len(i2), 4)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 0)
})
}
@ -267,7 +294,7 @@ func TestSortedArray_PopRights(t *testing.T) {
i2 := array1.PopRights(10)
t.Assert(len(i2), 4)
t.Assert(array1.Len(), 0)
})
}
@ -499,15 +526,21 @@ func TestSortedArray_CountValues(t *testing.T) {
func TestSortedArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.SetUnique(true)
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "d"})
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{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}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
})
}
@ -612,6 +645,7 @@ 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"}
@ -631,7 +665,27 @@ func TestSortedArray_Json(t *testing.T) {
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s2 := []interface{}{"a", "b", "c", "d"}
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedArray(gutil.ComparatorString)
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -663,6 +717,42 @@ func TestSortedArray_Json(t *testing.T) {
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
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, nil)
t.Assert(ok, false)
@ -779,3 +869,12 @@ func TestSortedArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.Slice{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{} {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -133,6 +133,21 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(1), -1)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{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 TestSortedIntArray_PopRight(t *testing.T) {
@ -145,6 +160,23 @@ func TestSortedIntArray_PopRight(t *testing.T) {
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(5), -1)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{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 TestSortedIntArray_PopRand(t *testing.T) {
@ -404,7 +436,7 @@ func TestSortedIntArray_CountValues(t *testing.T) {
func TestSortedIntArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3}
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.SetUnique(true)
t.Assert(array1.Len(), 5)
@ -412,6 +444,16 @@ func TestSortedIntArray_SetUnique(t *testing.T) {
})
}
func TestSortedIntArray_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.NewSortedIntArrayFrom(a1)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
})
}
func TestSortedIntArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}
@ -504,6 +546,7 @@ func TestSortedIntArray_Merge(t *testing.T) {
}
func TestSortedIntArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
s2 := []int{1, 2, 3, 4}
@ -522,7 +565,26 @@ func TestSortedIntArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
s2 := []int{1, 2, 3, 4}
a1 := *garray.NewSortedIntArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedIntArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedIntArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -535,6 +597,25 @@ func TestSortedIntArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []int{98, 99, 100})
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedIntArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
@ -642,3 +723,12 @@ func TestSortedIntArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}
func TestSortedIntArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2})
t.Assert(array.Walk(func(value int) int {
return 10 + value
}), g.Slice{11, 12})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -51,6 +51,16 @@ func TestSortedStrArray_SetArray(t *testing.T) {
})
}
func TestSortedStrArray_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := garray.NewSortedStrArray()
s.Append("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestSortedStrArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
@ -127,6 +137,21 @@ func TestSortedStrArray_PopLeft(t *testing.T) {
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("a"), false)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"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 TestSortedStrArray_PopRight(t *testing.T) {
@ -139,6 +164,23 @@ func TestSortedStrArray_PopRight(t *testing.T) {
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("e"), false)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"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 TestSortedStrArray_PopRand(t *testing.T) {
@ -200,6 +242,7 @@ func TestSortedStrArray_PopLefts(t *testing.T) {
s1 = array1.PopLefts(4)
t.Assert(len(s1), 3)
t.Assert(s1, []string{"c", "d", "e"})
t.Assert(array1.Len(), 0)
})
}
@ -411,11 +454,21 @@ func TestSortedStrArray_Chunk(t *testing.T) {
func TestSortedStrArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := array1.SetUnique(true)
t.Assert(array2.Len(), 4)
t.Assert(array2, []string{"a", "c", "d", "e"})
t.Assert(array2.Len(), 3)
t.Assert(array2, []string{"1", "2", "3"})
})
}
func TestSortedStrArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array1 := garray.NewSortedStrArrayFrom(a1)
array1.Unique()
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"1", "2", "3"})
})
}
@ -513,6 +566,7 @@ func TestSortedStrArray_Merge(t *testing.T) {
}
func TestSortedStrArray_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"}
@ -533,7 +587,28 @@ func TestSortedStrArray_Json(t *testing.T) {
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.NewSortedStrArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedStrArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
t.Assert(a2.Interfaces(), s2)
var a3 garray.SortedStrArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -546,6 +621,25 @@ func TestSortedStrArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []string{"A", "A", "A+"})
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedStrArray
}
data := g.Map{
"Name": "john",
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
@ -652,3 +746,12 @@ func TestSortedStrArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}
func TestSortedStrArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
t.Assert(array.Walk(func(value string) string {
return "key-" + value
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -1,70 +0,0 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gchan provides graceful channel for no panic operations.
//
// It's safe to call Chan.Push/Close functions repeatedly.
package gchan
import (
"errors"
"github.com/gogf/gf/container/gtype"
)
// Graceful channel.
type Chan struct {
channel chan interface{}
closed *gtype.Bool
}
// New creates a graceful channel with given <limit>.
func New(limit int) *Chan {
return &Chan{
channel: make(chan interface{}, limit),
closed: gtype.NewBool(),
}
}
// Push pushes <value> to channel.
// It is safe to be called repeatedly.
func (c *Chan) Push(value interface{}) error {
if c.closed.Val() {
return errors.New("channel is closed")
}
c.channel <- value
return nil
}
// Pop pops value from channel.
// If there's no value in channel, it would block to wait.
// If the channel is closed, it will return a nil value immediately.
func (c *Chan) Pop() interface{} {
return <-c.channel
}
// Close closes the channel.
// It is safe to be called repeatedly.
func (c *Chan) Close() {
if !c.closed.Set(true) {
close(c.channel)
}
}
// See Len.
func (c *Chan) Size() int {
return c.Len()
}
// Len returns the length of the channel.
func (c *Chan) Len() int {
return len(c.channel)
}
// Cap returns the capacity of the channel.
func (c *Chan) Cap() int {
return cap(c.channel)
}

View File

@ -1,30 +0,0 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gchan_test
import (
"fmt"
"github.com/gogf/gf/container/gchan"
)
func Example_basic() {
n := 10
c := gchan.New(n)
for i := 0; i < n; i++ {
c.Push(i)
}
fmt.Println(c.Len(), c.Cap())
for i := 0; i < n; i++ {
fmt.Print(c.Pop())
}
c.Close()
// Output:
//10 10
//0123456789
}

View File

@ -1,47 +0,0 @@
package gchan_test
import (
"errors"
"testing"
"github.com/gogf/gf/container/gchan"
"github.com/gogf/gf/test/gtest"
)
func Test_Gchan(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ch := gchan.New(10)
t.Assert(ch.Cap(), 10)
t.Assert(ch.Push(1), nil)
t.Assert(ch.Len(), 1)
t.Assert(ch.Size(), 1)
ch.Pop()
t.Assert(ch.Len(), 0)
t.Assert(ch.Size(), 0)
ch.Close()
t.Assert(ch.Push(1), errors.New("channel is closed"))
ch = gchan.New(0)
ch1 := gchan.New(0)
go func() {
var i = 0
for {
v := ch.Pop()
if v == nil {
ch1.Push(i)
break
}
t.Assert(v, i)
i++
}
}()
for index := 0; index < 10; index++ {
ch.Push(index)
}
ch.Close()
t.Assert(ch1.Pop(), 10)
ch1.Close()
})
}

View File

@ -5,32 +5,33 @@
// You can obtain one at https://github.com/gogf/gf.
//
// Package glist provides a concurrent-safe/unsafe doubly linked list.
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
package glist
import (
"bytes"
"container/list"
"encoding/json"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/rwmutex"
)
type (
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
// The switch should be set when its initialization and cannot be changed then.
List struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
list *list.List
}
// Element the item type of the list.
Element = list.Element
)
// New creates and returns a new empty doubly linked list.
func New(safe ...bool) *List {
return &List{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
list: list.New(),
}
}
@ -44,7 +45,7 @@ func NewFrom(array []interface{}, safe ...bool) *List {
l.PushBack(v)
}
return &List{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
list: l,
}
}
@ -52,6 +53,9 @@ 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) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
e = l.list.PushFront(v)
l.mu.Unlock()
return
@ -60,6 +64,9 @@ 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) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
e = l.list.PushBack(v)
l.mu.Unlock()
return
@ -68,6 +75,9 @@ 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{}) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
for _, v := range values {
l.list.PushFront(v)
}
@ -77,6 +87,9 @@ 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{}) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
for _, v := range values {
l.list.PushBack(v)
}
@ -86,20 +99,28 @@ 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{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
if e := l.list.Back(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
// PopFront removes the element from front of <l> and returns the value of the element.
func (l *List) PopFront() (value interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
if e := l.list.Front(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
@ -107,6 +128,11 @@ func (l *List) PopFront() (value interface{}) {
// and returns values of the removed elements as slice.
func (l *List) PopBacks(max int) (values []interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
@ -117,7 +143,6 @@ func (l *List) PopBacks(max int) (values []interface{}) {
values[i] = l.list.Remove(l.list.Back())
}
}
l.mu.Unlock()
return
}
@ -125,6 +150,11 @@ func (l *List) PopBacks(max int) (values []interface{}) {
// and returns values of the removed elements as slice.
func (l *List) PopFronts(max int) (values []interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
@ -135,7 +165,6 @@ func (l *List) PopFronts(max int) (values []interface{}) {
values[i] = l.list.Remove(l.list.Front())
}
}
l.mu.Unlock()
return
}
@ -154,6 +183,10 @@ func (l *List) PopFrontAll() []interface{} {
// FrontAll copies and returns values of all elements from front of <l> as slice.
func (l *List) FrontAll() (values []interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
@ -161,13 +194,16 @@ func (l *List) FrontAll() (values []interface{}) {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// BackAll copies and returns values of all elements from back of <l> as slice.
func (l *List) BackAll() (values []interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
@ -175,43 +211,54 @@ func (l *List) BackAll() (values []interface{}) {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// FrontValue returns value of the first element of <l> or nil if the list is empty.
func (l *List) FrontValue() (value interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
if e := l.list.Front(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// BackValue returns value of the last element of <l> or nil if the list is empty.
func (l *List) BackValue() (value interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
if e := l.list.Back(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// Front returns the first element of list <l> or nil if the list is empty.
func (l *List) Front() (e *Element) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
e = l.list.Front()
l.mu.RUnlock()
return
}
// Back returns the last element of list <l> or nil if the list is empty.
func (l *List) Back() (e *Element) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
e = l.list.Back()
l.mu.RUnlock()
return
}
@ -219,8 +266,11 @@ func (l *List) Back() (e *Element) {
// The complexity is O(1).
func (l *List) Len() (length int) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length = l.list.Len()
l.mu.RUnlock()
return
}
@ -234,8 +284,11 @@ func (l *List) Size() int {
// The element and <p> must not be nil.
func (l *List) MoveBefore(e, p *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveBefore(e, p)
l.mu.Unlock()
}
// MoveAfter moves element <e> to its new position after <p>.
@ -243,8 +296,11 @@ func (l *List) MoveBefore(e, p *Element) {
// The element and <p> must not be nil.
func (l *List) MoveAfter(e, p *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveAfter(e, p)
l.mu.Unlock()
}
// MoveToFront moves element <e> to the front of list <l>.
@ -252,8 +308,11 @@ func (l *List) MoveAfter(e, p *Element) {
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveToFront(e)
l.mu.Unlock()
}
// MoveToBack moves element <e> to the back of list <l>.
@ -261,8 +320,11 @@ func (l *List) MoveToFront(e *Element) {
// The element must not be nil.
func (l *List) MoveToBack(e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveToBack(e)
l.mu.Unlock()
}
// PushBackList inserts a copy of an other list at the back of list <l>.
@ -273,8 +335,11 @@ func (l *List) PushBackList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.PushBackList(other.list)
l.mu.Unlock()
}
// PushFrontList inserts a copy of an other list at the front of list <l>.
@ -285,8 +350,11 @@ func (l *List) PushFrontList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.PushFrontList(other.list)
l.mu.Unlock()
}
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
@ -294,8 +362,11 @@ func (l *List) PushFrontList(other *List) {
// The <p> must not be nil.
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
e = l.list.InsertAfter(v, p)
l.mu.Unlock()
return
}
@ -304,8 +375,11 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
// The <p> must not be nil.
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
e = l.list.InsertBefore(v, p)
l.mu.Unlock()
return
}
@ -314,18 +388,24 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
// The element must not be nil.
func (l *List) Remove(e *Element) (value interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
value = l.list.Remove(e)
l.mu.Unlock()
return
}
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
func (l *List) Removes(es []*Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
for _, e := range es {
l.list.Remove(e)
}
l.mu.Unlock()
return
}
@ -345,13 +425,18 @@ func (l *List) Clear() {
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
if l.list != nil {
f(l.list)
}
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
f(l.list)
}
@ -360,11 +445,14 @@ func (l *List) Iterator(f func(e *Element) bool) {
l.IteratorAsc(f)
}
// IteratorAsc iterates the list in ascending order with given callback function <f>.
// IteratorAsc iterates the list readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorAsc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
@ -375,11 +463,14 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
}
}
// IteratorDesc iterates the list in descending order with given callback function <f>.
// IteratorDesc iterates the list readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorDesc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
@ -394,17 +485,14 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
func (l *List) Join(glue string) string {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return ""
}
buffer := bytes.NewBuffer(nil)
length := l.list.Len()
if length > 0 {
s := ""
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
s = gconv.String(e.Value)
if gstr.IsNumeric(s) {
buffer.WriteString(s)
} else {
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
}
buffer.WriteString(gconv.String(e.Value))
if i != length-1 {
buffer.WriteString(glue)
}
@ -425,12 +513,11 @@ func (l *List) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (l *List) UnmarshalJSON(b []byte) error {
if l.mu == nil {
l.mu = rwmutex.New()
l.list = list.New()
}
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -441,12 +528,11 @@ 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) {
if l.mu == nil {
l.mu = rwmutex.New()
l.list = list.New()
}
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
switch value.(type) {
case string, []byte:

View File

@ -10,11 +10,12 @@ import (
"container/list"
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/glist"
)
func Example_basic() {
func ExampleNew() {
n := 10
l := glist.New()
for i := 0; i < n; i++ {
@ -31,14 +32,14 @@ func Example_basic() {
fmt.Println(l.Len())
// Output:
//10
//[0 1 2 3 4 5 6 7 8 9]
//[9 8 7 6 5 4 3 2 1 0]
//0123456789
//0
// 10
// [0 1 2 3 4 5 6 7 8 9]
// [9 8 7 6 5 4 3 2 1 0]
// 0123456789
// 0
}
func Example_iterate() {
func ExampleList_RLockFunc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from head.
@ -62,21 +63,39 @@ func Example_iterate() {
})
fmt.Println()
// Output:
// 12345678910
// 10987654321
}
func ExampleList_IteratorAsc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from head using IteratorAsc.
l.IteratorAsc(func(e *glist.Element) bool {
fmt.Print(e.Value)
return true
})
fmt.Println()
// Output:
// 12345678910
}
func ExampleList_IteratorDesc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from tail using IteratorDesc.
l.IteratorDesc(func(e *glist.Element) bool {
fmt.Print(e.Value)
return true
})
// Output:
// 10987654321
}
fmt.Println()
func ExampleList_LockFunc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate writing from head.
l.LockFunc(func(list *list.List) {
length := list.Len()
@ -91,10 +110,51 @@ func Example_iterate() {
})
fmt.Println(l)
//output:
//12345678910
//10987654321
//12345678910
//10987654321
//[1,2,3,4,5,"M",7,8,9,10]
// Output:
// [1,2,3,4,5,M,7,8,9,10]
}
func ExampleList_PopBack() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBack())
// Output:
// 9
}
func ExampleList_PopBacks() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBacks(2))
// Output:
// [9 8]
}
func ExampleList_PopFront() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopFront())
// Output:
// 1
}
func ExampleList_PopFronts() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopFronts(2))
// Output:
// [1 2]
}
func ExampleList_Join() {
var l glist.List
l.PushBacks(g.Slice{"a", "b", "c", "d"})
fmt.Println(l.Join(","))
// Output:
// a,b,c,d
}

View File

@ -8,14 +8,13 @@ package glist
import (
"container/list"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"
)
// 检查链表长度
func checkListLen(t *gtest.T, l *List, len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
@ -24,7 +23,6 @@ func checkListLen(t *gtest.T, l *List, len int) bool {
return true
}
// 检查指针地址
func checkListPointers(t *gtest.T, l *List, es []*Element) {
if !checkListLen(t, l, len(es)) {
return
@ -41,6 +39,44 @@ func checkListPointers(t *gtest.T, l *List, es []*Element) {
})
}
func TestVar(t *testing.T) {
var l List
l.PushFront(1)
l.PushFront(2)
if v := l.PopBack(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l.PushBack(1)
l.PushBack(2)
if v := l.PopFront(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
}
func TestBasic(t *testing.T) {
l := New()
l.PushFront(1)
@ -567,6 +603,17 @@ func TestList_Removes(t *testing.T) {
})
}
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})
t.Assert(l.PopBack(), 9)
t.Assert(l.PopBacks(2), []interface{}{8, 7})
t.Assert(l.PopFront(), 1)
t.Assert(l.PopFronts(2), []interface{}{2, 3})
})
}
func TestList_Clear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
@ -634,15 +681,15 @@ func TestList_Iterator(t *testing.T) {
func TestList_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
t.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
t.Assert(l.Join("."), `1.2."a"."\"b\""."\\c"`)
t.Assert(l.Join(","), `1,2,a,"b",\c`)
t.Assert(l.Join("."), `1.2.a."b".\c`)
})
}
func TestList_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
t.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
t.Assert(l.String(), `[1,2,a,"b",\c]`)
})
}

View File

@ -4,12 +4,13 @@
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
// Package gmap provides concurrent-safe/unsafe map containers.
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
package gmap
// Map based on hash table, alias of AnyAnyMap.
type Map = AnyAnyMap
type HashMap = AnyAnyMap
type (
Map = AnyAnyMap // Map is alias of AnyAnyMap.
HashMap = AnyAnyMap // HashMap is alias of AnyAnyMap.
)
// New creates and returns an empty hash map.
// The parameter <safe> is used to specify whether using map in concurrent-safety,

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -18,7 +18,7 @@ import (
)
type AnyAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]interface{}
}
@ -27,7 +27,7 @@ type AnyAnyMap struct {
// which is false in default.
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[interface{}]interface{}),
}
}
@ -37,12 +37,12 @@ func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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) {
m.mu.RLock()
@ -89,37 +89,56 @@ func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
m.mu.RUnlock()
return data
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *AnyAnyMap) FilterEmpty() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsEmpty(v) {
delete(m.data, k)
}
}
m.mu.Unlock()
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *AnyAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
m.data[key] = val
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
m.data[key] = value
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -128,17 +147,21 @@ func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *AnyAnyMap) Get(key interface{}) interface{} {
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -163,8 +186,10 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[interface{}]interface{}, size)
var (
index = 0
newMap = make(map[interface{}]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -188,6 +213,9 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -235,26 +263,26 @@ func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) inte
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -293,21 +321,25 @@ 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{}) interface{} {
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Removes batch deletes values of the map by keys.
func (m *AnyAnyMap) Removes(keys []interface{}) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
@ -315,36 +347,43 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice.
func (m *AnyAnyMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, len(m.data))
index := 0
defer m.mu.RUnlock()
var (
keys = make([]interface{}, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *AnyAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
defer m.mu.RUnlock()
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *AnyAnyMap) Contains(key interface{}) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -405,6 +444,10 @@ func (m *AnyAnyMap) Flip() {
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -414,6 +457,12 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
}
}
// String returns the map as a string.
func (m *AnyAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
return json.Marshal(gconv.Map(m.Map()))
@ -421,12 +470,11 @@ func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[interface{}]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
@ -439,12 +487,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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[interface{}]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
for k, v := range gconv.Map(value) {
m.data[k] = v
}

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -18,7 +18,7 @@ import (
)
type IntAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]interface{}
}
@ -27,7 +27,7 @@ type IntAnyMap struct {
// which is false in default.
func NewIntAnyMap(safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]interface{}),
}
}
@ -37,12 +37,12 @@ func NewIntAnyMap(safe ...bool) *IntAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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) {
m.mu.RLock()
@ -98,6 +98,7 @@ func (m *IntAnyMap) MapCopy() map[int]interface{} {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntAnyMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -108,9 +109,23 @@ func (m *IntAnyMap) FilterEmpty() {
m.mu.Unlock()
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *IntAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *IntAnyMap) Set(key int, val interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]interface{})
}
m.data[key] = val
m.mu.Unlock()
}
@ -118,8 +133,12 @@ func (m *IntAnyMap) Set(key int, val interface{}) {
// Sets batch sets key-values to the hash map.
func (m *IntAnyMap) Sets(data map[int]interface{}) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -128,17 +147,21 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntAnyMap) Get(key int) interface{} {
func (m *IntAnyMap) Get(key int) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -163,8 +186,10 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]interface{}, size)
var (
index = 0
newMap = make(map[int]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -188,6 +213,9 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -233,26 +261,26 @@ func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{}
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -293,28 +321,34 @@ func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
// Removes batch deletes values of the map by keys.
func (m *IntAnyMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntAnyMap) Remove(key int) interface{} {
func (m *IntAnyMap) Remove(key int) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *IntAnyMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -326,8 +360,10 @@ func (m *IntAnyMap) Keys() []int {
// Values returns all values of the map as a slice.
func (m *IntAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -339,10 +375,13 @@ func (m *IntAnyMap) Values() []interface{} {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntAnyMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -403,6 +442,10 @@ func (m *IntAnyMap) Flip() {
func (m *IntAnyMap) Merge(other *IntAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -412,6 +455,12 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
}
}
// String returns the map as a string.
func (m *IntAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -421,12 +470,11 @@ func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -435,12 +483,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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/empty"
@ -16,7 +16,7 @@ import (
)
type IntIntMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]int
}
@ -25,7 +25,7 @@ type IntIntMap struct {
// which is false in default.
func NewIntIntMap(safe ...bool) *IntIntMap {
return &IntIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]int),
}
}
@ -35,12 +35,12 @@ func NewIntIntMap(safe ...bool) *IntIntMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
return &IntIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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 *IntIntMap) Iterator(f func(k int, v int) bool) {
m.mu.RLock()
@ -96,6 +96,7 @@ func (m *IntIntMap) MapCopy() map[int]int {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntIntMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -109,6 +110,9 @@ func (m *IntIntMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *IntIntMap) Set(key int, val int) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]int)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +120,12 @@ func (m *IntIntMap) Set(key int, val int) {
// Sets batch sets key-values to the hash map.
func (m *IntIntMap) Sets(data map[int]int) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +134,21 @@ func (m *IntIntMap) Sets(data map[int]int) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *IntIntMap) Search(key int) (value int, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntIntMap) Get(key int) int {
func (m *IntIntMap) Get(key int) (value int) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +173,10 @@ func (m *IntIntMap) Pops(size int) map[int]int {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]int, size)
var (
index = 0
newMap = make(map[int]int, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -181,12 +195,14 @@ func (m *IntIntMap) Pops(size int) map[int]int {
// It returns value with given <key>.
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
@ -219,6 +235,9 @@ func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if v, ok = m.data[key]; ok {
return v
}
@ -259,6 +278,9 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -270,28 +292,34 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
// Removes batch deletes values of the map by keys.
func (m *IntIntMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntIntMap) Remove(key int) int {
func (m *IntIntMap) Remove(key int) (value int) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *IntIntMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -303,8 +331,10 @@ func (m *IntIntMap) Keys() []int {
// Values returns all values of the map as a slice.
func (m *IntIntMap) Values() []int {
m.mu.RLock()
values := make([]int, len(m.data))
index := 0
var (
values = make([]int, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -316,10 +346,13 @@ func (m *IntIntMap) Values() []int {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntIntMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -380,6 +413,10 @@ func (m *IntIntMap) Flip() {
func (m *IntIntMap) Merge(other *IntIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -389,6 +426,12 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
}
}
// String returns the map as a string.
func (m *IntIntMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntIntMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -398,12 +441,11 @@ func (m *IntIntMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntIntMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -412,12 +454,11 @@ 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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -16,7 +16,7 @@ import (
)
type IntStrMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]string
}
@ -25,7 +25,7 @@ type IntStrMap struct {
// which is false in default.
func NewIntStrMap(safe ...bool) *IntStrMap {
return &IntStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]string),
}
}
@ -35,12 +35,12 @@ func NewIntStrMap(safe ...bool) *IntStrMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
return &IntStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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 *IntStrMap) Iterator(f func(k int, v string) bool) {
m.mu.RLock()
@ -96,6 +96,7 @@ func (m *IntStrMap) MapCopy() map[int]string {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntStrMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -109,6 +110,9 @@ func (m *IntStrMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *IntStrMap) Set(key int, val string) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]string)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +120,12 @@ func (m *IntStrMap) Set(key int, val string) {
// Sets batch sets key-values to the hash map.
func (m *IntStrMap) Sets(data map[int]string) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +134,21 @@ func (m *IntStrMap) Sets(data map[int]string) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *IntStrMap) Search(key int) (value string, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntStrMap) Get(key int) string {
func (m *IntStrMap) Get(key int) (value string) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +173,10 @@ func (m *IntStrMap) Pops(size int) map[int]string {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]string, size)
var (
index = 0
newMap = make(map[int]string, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -182,6 +196,9 @@ func (m *IntStrMap) Pops(size int) map[int]string {
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if v, ok := m.data[key]; ok {
return v
}
@ -218,13 +235,14 @@ func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if v, ok = m.data[key]; ok {
return v
}
v = f()
if v != "" {
m.data[key] = v
}
m.data[key] = v
return v
} else {
return v
@ -260,6 +278,9 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -271,28 +292,34 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
// Removes batch deletes values of the map by keys.
func (m *IntStrMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntStrMap) Remove(key int) string {
func (m *IntStrMap) Remove(key int) (value string) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *IntStrMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -304,8 +331,10 @@ func (m *IntStrMap) Keys() []int {
// Values returns all values of the map as a slice.
func (m *IntStrMap) Values() []string {
m.mu.RLock()
values := make([]string, len(m.data))
index := 0
var (
values = make([]string, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -317,10 +346,13 @@ func (m *IntStrMap) Values() []string {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntStrMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -381,6 +413,10 @@ func (m *IntStrMap) Flip() {
func (m *IntStrMap) Merge(other *IntStrMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -390,6 +426,12 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
}
}
// String returns the map as a string.
func (m *IntStrMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntStrMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -399,12 +441,11 @@ func (m *IntStrMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -413,12 +454,11 @@ 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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -18,7 +18,7 @@ import (
)
type StrAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]interface{}
}
@ -27,7 +27,7 @@ type StrAnyMap struct {
// which is false in default.
func NewStrAnyMap(safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]interface{}),
}
}
@ -37,12 +37,12 @@ func NewStrAnyMap(safe ...bool) *StrAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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) {
m.mu.RLock()
@ -92,6 +92,7 @@ func (m *StrAnyMap) MapCopy() map[string]interface{} {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrAnyMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -102,9 +103,23 @@ func (m *StrAnyMap) FilterEmpty() {
m.mu.Unlock()
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *StrAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *StrAnyMap) Set(key string, val interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]interface{})
}
m.data[key] = val
m.mu.Unlock()
}
@ -112,8 +127,12 @@ func (m *StrAnyMap) Set(key string, val interface{}) {
// Sets batch sets key-values to the hash map.
func (m *StrAnyMap) Sets(data map[string]interface{}) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -122,17 +141,21 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrAnyMap) Get(key string) interface{} {
func (m *StrAnyMap) Get(key string) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -157,8 +180,10 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]interface{}, size)
var (
index = 0
newMap = make(map[string]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -182,6 +207,9 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -229,26 +257,26 @@ func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// 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 {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -289,28 +317,34 @@ func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool
// Removes batch deletes values of the map by keys.
func (m *StrAnyMap) Removes(keys []string) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrAnyMap) Remove(key string) interface{} {
func (m *StrAnyMap) Remove(key string) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *StrAnyMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
index := 0
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -322,8 +356,10 @@ func (m *StrAnyMap) Keys() []string {
// Values returns all values of the map as a slice.
func (m *StrAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -335,10 +371,13 @@ func (m *StrAnyMap) Values() []interface{} {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrAnyMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -399,6 +438,10 @@ func (m *StrAnyMap) Flip() {
func (m *StrAnyMap) Merge(other *StrAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -408,6 +451,12 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
}
}
// String returns the map as a string.
func (m *StrAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -417,12 +466,11 @@ func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -431,10 +479,6 @@ 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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.Map(value)

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/rwmutex"
@ -16,7 +16,7 @@ import (
)
type StrIntMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]int
}
@ -25,7 +25,7 @@ type StrIntMap struct {
// which is false in default.
func NewStrIntMap(safe ...bool) *StrIntMap {
return &StrIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]int),
}
}
@ -35,12 +35,12 @@ func NewStrIntMap(safe ...bool) *StrIntMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
return &StrIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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 *StrIntMap) Iterator(f func(k string, v int) bool) {
m.mu.RLock()
@ -76,11 +76,11 @@ func (m *StrIntMap) Map() map[string]int {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrIntMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
@ -96,6 +96,7 @@ func (m *StrIntMap) MapCopy() map[string]int {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrIntMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -109,6 +110,9 @@ func (m *StrIntMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *StrIntMap) Set(key string, val int) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]int)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +120,12 @@ func (m *StrIntMap) Set(key string, val int) {
// Sets batch sets key-values to the hash map.
func (m *StrIntMap) Sets(data map[string]int) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +134,21 @@ func (m *StrIntMap) Sets(data map[string]int) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *StrIntMap) Search(key string) (value int, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrIntMap) Get(key string) int {
func (m *StrIntMap) Get(key string) (value int) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +173,10 @@ func (m *StrIntMap) Pops(size int) map[string]int {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]int, size)
var (
index = 0
newMap = make(map[string]int, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -181,6 +195,9 @@ func (m *StrIntMap) Pops(size int) map[string]int {
// It returns value with given <key>.
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]int)
}
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
@ -221,6 +238,9 @@ func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
if v, ok = m.data[key]; ok {
return v
}
@ -261,6 +281,9 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -272,28 +295,34 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
// Removes batch deletes values of the map by keys.
func (m *StrIntMap) Removes(keys []string) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrIntMap) Remove(key string) int {
func (m *StrIntMap) Remove(key string) (value int) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *StrIntMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
index := 0
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -305,8 +334,10 @@ func (m *StrIntMap) Keys() []string {
// Values returns all values of the map as a slice.
func (m *StrIntMap) Values() []int {
m.mu.RLock()
values := make([]int, len(m.data))
index := 0
var (
values = make([]int, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -318,10 +349,13 @@ func (m *StrIntMap) Values() []int {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrIntMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -382,6 +416,10 @@ func (m *StrIntMap) Flip() {
func (m *StrIntMap) Merge(other *StrIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -391,6 +429,12 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
}
}
// String returns the map as a string.
func (m *StrIntMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrIntMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -400,12 +444,11 @@ func (m *StrIntMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrIntMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -414,12 +457,11 @@ 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) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/empty"
@ -17,7 +17,7 @@ import (
)
type StrStrMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]string
}
@ -27,7 +27,7 @@ type StrStrMap struct {
func NewStrStrMap(safe ...bool) *StrStrMap {
return &StrStrMap{
data: make(map[string]string),
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
@ -36,12 +36,12 @@ func NewStrStrMap(safe ...bool) *StrStrMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
return &StrStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// 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 *StrStrMap) Iterator(f func(k string, v string) bool) {
m.mu.RLock()
@ -97,6 +97,7 @@ func (m *StrStrMap) MapCopy() map[string]string {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrStrMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -110,6 +111,9 @@ func (m *StrStrMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *StrStrMap) Set(key string, val string) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]string)
}
m.data[key] = val
m.mu.Unlock()
}
@ -117,8 +121,12 @@ func (m *StrStrMap) Set(key string, val string) {
// Sets batch sets key-values to the hash map.
func (m *StrStrMap) Sets(data map[string]string) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -127,17 +135,21 @@ func (m *StrStrMap) Sets(data map[string]string) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *StrStrMap) Search(key string) (value string, found bool) {
m.mu.RLock()
value, found = m.data[key]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrStrMap) Get(key string) string {
func (m *StrStrMap) Get(key string) (value string) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -162,8 +174,10 @@ func (m *StrStrMap) Pops(size int) map[string]string {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]string, size)
var (
index = 0
newMap = make(map[string]string, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -183,6 +197,9 @@ func (m *StrStrMap) Pops(size int) map[string]string {
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if v, ok := m.data[key]; ok {
return v
}
@ -221,13 +238,14 @@ func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if v, ok = m.data[key]; ok {
return v
}
v = f()
if v != "" {
m.data[key] = v
}
m.data[key] = v
return v
} else {
return v
@ -263,6 +281,9 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -274,28 +295,34 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
// Removes batch deletes values of the map by keys.
func (m *StrStrMap) Removes(keys []string) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrStrMap) Remove(key string) string {
func (m *StrStrMap) Remove(key string) (value string) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// Keys returns all keys of the map as a slice.
func (m *StrStrMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
index := 0
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -307,8 +334,10 @@ func (m *StrStrMap) Keys() []string {
// Values returns all values of the map as a slice.
func (m *StrStrMap) Values() []string {
m.mu.RLock()
values := make([]string, len(m.data))
index := 0
var (
values = make([]string, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -320,10 +349,13 @@ func (m *StrStrMap) Values() []string {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrStrMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -384,6 +416,10 @@ func (m *StrStrMap) Flip() {
func (m *StrStrMap) Merge(other *StrStrMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -393,6 +429,12 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
}
}
// String returns the map as a string.
func (m *StrStrMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrStrMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()
@ -402,12 +444,11 @@ func (m *StrStrMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrStrMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -416,9 +457,6 @@ func (m *StrStrMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
}
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.MapStrStr(value)

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -19,7 +19,7 @@ import (
)
type ListMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]*glist.Element
list *glist.List
}
@ -35,7 +35,7 @@ type gListMapNode struct {
// which is false in default.
func NewListMap(safe ...bool) *ListMap {
return &ListMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[interface{}]*glist.Element),
list: glist.New(),
}
@ -55,28 +55,32 @@ func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
m.IteratorAsc(f)
}
// IteratorAsc iterates the map in ascending order with given callback function <f>.
// IteratorAsc iterates the map readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
if m.list != nil {
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
}
// IteratorDesc iterates the map in descending order with given callback function <f>.
// IteratorDesc iterates the map readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorDesc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
if m.list != nil {
node := (*gListMapNode)(nil)
m.list.IteratorDesc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
}
// Clone returns a new link map with copy of current map data.
@ -110,13 +114,16 @@ func (m *ListMap) Replace(data map[interface{}]interface{}) {
// Map returns a copy of the underlying data of the map.
func (m *ListMap) Map() map[interface{}]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
data := make(map[interface{}]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[node.key] = node.value
return true
})
var node *gListMapNode
var data map[interface{}]interface{}
if m.list != nil {
data = make(map[interface{}]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[node.key] = node.value
return true
})
}
m.mu.RUnlock()
return data
}
@ -124,13 +131,16 @@ func (m *ListMap) Map() map[interface{}]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *ListMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
data := make(map[string]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[gconv.String(node.key)] = node.value
return true
})
var node *gListMapNode
var data map[string]interface{}
if m.list != nil {
data = make(map[string]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[gconv.String(node.key)] = node.value
return true
})
}
m.mu.RUnlock()
return data
}
@ -138,20 +148,22 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
// FilterEmpty deletes all key-value pair of which the value is empty.
func (m *ListMap) FilterEmpty() {
m.mu.Lock()
keys := make([]interface{}, 0)
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if empty.IsEmpty(node.value) {
keys = append(keys, node.key)
}
return true
})
if len(keys) > 0 {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
if m.list != nil {
keys := make([]interface{}, 0)
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if empty.IsEmpty(node.value) {
keys = append(keys, node.key)
}
return true
})
if len(keys) > 0 {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
}
}
}
}
@ -161,6 +173,10 @@ func (m *ListMap) FilterEmpty() {
// Set sets key-value to the map.
func (m *ListMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
@ -172,6 +188,10 @@ func (m *ListMap) Set(key interface{}, value interface{}) {
// Sets batch sets key-values to the map.
func (m *ListMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
@ -186,9 +206,11 @@ func (m *ListMap) Sets(data map[interface{}]interface{}) {
// Second return parameter <found> is true if key was found, otherwise false.
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
found = ok
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
found = ok
}
}
m.mu.RUnlock()
return
@ -197,8 +219,10 @@ func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
// Get returns the value by given <key>.
func (m *ListMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
}
}
m.mu.RUnlock()
return
@ -255,6 +279,10 @@ func (m *ListMap) Pops(size int) map[interface{}]interface{} {
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if e, ok := m.data[key]; ok {
return e.Value.(*gListMapNode).value
}
@ -302,26 +330,26 @@ func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interf
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -362,10 +390,12 @@ func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) b
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *ListMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
}
}
m.mu.Unlock()
return
@ -374,10 +404,12 @@ func (m *ListMap) Remove(key interface{}) (value interface{}) {
// Removes batch deletes values of the map by keys.
func (m *ListMap) Removes(keys []interface{}) {
m.mu.Lock()
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
if m.data != nil {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
}
}
}
m.mu.Unlock()
@ -386,13 +418,17 @@ func (m *ListMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice in ascending order.
func (m *ListMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
var (
keys = make([]interface{}, m.list.Len())
index = 0
)
if m.list != nil {
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
}
m.mu.RUnlock()
return keys
}
@ -400,13 +436,17 @@ func (m *ListMap) Keys() []interface{} {
// Values returns all values of the map as a slice.
func (m *ListMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
values[index] = e.Value.(*gListMapNode).value
index++
return true
})
var (
values = make([]interface{}, m.list.Len())
index = 0
)
if m.list != nil {
m.list.IteratorAsc(func(e *glist.Element) bool {
values[index] = e.Value.(*gListMapNode).value
index++
return true
})
}
m.mu.RUnlock()
return values
}
@ -415,7 +455,9 @@ func (m *ListMap) Values() []interface{} {
// It returns true if the <key> exists, or else false.
func (m *ListMap) Contains(key interface{}) (ok bool) {
m.mu.RLock()
_, ok = m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return
}
@ -448,6 +490,10 @@ func (m *ListMap) Flip() {
func (m *ListMap) Merge(other *ListMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -464,6 +510,12 @@ func (m *ListMap) Merge(other *ListMap) {
})
}
// String returns the map as a string.
func (m *ListMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *ListMap) MarshalJSON() ([]byte, error) {
return json.Marshal(gconv.Map(m.Map()))
@ -471,13 +523,12 @@ func (m *ListMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *ListMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
m.mu.Lock()
defer m.mu.Unlock()
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
@ -494,13 +545,12 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range gconv.Map(value) {
if e, ok := m.data[k]; !ok {
m.data[k] = m.list.PushBack(&gListMapNode{k, v})

View File

@ -7,6 +7,7 @@
package gmap_test
import (
"github.com/gogf/gf/util/gutil"
"testing"
"github.com/gogf/gf/container/gmap"
@ -17,6 +18,55 @@ func getValue() interface{} {
return 3
}
func Test_Map_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.Map
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntAnyMap
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntIntMap
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntStrMap
m.Set(1, "11")
t.Assert(m.Get(1), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrAnyMap
m.Set("1", "11")
t.Assert(m.Get("1"), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrStrMap
m.Set("1", "11")
t.Assert(m.Get("1"), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrIntMap
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.ListMap
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.TreeMap
m.SetComparator(gutil.ComparatorString)
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
}
func Test_Map_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.New()

View File

@ -0,0 +1,207 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/gmap"
)
func ExampleNew() {
m := gmap.New()
// Add data.
m.Set("key1", "val1")
// Print size.
fmt.Println(m.Size())
addMap := make(map[interface{}]interface{})
addMap["key2"] = "val2"
addMap["key3"] = "val3"
addMap[1] = 1
fmt.Println(m.Values())
// Batch add data.
m.Sets(addMap)
// Gets the value of the corresponding key.
fmt.Println(m.Get("key3"))
// Get the value by key, or set it with given key-value if not exist.
fmt.Println(m.GetOrSet("key4", "val4"))
// Set key-value if the key does not exist, then return true; or else return false.
fmt.Println(m.SetIfNotExist("key3", "val3"))
// Remove key
m.Remove("key2")
fmt.Println(m.Keys())
// Batch remove keys.
m.Removes([]interface{}{"key1", 1})
fmt.Println(m.Keys())
// Contains checks whether a key exists.
fmt.Println(m.Contains("key3"))
// Flip exchanges key-value of the map, it will change key-value to value-key.
m.Flip()
fmt.Println(m.Map())
// Clear deletes all data of the map.
m.Clear()
fmt.Println(m.Size())
// May Output:
// 1
// [val1]
// val3
// val4
// false
// [key4 key1 key3 1]
// [key4 key3]
// true
// map[val3:key3 val4:key4]
// 0
}
func ExampleAnyAnyMap_Keys() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func ExampleAnyAnyMap_Values() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func ExampleAnyAnyMap_Flip() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
m.Flip()
fmt.Println(m.Map())
// May Output:
// map[v1:k1 v2:k2]
}
func ExampleAnyAnyMap_Pop() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func ExampleAnyAnyMap_Pops() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func ExampleAnyAnyMap_FilterEmpty() {
m := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m.FilterEmpty()
fmt.Println(m.Map())
// May Output:
// map[k4:1]
}
func ExampleAnyAnyMap_FilterNil() {
m := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m.FilterNil()
fmt.Println(m.Map())
// May Output:
// map[k1: k3:0 k4:1]
}
func ExampleAnyAnyMap_SetIfNotExist() {
var m gmap.Map
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.Map())
// Output:
// true
// false
// map[k1:v1]
}
func ExampleAnyAnyMap_Merge() {
var m1, m2 gmap.Map
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(&m2)
fmt.Println(m1.Map())
// May Output:
// map[key1:val1 key2:val2]
}

View File

@ -1,71 +0,0 @@
package gmap_test
import (
"fmt"
"github.com/gogf/gf/container/gmap"
)
func Example_normalBasic() {
m := gmap.New()
//Add data
m.Set("key1", "val1")
//Print size
fmt.Println(m.Size())
//output 1
add_map := make(map[interface{}]interface{})
add_map["key2"] = "val2"
add_map["key3"] = "val3"
add_map[1] = 1
fmt.Println(m.Values())
//Batch add data
m.Sets(add_map)
//Gets the value of the corresponding key
key3_val := m.Get("key3")
fmt.Println(key3_val)
//Get the value by key, or set it with given key-value if not exist.
get_or_set_val := m.GetOrSet("key4", "val4")
fmt.Println(get_or_set_val)
// Set key-value if the key does not exist, then return true; or else return false.
is_set := m.SetIfNotExist("key3", "val3")
fmt.Println(is_set)
//Remove key
m.Remove("key2")
fmt.Println(m.Keys())
//Batch remove keys
remove_keys := []interface{}{"key1", 1}
m.Removes(remove_keys)
fmt.Println(m.Keys())
//Contains checks whether a key exists.
is_contain := m.Contains("key3")
fmt.Println(is_contain)
//Flip exchanges key-value of the map, it will change key-value to value-key.
m.Flip()
fmt.Println(m.Map())
// Clear deletes all data of the map,
m.Clear()
fmt.Println(m.Size())
}
func Example_normalMerge() {
m1 := gmap.New()
m2 := gmap.New()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
fmt.Println(m1.Map())
}

View File

@ -7,18 +7,44 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/test/gtest"
"time"
)
func anyAnyCallBack(int, interface{}) bool {
return true
func Test_AnyAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.AnyAnyMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_AnyAnyMap_Basic(t *testing.T) {
@ -192,6 +218,18 @@ func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
t.Assert(m.Get(1), nil)
t.Assert(m.Get(2), 2)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set("time1", time.Time{})
m.Set("time2", time.Now())
t.Assert(m.Get(1), 0)
t.Assert(m.Get("time1"), time.Time{})
m.FilterEmpty()
t.Assert(m.Get(1), nil)
t.Assert(m.Get("time1"), nil)
t.AssertNE(m.Get("time2"), nil)
})
}
func Test_AnyAnyMap_Json(t *testing.T) {

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -20,9 +20,37 @@ import (
func getAny() interface{} {
return 123
}
func intAnyCallBack(int, interface{}) bool {
return true
func Test_IntAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntAnyMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntAnyMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
@ -55,6 +83,7 @@ func Test_IntAnyMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
})
}
func Test_IntAnyMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -20,9 +20,41 @@ import (
func getInt() int {
return 123
}
func intIntCallBack(int, int) bool {
return true
}
func Test_IntIntMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntIntMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, 2), 2)
t.Assert(m.SetIfNotExist(2, 2), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), 2)
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntIntMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
@ -55,6 +87,7 @@ func Test_IntIntMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
})
}
func Test_IntIntMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -20,9 +20,40 @@ import (
func getStr() string {
return "z"
}
func intStrCallBack(int, string) bool {
return true
func Test_IntStrMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntStrMap
m.Set(1, "a")
t.Assert(m.Get(1), "a")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "b"), "b")
t.Assert(m.SetIfNotExist(2, "b"), false)
t.Assert(m.SetIfNotExist(3, "c"), true)
t.Assert(m.Remove(2), "b")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN("a", m.Values())
t.AssertIN("c", m.Values())
m_f := gmap.NewIntStrMap()
m_f.Set(1, "2")
m_f.Flip()
t.Assert(m_f.Map(), map[int]string{2: "1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntStrMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
@ -60,6 +91,7 @@ func Test_IntStrMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
})
}
func Test_IntStrMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()

View File

@ -7,8 +7,8 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -17,6 +17,38 @@ import (
"github.com/gogf/gf/test/gtest"
)
func Test_ListMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.ListMap
m.Set("key1", "val1")
t.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_ListMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
@ -51,6 +83,7 @@ func Test_ListMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_ListMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -17,9 +17,37 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringAnyCallBack(string, interface{}) bool {
return true
func Test_StrAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrAnyMap
m.Set("a", 1)
t.Assert(m.Get("a"), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", "2"), "2")
t.Assert(m.SetIfNotExist("b", "2"), false)
t.Assert(m.SetIfNotExist("c", 3), true)
t.Assert(m.Remove("b"), "2")
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrAnyMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrAnyMap()
@ -53,6 +81,7 @@ func Test_StrAnyMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
})
}
func Test_StrAnyMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrAnyMap()

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -17,9 +17,39 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringIntCallBack(string, int) bool {
return true
func Test_StrIntMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrIntMap
m.Set("a", 1)
t.Assert(m.Get("a"), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", 2), 2)
t.Assert(m.SetIfNotExist("b", 2), false)
t.Assert(m.SetIfNotExist("c", 3), true)
t.Assert(m.Remove("b"), 2)
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m_f := gmap.NewStrIntMap()
m_f.Set("1", 2)
m_f.Flip()
t.Assert(m_f.Map(), map[string]int{"2": 1})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrIntMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMap()
@ -55,6 +85,7 @@ func Test_StrIntMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
})
}
func Test_StrIntMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMap()

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -17,9 +17,38 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringStrCallBack(string, string) bool {
return true
func Test_StrStrMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrStrMap
m.Set("a", "a")
t.Assert(m.Get("a"), "a")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", "b"), "b")
t.Assert(m.SetIfNotExist("b", "b"), false)
t.Assert(m.SetIfNotExist("c", "c"), true)
t.Assert(m.Remove("b"), "b")
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN("a", m.Values())
t.AssertIN("c", m.Values())
m.Flip()
t.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrStrMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMap()
@ -54,6 +83,7 @@ func Test_StrStrMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
})
}
func Test_StrStrMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMap()

View File

@ -7,8 +7,8 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"
@ -17,6 +17,39 @@ import (
"github.com/gogf/gf/util/gutil"
)
func Test_TreeMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.TreeMap
m.SetComparator(gutil.ComparatorString)
m.Set("key1", "val1")
t.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_TreeMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)
@ -51,6 +84,7 @@ func Test_TreeMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_TreeMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)

View File

@ -170,6 +170,7 @@ func (p *Pool) checkExpireItems() {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
latestExpire = item.expire
// TODO improve the auto-expiration mechanism of the pool.
if item.expire > timestampMilli {
p.list.PushFront(item)
break

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/internal/rwmutex"
)
// Ring is a struct of ring structure.
type Ring struct {
mu *rwmutex.RWMutex
ring *ring.Ring // Underlying ring.
@ -22,6 +23,9 @@ type Ring struct {
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated. It's marked dirty when the size of ring changes.
}
// New creates and returns a Ring structure of <cap> elements.
// The optional parameter <safe> specifies whether using this structure in concurrent safety,
// which is false in default.
func New(cap int, safe ...bool) *Ring {
return &Ring{
mu: rwmutex.New(safe...),

View File

@ -1,3 +1,9 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gring_test
import (

View File

@ -9,14 +9,14 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
)
type Set struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]struct{}
}
@ -31,7 +31,7 @@ func New(safe ...bool) *Set {
func NewSet(safe ...bool) *Set {
return &Set{
data: make(map[interface{}]struct{}),
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
@ -44,13 +44,13 @@ func NewFrom(items interface{}, safe ...bool) *Set {
}
return &Set{
data: m,
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *Set) Iterator(f func(v interface{}) bool) *Set {
func (set *Set) Iterator(f func(v interface{}) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -58,77 +58,113 @@ func (set *Set) Iterator(f func(v interface{}) bool) *Set {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *Set) Add(item ...interface{}) *Set {
func (set *Set) Add(items ...interface{}) {
set.mu.Lock()
for _, v := range item {
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
for _, v := range items {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *Set) AddIfNotExistFunc(item interface{}, f func() interface{}) *Set {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false.
func (set *Set) AddIfNotExist(item interface{}) bool {
if item == nil {
return false
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() interface{}) *Set {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interface{} {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() interface{}); ok {
item = f()
} else {
item = value
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
if item != nil {
set.data[item] = struct{}{}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
// is executed without writing lock.
func (set *Set) AddIfNotExistFunc(item interface{}, f func() bool) bool {
if item == nil {
return false
}
return item
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
// is executed within writing lock.
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() bool) bool {
if item == nil {
return false
}
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *Set) Contains(item interface{}) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// Remove deletes <item> from set.
func (set *Set) Remove(item interface{}) *Set {
func (set *Set) Remove(item interface{}) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -140,18 +176,19 @@ func (set *Set) Size() int {
}
// Clear deletes all items of the set.
func (set *Set) Clear() *Set {
func (set *Set) Clear() {
set.mu.Lock()
set.data = make(map[interface{}]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *Set) Slice() []interface{} {
set.mu.RLock()
i := 0
ret := make([]interface{}, len(set.data))
var (
i = 0
ret = make([]interface{}, len(set.data))
)
for item := range set.data {
ret[i] = item
i++
@ -164,9 +201,14 @@ func (set *Set) Slice() []interface{} {
func (set *Set) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(gconv.String(k))
if i != l-1 {
@ -181,11 +223,13 @@ func (set *Set) Join(glue string) string {
func (set *Set) String() string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
var (
s = ""
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
buffer.WriteByte('[')
s := ""
l := len(set.data)
i := 0
for k, _ := range set.data {
s = gconv.String(k)
if gstr.IsNumeric(s) {
@ -256,7 +300,7 @@ func (set *Set) IsSubsetOf(other *Set) bool {
// Union returns a new set which is the union of <set> and <others>.
// Which means, all the items in <newSet> are in <set> or in <others>.
func (set *Set) Union(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -282,7 +326,7 @@ func (set *Set) Union(others ...*Set) (newSet *Set) {
// Diff returns a new set which is the difference set from <set> to <others>.
// Which means, all the items in <newSet> are in <set> but not in <others>.
func (set *Set) Diff(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -303,7 +347,7 @@ func (set *Set) Diff(others ...*Set) (newSet *Set) {
// Intersect returns a new set which is the intersection from <set> to <others>.
// Which means, all the items in <newSet> are in <set> and also in <others>.
func (set *Set) Intersect(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -328,7 +372,7 @@ func (set *Set) Intersect(others ...*Set) (newSet *Set) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *Set) Complement(full *Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -408,6 +452,18 @@ func (set *Set) Pops(size int) []interface{} {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[interface{}]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *Set) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())
@ -415,12 +471,11 @@ func (set *Set) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *Set) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[interface{}]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
var array []interface{}
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -433,12 +488,11 @@ func (set *Set) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *Set) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[interface{}]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
var array []interface{}
switch value.(type) {
case string, []byte:

View File

@ -9,13 +9,13 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
)
type IntSet struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]struct{}
}
@ -24,7 +24,7 @@ type IntSet struct {
// which is false in default.
func NewIntSet(safe ...bool) *IntSet {
return &IntSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]struct{}),
}
}
@ -36,14 +36,14 @@ func NewIntSetFrom(items []int, safe ...bool) *IntSet {
m[v] = struct{}{}
}
return &IntSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: m,
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
func (set *IntSet) Iterator(f func(v int) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -51,75 +51,102 @@ func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *IntSet) Add(item ...int) *IntSet {
func (set *IntSet) Add(item ...int) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[int]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *IntSet) AddIfNotExistFunc(item int, f func() int) *IntSet {
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false.
func (set *IntSet) AddIfNotExist(item int) bool {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() int) *IntSet {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *IntSet) doAddWithLockCheck(item int, value interface{}) int {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() int); ok {
item = f()
} else {
item = value.(int)
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
set.data[item] = struct{}{}
return item
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *IntSet) AddIfNotExistFunc(item int, f func() bool) bool {
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() bool) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *IntSet) Contains(item int) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// Remove deletes <item> from set.
func (set *IntSet) Remove(item int) *IntSet {
func (set *IntSet) Remove(item int) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -131,18 +158,19 @@ func (set *IntSet) Size() int {
}
// Clear deletes all items of the set.
func (set *IntSet) Clear() *IntSet {
func (set *IntSet) Clear() {
set.mu.Lock()
set.data = make(map[int]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *IntSet) Slice() []int {
set.mu.RLock()
ret := make([]int, len(set.data))
i := 0
var (
i = 0
ret = make([]int, len(set.data))
)
for k, _ := range set.data {
ret[i] = k
i++
@ -155,9 +183,14 @@ func (set *IntSet) Slice() []int {
func (set *IntSet) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(gconv.String(k))
if i != l-1 {
@ -227,7 +260,7 @@ func (set *IntSet) IsSubsetOf(other *IntSet) bool {
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -253,7 +286,7 @@ func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -274,7 +307,7 @@ func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -299,7 +332,7 @@ func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -379,6 +412,18 @@ func (set *IntSet) Pops(size int) []int {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *IntSet) Walk(f func(item int) int) *IntSet {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[int]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *IntSet) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())
@ -386,12 +431,11 @@ func (set *IntSet) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *IntSet) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[int]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
var array []int
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -404,12 +448,11 @@ func (set *IntSet) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[int]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
var array []int
switch value.(type) {
case string, []byte:

View File

@ -9,14 +9,15 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"strings"
)
type StrSet struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]struct{}
}
@ -25,7 +26,7 @@ type StrSet struct {
// which is false in default.
func NewStrSet(safe ...bool) *StrSet {
return &StrSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]struct{}),
}
}
@ -37,14 +38,14 @@ func NewStrSetFrom(items []string, safe ...bool) *StrSet {
m[v] = struct{}{}
}
return &StrSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: m,
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
func (set *StrSet) Iterator(f func(v string) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -52,77 +53,113 @@ func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *StrSet) Add(item ...string) *StrSet {
func (set *StrSet) Add(item ...string) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[string]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *StrSet) AddIfNotExistFunc(item string, f func() string) *StrSet {
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
func (set *StrSet) AddIfNotExist(item string) bool {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() string) *StrSet {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() string); ok {
item = f()
} else {
item = value.(string)
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
if item != "" {
set.data[item] = struct{}{}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool {
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return item
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *StrSet) Contains(item string) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// ContainsI checks whether a value exists in the set with case-insensitively.
// Note that it internally iterates the whole set to do the comparison with case-insensitively.
func (set *StrSet) ContainsI(item string) bool {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
if strings.EqualFold(k, item) {
return true
}
}
return false
}
// Remove deletes <item> from set.
func (set *StrSet) Remove(item string) *StrSet {
func (set *StrSet) Remove(item string) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -134,18 +171,19 @@ func (set *StrSet) Size() int {
}
// Clear deletes all items of the set.
func (set *StrSet) Clear() *StrSet {
func (set *StrSet) Clear() {
set.mu.Lock()
set.data = make(map[string]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *StrSet) Slice() []string {
set.mu.RLock()
ret := make([]string, len(set.data))
i := 0
var (
i = 0
ret = make([]string, len(set.data))
)
for item := range set.data {
ret[i] = item
i++
@ -159,9 +197,14 @@ func (set *StrSet) Slice() []string {
func (set *StrSet) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(k)
if i != l-1 {
@ -176,9 +219,11 @@ func (set *StrSet) Join(glue string) string {
func (set *StrSet) String() string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
if i != l-1 {
@ -243,7 +288,7 @@ func (set *StrSet) IsSubsetOf(other *StrSet) bool {
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -269,7 +314,7 @@ func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -290,7 +335,7 @@ func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -315,7 +360,7 @@ func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -395,6 +440,18 @@ func (set *StrSet) Pops(size int) []string {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *StrSet) Walk(f func(item string) string) *StrSet {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[string]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *StrSet) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())
@ -402,12 +459,11 @@ func (set *StrSet) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *StrSet) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[string]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -420,12 +476,11 @@ func (set *StrSet) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[string]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
switch value.(type) {
case string, []byte:

View File

@ -0,0 +1,166 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/frame/g"
)
func ExampleSet_Intersect() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_Diff() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_Union() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_Complement() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_IsSubsetOf() {
var s1, s2 gset.Set
s1.Add(g.Slice{1, 2, 3}...)
s2.Add(g.Slice{2, 3}...)
fmt.Println(s1.IsSubsetOf(&s2))
fmt.Println(s2.IsSubsetOf(&s1))
// Output:
// false
// true
}
func ExampleSet_AddIfNotExist() {
var set gset.Set
fmt.Println(set.AddIfNotExist(1))
fmt.Println(set.AddIfNotExist(1))
fmt.Println(set.Slice())
// Output:
// true
// false
// [1]
}
func ExampleSet_Pop() {
var set gset.Set
set.Add(1, 2, 3, 4)
fmt.Println(set.Pop())
fmt.Println(set.Pops(2))
fmt.Println(set.Size())
// May Output:
// 1
// [2 3]
// 1
}
func ExampleSet_Pops() {
var set gset.Set
set.Add(1, 2, 3, 4)
fmt.Println(set.Pop())
fmt.Println(set.Pops(2))
fmt.Println(set.Size())
// May Output:
// 1
// [2 3]
// 1
}
func ExampleSet_Join() {
var set gset.Set
set.Add("a", "b", "c", "d")
fmt.Println(set.Join(","))
// May Output:
// a,b,c,d
}
func ExampleSet_Contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}
func ExampleSet_ContainsI() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}

View File

@ -0,0 +1,23 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
)
func ExampleIntSet_Contains() {
var set gset.IntSet
set.Add(1)
fmt.Println(set.Contains(1))
fmt.Println(set.Contains(2))
// Output:
// true
// false
}

View File

@ -0,0 +1,43 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/frame/g"
)
func ExampleStrSet_Contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}
func ExampleStrSet_Walk() {
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
fmt.Println(set.Slice())
// May Output:
// [gf_user gf_user_detail]
}

View File

@ -9,10 +9,12 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
@ -21,10 +23,30 @@ import (
"testing"
)
func TestSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var s gset.Set
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
t.AssertIN(2, s.Slice())
t.AssertIN(3, s.Slice())
t.AssertIN(4, s.Slice())
t.AssertNI(0, s.Slice())
t.Assert(s.Contains(4), true)
t.Assert(s.Contains(5), false)
s.Remove(1)
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestSet_New(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -44,7 +66,7 @@ func TestSet_New(t *testing.T) {
func TestSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -64,7 +86,7 @@ func TestSet_Basic(t *testing.T) {
func TestSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -85,7 +107,7 @@ func TestSet_Iterator(t *testing.T) {
func TestSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[interface{}]struct{}) {
delete(m, 1)
@ -105,9 +127,9 @@ func TestSet_Equal(t *testing.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -118,9 +140,9 @@ func TestSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -133,8 +155,8 @@ func TestSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s1.Add(1, 2)
s2.Add(3, 4)
s3 := s1.Union(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -147,8 +169,8 @@ func TestSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Diff(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -161,8 +183,8 @@ func TestSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Intersect(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -175,8 +197,8 @@ func TestSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Complement(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -203,9 +225,9 @@ func TestNewFrom(t *testing.T) {
func TestNew(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New()
s1.Add("a").Add(2)
s1.Add("a", 2)
s2 := gset.New(true)
s2.Add("b").Add(3)
s2.Add("b", 3)
t.Assert(s1.Contains("a"), true)
})
@ -214,13 +236,13 @@ func TestNew(t *testing.T) {
func TestSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a1").Add("b").Add("c")
s1.Add("a", "a1", "b", "c")
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, "a1"), true)
})
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add(`"b"`).Add(`\c`)
s1.Add("a", `"b"`, `\c`)
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, `"b"`), true)
t.Assert(strings.Contains(str1, `\c`), true)
@ -231,7 +253,7 @@ func TestSet_Join(t *testing.T) {
func TestSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s1.Add("a", "a2", "b", "c")
str1 := s1.String()
t.Assert(strings.Contains(str1, "["), true)
t.Assert(strings.Contains(str1, "]"), true)
@ -243,8 +265,8 @@ func TestSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s2 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s2.Add("b").Add("b1").Add("e").Add("f")
s1.Add("a", "a2", "b", "c")
s2.Add("b", "b1", "e", "f")
ss := s1.Merge(s2)
t.Assert(ss.Contains("a2"), true)
t.Assert(ss.Contains("b1"), true)
@ -255,7 +277,7 @@ func TestSet_Merge(t *testing.T) {
func TestSet_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3, 4)
t.Assert(s1.Sum(), int(10))
})
@ -264,7 +286,7 @@ func TestSet_Sum(t *testing.T) {
func TestSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1).Add(2).Add(3).Add(4)
s.Add(1, 2, 3, 4)
t.Assert(s.Size(), 4)
t.AssertIN(s.Pop(), []int{1, 2, 3, 4})
t.Assert(s.Size(), 3)
@ -274,7 +296,7 @@ func TestSet_Pop(t *testing.T) {
func TestSet_Pops(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1).Add(2).Add(3).Add(4)
s.Add(1, 2, 3, 4)
t.Assert(s.Size(), 4)
t.Assert(s.Pops(0), nil)
t.AssertIN(s.Pops(1), []int{1, 2, 3, 4})
@ -324,43 +346,84 @@ func TestSet_Json(t *testing.T) {
})
}
func TestSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.AddIfNotExist(1), false)
t.Assert(s.AddIfNotExist(2), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.Contains(2), true)
})
}
func TestSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFunc(2, func() interface{} {
return 3
})
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFunc(3, func() interface{} {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
t.Assert(s.Contains(2), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc(1, func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
wg.Wait()
})
}
s.AddIfNotExistFuncLock(2, func() interface{} {
return 3
func TestSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var set gset.Set
set.Add(g.Slice{1, 2}...)
set.Walk(func(item interface{}) interface{} {
return gconv.Int(item) + 10
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
t.Assert(set.Size(), 2)
t.Assert(set.Contains(11), true)
t.Assert(set.Contains(12), true)
})
}
s.AddIfNotExistFuncLock(3, func() interface{} {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
func TestSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}

View File

@ -9,21 +9,43 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"testing"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/test/gtest"
)
func TestIntSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var s gset.IntSet
s.Add(1, 1, 2)
s.Add([]int{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
t.AssertIN(2, s.Slice())
t.AssertIN(3, s.Slice())
t.AssertIN(4, s.Slice())
t.AssertNI(0, s.Slice())
t.Assert(s.Contains(4), true)
t.Assert(s.Contains(5), false)
s.Remove(1)
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestIntSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]int{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -43,7 +65,7 @@ func TestIntSet_Basic(t *testing.T) {
func TestIntSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -64,7 +86,7 @@ func TestIntSet_Iterator(t *testing.T) {
func TestIntSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[int]struct{}) {
delete(m, 1)
@ -84,9 +106,9 @@ func TestIntSet_Equal(t *testing.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -97,9 +119,9 @@ func TestIntSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -112,8 +134,8 @@ func TestIntSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s1.Add(1, 2)
s2.Add(3, 4)
s3 := s1.Union(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -126,8 +148,8 @@ func TestIntSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Diff(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -140,8 +162,8 @@ func TestIntSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Intersect(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -154,8 +176,8 @@ func TestIntSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Complement(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -167,7 +189,7 @@ func TestIntSet_Complement(t *testing.T) {
func TestIntSet_Size(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet(true)
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
t.Assert(s1.Size(), 3)
})
@ -178,8 +200,8 @@ func TestIntSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Merge(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(5), true)
@ -190,7 +212,7 @@ func TestIntSet_Merge(t *testing.T) {
func TestIntSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s3 := s1.Join(",")
t.Assert(strings.Contains(s3, "1"), true)
t.Assert(strings.Contains(s3, "2"), true)
@ -201,7 +223,7 @@ func TestIntSet_Join(t *testing.T) {
func TestIntSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s3 := s1.String()
t.Assert(strings.Contains(s3, "["), true)
t.Assert(strings.Contains(s3, "]"), true)
@ -214,9 +236,9 @@ func TestIntSet_String(t *testing.T) {
func TestIntSet_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s2 := gset.NewIntSet()
s2.Add(5).Add(6).Add(7)
s2.Add(5, 6, 7)
t.Assert(s2.Sum(), 18)
})
@ -226,7 +248,7 @@ func TestIntSet_Sum(t *testing.T) {
func TestIntSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(4).Add(2).Add(3)
s.Add(4, 2, 3)
t.Assert(s.Size(), 3)
t.AssertIN(s.Pop(), []int{4, 2, 3})
t.AssertIN(s.Pop(), []int{4, 2, 3})
@ -237,7 +259,7 @@ func TestIntSet_Pop(t *testing.T) {
func TestIntSet_Pops(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(4).Add(2).Add(3)
s.Add(1, 4, 2, 3)
t.Assert(s.Size(), 4)
t.Assert(s.Pops(0), nil)
t.AssertIN(s.Pops(1), []int{1, 4, 2, 3})
@ -258,6 +280,74 @@ func TestIntSet_Pops(t *testing.T) {
})
}
func TestIntSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.AddIfNotExist(1), false)
t.Assert(s.AddIfNotExist(2), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.Contains(2), true)
})
}
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
t.Assert(s.Contains(2), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
t.Assert(s.Contains(2), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc(1, func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add(1)
wg.Wait()
})
}
func TestIntSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}
func TestIntSet_Json(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 3, 2, 4}
@ -287,43 +377,16 @@ func TestIntSet_Json(t *testing.T) {
})
}
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
func TestIntSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFunc(2, func() int {
return 3
var set gset.IntSet
set.Add(g.SliceInt{1, 2}...)
set.Walk(func(item int) int {
return item + 10
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFunc(3, func() int {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFuncLock(2, func() int {
return 3
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFuncLock(3, func() int {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
t.Assert(set.Size(), 2)
t.Assert(set.Contains(11), true)
t.Assert(set.Contains(12), true)
})
}

View File

@ -9,21 +9,23 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"testing"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/test/gtest"
)
func TestStrSet_Basic(t *testing.T) {
func TestStrSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("1").Add("2")
var s gset.StrSet
s.Add("1", "1", "2")
s.Add([]string{"3", "4"}...)
t.Assert(s.Size(), 4)
t.AssertIN("1", s.Slice())
@ -40,10 +42,40 @@ func TestStrSet_Basic(t *testing.T) {
})
}
func TestStrSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1", "1", "2")
s.Add([]string{"3", "4"}...)
t.Assert(s.Size(), 4)
t.AssertIN("1", s.Slice())
t.AssertIN("2", s.Slice())
t.AssertIN("3", s.Slice())
t.AssertIN("4", s.Slice())
t.AssertNI("0", s.Slice())
t.Assert(s.Contains("4"), true)
t.Assert(s.Contains("5"), false)
s.Remove("1")
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestStrSet_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestStrSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("2").Add("3")
s.Add("1", "2", "3")
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -64,7 +96,7 @@ func TestStrSet_Iterator(t *testing.T) {
func TestStrSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("2").Add("3")
s.Add("1", "2", "3")
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[string]struct{}) {
delete(m, "1")
@ -84,9 +116,9 @@ func TestStrSet_Equal(t *testing.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s3 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
s1.Add("1", "2", "3")
s2.Add("1", "2", "3")
s3.Add("1", "2", "3", "4")
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -97,9 +129,9 @@ func TestStrSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s3 := gset.NewStrSet()
s1.Add("1").Add("2")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
s1.Add("1", "2")
s2.Add("1", "2", "3")
s3.Add("1", "2", "3", "4")
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -112,8 +144,8 @@ func TestStrSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2")
s2.Add("3").Add("4")
s1.Add("1", "2")
s2.Add("3", "4")
s3 := s1.Union(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("2"), true)
@ -126,8 +158,8 @@ func TestStrSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Diff(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("2"), true)
@ -140,8 +172,8 @@ func TestStrSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Intersect(s2)
t.Assert(s3.Contains("1"), false)
t.Assert(s3.Contains("2"), false)
@ -154,8 +186,8 @@ func TestStrSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Complement(s2)
t.Assert(s3.Contains("1"), false)
t.Assert(s3.Contains("2"), false)
@ -179,8 +211,8 @@ func TestStrSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Merge(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("6"), false)
@ -207,7 +239,7 @@ func TestStrSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s1.Add("a").Add(`"b"`).Add(`\c`)
s1.Add("a", `"b"`, `\c`)
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, `"b"`), true)
t.Assert(strings.Contains(str1, `\c`), true)
@ -225,7 +257,7 @@ func TestStrSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s1.Add("a", "a2", "b", "c")
str1 := s1.String()
t.Assert(strings.Contains(str1, "["), true)
t.Assert(strings.Contains(str1, "]"), true)
@ -253,7 +285,7 @@ func TestStrSet_Size(t *testing.T) {
func TestStrSet_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSetFrom([]string{"a", "b", "c"}, true)
s1 = s1.Remove("b")
s1.Remove("b")
t.Assert(s1.Contains("b"), false)
t.Assert(s1.Contains("c"), true)
})
@ -294,6 +326,74 @@ func TestStrSet_Pops(t *testing.T) {
})
}
func TestStrSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.AddIfNotExist("1"), false)
t.Assert(s.AddIfNotExist("2"), true)
t.Assert(s.Contains("2"), true)
t.Assert(s.AddIfNotExist("2"), false)
t.Assert(s.Contains("2"), true)
})
}
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return false }), false)
t.Assert(s.Contains("2"), false)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), true)
t.Assert(s.Contains("2"), true)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), false)
t.Assert(s.Contains("2"), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc("1", func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add("1")
wg.Wait()
})
}
func TestStrSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock("1", func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock("1", func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}
func TestStrSet_Json(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
@ -323,43 +423,21 @@ func TestStrSet_Json(t *testing.T) {
})
}
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
func TestStrSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
s.AddIfNotExistFunc("2", func() string {
return "3"
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
t.Assert(s.Contains("2"), false)
t.Assert(s.Contains("3"), true)
s.AddIfNotExistFunc("3", func() string {
return "4"
})
t.Assert(s.Contains("3"), true)
t.Assert(s.Contains("4"), false)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
s.AddIfNotExistFuncLock("2", func() string {
return "3"
})
t.Assert(s.Contains("2"), false)
t.Assert(s.Contains("3"), true)
s.AddIfNotExistFuncLock("3", func() string {
return "4"
})
t.Assert(s.Contains("3"), true)
t.Assert(s.Contains("4"), false)
t.Assert(set.Size(), 2)
t.Assert(set.Contains("gf_user"), true)
t.Assert(set.Contains("gf_user_detail"), true)
})
}

View File

@ -5,4 +5,7 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gtree provides concurrent-safe/unsafe tree containers.
//
// Some implements are from: https://github.com/emirpasic/gods
// Thanks!
package gtree

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