Compare commits

...

385 Commits

Author SHA1 Message Date
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
992522342c version/readme update 2020-03-30 23:56:26 +08:00
040898cdc3 improve unit testing case for package gmutex 2020-03-30 22:55:03 +08:00
343126ef22 fix usage for garray.PopRand 2020-03-30 20:56:00 +08:00
05760d1eac fix usage for garray.PopRand 2020-03-30 20:47:50 +08:00
c10f73f1f7 add zero time.Time filtering for omitempty feature of gdb.Model 2020-03-30 20:44:36 +08:00
7e0fa8e0cd improve package garray 2020-03-30 20:31:47 +08:00
6fe6218505 README updates 2020-03-29 20:23:10 +08:00
6059782de8 add unit testing case of basic auth for ghttp.Client; remove intlog for New function of package gspath 2020-03-29 19:36:49 +08:00
4844eea0ab add convenience function g.Client for ghttp.Client 2020-03-29 09:52:37 +08:00
8ecd62d3de add example for package gres 2020-03-28 21:32:29 +08:00
4c610b4f58 improve session storage file for ghttp.Server 2020-03-28 20:02:57 +08:00
1932c4ec44 Merge pull request #575 from wenzi1/master
Fix the bug of MSSQL paging
2020-03-28 19:59:31 +08:00
bd2e51ddca improve unit testing case for package glog 2020-03-28 00:47:34 +08:00
ddcb7121c1 improve temporary path producing from gfile.Join to gfile.TempDir 2020-03-28 00:41:12 +08:00
f1f575fd5c improve package gins/gfile/intlog 2020-03-28 00:37:23 +08:00
99adb7cdc4 comment update 2020-03-26 23:48:21 +08:00
6b7ea97777 improve quote handling of table string for package gdb; improve rotation feature for package glog 2020-03-26 22:25:43 +08:00
495b5758ec improve quote handling of table string for package gdb; improve rotation feature for package glog 2020-03-26 22:22:27 +08:00
ba56eb87b1 improve rotation feature for package glog 2020-03-26 20:58:57 +08:00
4258a3bbc9 improve StrToSize function for package gfile 2020-03-26 15:54:57 +08:00
4912331ddc improve mtime feature for gfile; improve rotation feature for glog 2020-03-26 09:29:36 +08:00
e87b7ecf5d improve unit testing case for package gfpool 2020-03-26 08:52:50 +08:00
23bce7bde6 reduce the goroutine count for unit testing cases of package gfpool/glog 2020-03-25 23:49:33 +08:00
926b664615 improve package garray for crossing border handling; improve rotation feature for package glog 2020-03-25 23:36:56 +08:00
fa8257c85b improve ghttp.Client 2020-03-25 17:13:05 +08:00
abb9c88c23 improve ghttp.Client 2020-03-25 15:17:18 +08:00
f89976cad5 improve ghttp.Client 2020-03-25 15:09:13 +08:00
0389778725 improve package glog for file lock 2020-03-25 00:03:52 +08:00
3c36285126 improve Model.Data function for package gdb 2020-03-24 20:58:11 +08:00
75054ee109 fix issue in rebinding feature for grou router of ghttp.Server 2020-03-24 19:48:10 +08:00
8447b1a42b add function LevelStr/StackWithFilter functions for package glog 2020-03-24 00:05:11 +08:00
e5265a1c46 add level string feature for package glog 2020-03-23 22:40:44 +08:00
6e8ef8d0b0 add level string feature for package glog 2020-03-23 22:36:06 +08:00
75c081afc9 fix issue of char '/' in URL.Path handling of ghttp.Server 2020-03-23 20:57:34 +08:00
060fd9eaba improve comment for package gdb 2020-03-23 20:44:20 +08:00
63e5a60344 improve package gdb 2020-03-22 23:26:15 +08:00
75dc1d82c1 example names change 2020-03-22 12:49:46 +08:00
a5e048eb5f example name changes 2020-03-22 12:34:12 +08:00
65bc1d5eb8 comment update for package gjson 2020-03-22 09:24:45 +08:00
bfa64705b5 update comment for package gsession 2020-03-22 00:15:59 +08:00
17aea8d7d4 improve unit testing cases by changing creating testdata path using gdebug.TestdataPath function 2020-03-21 23:47:33 +08:00
a6a01fd7f2 improve template cache for ParseContent function of package gview 2020-03-21 23:42:34 +08:00
41a9e91b4c improve example for package gpage 2020-03-21 23:13:31 +08:00
e2c1e11f95 improve package gjson 2020-03-21 21:32:02 +08:00
c2966817ce improve function gdebug.TestDataPath and update all usage codes 2020-03-21 19:41:05 +08:00
16958413bb improve autoencode feature for package gview 2020-03-21 19:31:58 +08:00
c0a0913d4b add more unit testing case for template feature of ghttp.Server 2020-03-21 13:45:33 +08:00
6d47810782 improve context feature for ghttp.Request; improve comment for package gjson 2020-03-21 13:32:43 +08:00
7a9ea2e546 improve context feature for ghttp.Request 2020-03-20 23:22:32 +08:00
7881b2dee4 improve unit testing cases 2020-03-20 22:34:56 +08:00
5f223ef049 Merge pull request #4 from gogf/master
update
2020-03-20 10:06:50 +08:00
e57942b374 improve unit testing cases 2020-03-20 08:56:17 +08:00
f18e6f078c improve unit testing cases 2020-03-20 08:49:40 +08:00
f667cbc2a2 Fix the bug of MSSQL paging 2020-03-20 00:33:20 +08:00
07e65c14a9 improve unit testing cases 2020-03-19 23:53:03 +08:00
0b6d04485e improve unit testing cases 2020-03-19 22:56:12 +08:00
36401a063d improve gutil.Dump, improve sqlite file searching when opening db file 2020-03-19 13:38:42 +08:00
849e7370d1 improve gutil.Dump, improve sqlite file searching when opening db file 2020-03-19 11:37:31 +08:00
f01dca0895 version updates 2020-03-17 22:36:05 +08:00
55137b2aa3 README, DONATOR updates 2020-03-17 22:35:09 +08:00
22540921b3 improve codes typing for package package gvalid 2020-03-17 22:23:29 +08:00
6174097a07 Merge pull request #3 from gogf/master
update
2020-03-17 22:11:51 +08:00
52de11b1fe Merge pull request #564 from arieslee/master 2020-03-17 22:08:31 +08:00
5a646179ad fix issue in limit...offset statement of postgresql for packagegdb 2020-03-17 21:38:49 +08:00
33ae93e050 improve CORS feature for ghttp.Server 2020-03-17 17:46:43 +08:00
f3d859159d improve router group for duplicated router registering for package ghttp.Server 2020-03-17 14:48:52 +08:00
4fb01e68f7 移除字符串ip地址与Long类型互转的方法,修改中文长度校验的方法 2020-03-17 14:26:36 +08:00
2812a247aa 添加验证中英文混合字符串长度的判断规则,按utf8计算,一个中文算一个字符,cn-length,cn-min-length,cn-max-length 2020-03-17 11:19:43 +08:00
2b46e765c4 change function time.Duration.Milliseconds to time.Duration.Nanoseconds for compatibility of Golang version < 1.13 2020-03-16 23:03:37 +08:00
c7f911cae2 change function time.Duration.Milliseconds to time.Duration.Nanoseconds for compatibility of Golang version < 1.13 2020-03-16 23:03:35 +08:00
9f0548c03d improve testdata directory retrieving for unit testing cases 2020-03-16 22:47:39 +08:00
c578df06a3 fix issue in unit testing case of package gfile 2020-03-16 20:51:19 +08:00
8ed3cf9c97 fix issue in unit testing case of package gcdg 2020-03-16 20:46:03 +08:00
2438f565e9 添加字符串ip地址与Long类型互转的方法 2020-03-16 11:18:06 +08:00
8230c72ec6 improve file uploading feature for ghttp.Request 2020-03-15 20:11:38 +08:00
d716037caa improve package gcfg adding default configuration file searching for instance 2020-03-15 19:56:07 +08:00
855a4ddb2c add file rotation feature for package glog; improve gpool/gfpool; fix issue in gfile.MTimeMillisecond 2020-03-15 19:32:29 +08:00
74be9fac18 add file rotation feature for package glog; improve gpool/gfpool; fix issue in gfile.MTimeMillisecond 2020-03-15 19:32:26 +08:00
e9fba5a166 readme updates 2020-03-14 17:15:25 +08:00
0a92df691b improve package gpool/gfpool; donator updates 2020-03-14 17:12:44 +08:00
e513cd10ed add Lock* functions for gdb.Model and improve Data/Where functions for gdb.Model 2020-03-13 17:21:30 +08:00
b702d98700 version updates 2020-03-12 10:05:38 +08:00
99f1d9d0ed improve package gudp 2020-03-11 23:59:43 +08:00
707b08c585 improve package gudp 2020-03-11 23:56:58 +08:00
f44c19868c improve package gudp 2020-03-11 23:54:35 +08:00
13ab139afc add more unit testing case for package gdb 2020-03-11 23:24:19 +08:00
cacb2f142b Merge pull request #548 from wenzi1/master
add Tables and TableFields method for sqlite
2020-03-11 23:18:59 +08:00
11f0317e92 add Array feature for package gdb 2020-03-11 23:17:41 +08:00
9a667c8803 add Tables and TableFields method for sqlite 2020-03-11 22:45:38 +08:00
c1cce17934 add unit testing cases for gudp 2020-03-11 16:08:17 +08:00
5a92d7de0d fix issue in router group registering for controller 2020-03-11 15:51:24 +08:00
53bf378868 Merge pull request #550 from fulltimelove/master 2020-03-11 15:07:30 +08:00
cd7c45c00c improve default connection pool configuration for gredis 2020-03-11 14:35:23 +08:00
f13a5ad82e 分组路由对象绑定时支持多方法绑定 2020-03-11 10:10:00 +08:00
2e9be609c8 fix issue in unit testing of gins 2020-03-11 09:22:13 +08:00
2f2f6e1ffe add Tables and TableFields method 2020-03-11 00:29:25 +08:00
f3bd2b67f7 change attribute Context to context.Context for ghttp.Request 2020-03-10 20:45:22 +08:00
645cecdffb improve template view instance initialization for instance of ghttp.Server 2020-03-10 20:13:36 +08:00
22e9965629 improve template view instance initialization for instance of ghttp.Server 2020-03-10 08:57:37 +08:00
24ea9f9245 Merge pull request #2 from gogf/master
update
2020-03-09 23:35:59 +08:00
9dbde6e8f1 change function HandleSqlBeforeExec to HandleSqlBeforeCommit for package gdb 2020-03-09 22:00:01 +08:00
fe0b34544d fix missing of pgsql driver support for package gdb 2020-03-09 21:56:53 +08:00
5e489d59b4 improve HandleSqlBeforeExec function for package gdb 2020-03-09 21:53:58 +08:00
61f49574a9 fix issue in unit testing case for gparser 2020-03-09 20:56:10 +08:00
2fabcb62a8 remove binary from git 2020-03-09 19:48:51 +08:00
3bc3b652c1 improve unit testing case for gjson 2020-03-09 09:00:38 +08:00
042a6f12f5 add map conversion support for result of HGetAll for package gredis 2020-03-08 23:16:06 +08:00
5acce82e63 add slice support for gconv.Map 2020-03-08 23:09:37 +08:00
4732bf46ad add Context map for internal context workflow parameter exchanges for ghttp.Request 2020-03-08 22:53:20 +08:00
5bed5a1532 improve unit testing case for driver feature og package gdb 2020-03-08 12:18:07 +08:00
5b7576430f improve unit testing case for driver feature og package gdb 2020-03-08 12:16:44 +08:00
356f4cd701 improve unit testing case for driver feature og package gdb 2020-03-08 12:04:24 +08:00
c444630d1e add workaround for package gdb for json infinite loop bug of Golang version < v1.14 2020-03-08 11:56:19 +08:00
8e40cded42 add custom driver feature for package gdb 2020-03-08 11:03:18 +08:00
0e52d467d3 improving package gdb 2020-03-08 00:17:42 +08:00
6665d62e7e improve package gfile 2020-03-07 20:28:00 +08:00
5bdf1a71b8 improve uploading file feature for ghttp.Server 2020-03-07 20:20:52 +08:00
a34ca0ff4b improve uploading file feature for ghttp.Server; improve package gfile/gstr/gdebug 2020-03-07 19:31:33 +08:00
7f0163d958 improve gconv.Struct* functions for custom types conversion 2020-03-06 23:22:08 +08:00
31f19b0eee improve package gcompress 2020-03-06 15:38:32 +08:00
93d0760898 add GetPage function for ghttp.Request 2020-03-06 11:01:03 +08:00
4863e7a6ae Merge pull request #529 from wenzi1/master 2020-03-05 22:02:16 +08:00
a161b44cc7 improve package gpage 2020-03-05 18:07:07 +08:00
7072244420 improve comment for ghttp.Server 2020-03-05 16:08:55 +08:00
f68b66e606 update comment for ghttp.Server 2020-03-04 23:32:27 +08:00
4e7c6c1fb4 improve CORS feature for ghttp.Server 2020-03-04 22:52:56 +08:00
d8a7e36478 improve router feature for ghttp.Server 2020-03-04 17:29:23 +08:00
8971ad8445 Add tables method of MSSQL, Oracle and PgSQL 2020-03-02 23:41:33 +08:00
270af8accb Merge pull request #1 from gogf/master
update
2020-02-29 22:46:00 +08:00
b0ef63fc9d improve bindArgsToQuery for surpport for pgsql/mssql/oracle for package gdb 2020-02-29 19:55:53 +08:00
6e1f8c3cfc version updates 2020-02-29 08:54:18 +08:00
e58d7e8dda add internal log for gi18n; improve unit testing case for ghttp.Server 2020-02-28 23:00:05 +08:00
3a3384cf06 Merge branch 'master' of https://github.com/gogf/gf 2020-02-27 17:09:44 +08:00
ef2a9f6fd1 fix issue in log paatern for ghttp.Server 2020-02-27 17:09:12 +08:00
63f756f731 Merge pull request #514 from kevinlincg/master
fix typo in gpool_bench_test.go
2020-02-26 23:38:33 +08:00
bb1c27c36a add more unit testing cases for glog 2020-02-26 23:26:24 +08:00
87cd0703c0 change internal log prefix from '[GF]' to '[INTE]' 2020-02-26 11:57:26 +08:00
6317d9de53 add ci for golang v1.14 2020-02-26 11:54:46 +08:00
7acf16fdba add UploadFile feature for ghttp.Server 2020-02-26 01:01:28 +08:00
a52b454d3e add UploadFile feature for ghttp.Server 2020-02-26 00:48:27 +08:00
816e075c52 add more unit testing cases for package garray 2020-02-25 23:19:37 +08:00
9882b361a8 add more unit testing cases for package gstr 2020-02-25 23:01:51 +08:00
4415dcf1c1 add HasPrefix/HasSuffix for package gstr 2020-02-25 21:03:07 +08:00
42fd583bfd add more inernal logging points for core components 2020-02-24 21:09:19 +08:00
c70bc7c96a improve pakage g/gins 2020-02-23 20:25:55 +08:00
02bd780a33 readme and donator update 2020-02-22 17:06:58 +08:00
24a2192ce2 README updates 2020-02-22 15:36:31 +08:00
d8ef8a1f5d add gf.debug options and GF_DEBUG env control params for internal logging feature 2020-02-22 14:51:44 +08:00
745a913cfb add FilterNil/FilterEmpty functions for package garray; add FieldsStr/FieldsExStr for gdb.Model 2020-02-22 14:26:36 +08:00
13dba407a2 fix parameter issue in benchmark of internal/rwmutex 2020-02-21 13:15:40 +08:00
34e7c5f809 rename grand.Str to grand.S; add different function Str for grand 2020-02-21 00:01:27 +08:00
d570624caa fix typo in gpool_bench_test.go 2020-02-20 09:01:09 +08:00
f18312419b improve CORS feature for ghttp.Server 2020-02-16 23:18:37 +08:00
89f869dd44 remove internal logging for gsession.StorageFile; improve example codes for CORS feature of ghttp.Server 2020-02-16 23:00:42 +08:00
20b64507b1 improve string conversion for gtime.Time 2020-02-16 22:39:12 +08:00
7443246e05 add gtime.TimeWrapper 2020-02-16 18:07:05 +08:00
f9e7823c14 improve configuration for package gdb 2020-02-16 16:36:39 +08:00
52943b283c go.mod updates; add search path configuration in file for package ghttp.Server 2020-02-16 16:34:30 +08:00
36403fdc08 improve configuration for mssql 2020-02-16 16:25:03 +08:00
0317f6812e add more unit testing cases for gtime 2020-02-16 15:11:21 +08:00
5169137069 improve unit testing case for CORS feature for package ghttp 2020-02-16 13:32:06 +08:00
7d9bccf912 README updates 2020-02-15 12:00:20 +08:00
14f56ea18f donator updates 2020-02-14 22:21:53 +08:00
1736d271d2 add more unit testing case 2020-02-14 22:13:41 +08:00
19755ad233 improve gcfg 2020-02-14 21:57:35 +08:00
cfdd043e4e add OctStr function for gstr 2020-02-12 10:48:15 +08:00
78917ed5cb comment updates for package ghttp 2020-02-11 10:00:10 +08:00
88684ca00a add panic if internal watcher creation fails for gfsnotify; improve codes and change comment from chinese to english for gdb 2020-02-10 20:37:53 +08:00
784983806a add InserIgnore feature for package gdb 2020-02-08 23:46:10 +08:00
cdb3b94e22 add NewWithTag function for gjson/gparser 2020-02-08 14:07:32 +08:00
83dcc4a5e0 fix issue for overwriting the route item if allowed 2020-02-08 11:17:09 +08:00
a6c0b281a3 add struct support for where condition statement of gdb 2020-02-07 20:58:47 +08:00
3120f24553 add struct support for where condition statement og gdb 2020-02-07 19:44:11 +08:00
ac9be6134b add global schema access support for mssql in gdb 2020-02-07 17:21:05 +08:00
4c1b4f7858 change time measuing unit from microtime to millitime for ghttp.Request; change graceful reload feature from true to false in default 2020-02-07 16:29:14 +08:00
1e45bf93d8 remove error printing when decoding error in communication feature of gproc 2020-02-06 19:21:17 +08:00
e8dd3979b6 add more unit testing cases for ghttp/gview 2020-02-06 15:17:10 +08:00
374ee4c0ea improve ghttp.Request for making the request body reusable for multiple times 2020-02-06 11:22:36 +08:00
95411aff77 improve ghttp.Request for making the request body reusable for multiple times 2020-02-06 11:14:38 +08:00
1999ef95c1 fix issue in gconv.Struct* functions panic when converting attribute value is nil 2020-02-05 22:06:24 +08:00
b15075fdfe fix issue in gconv.Struct* functions panic when converting attribute value is nil 2020-02-05 22:02:49 +08:00
4d2b244319 fix issue in gres.UnpackContent; fix issue in gtime.NewFromTimeStamp 2020-02-04 17:09:18 +08:00
4c3af63076 improve time string parsing for invalid datetime 2020-02-01 20:14:24 +08:00
91bbff6ced architecture updates 2020-01-23 15:04:12 +08:00
26aab44ec8 fix issue in char '-' support for parameter retrieving for ghttp.Request 2020-01-22 20:28:42 +08:00
2e10ce421b improve plugin feature 2020-01-21 22:18:49 +08:00
8eda69b11e improve the plugin feature for ghttp.Server 2020-01-21 17:18:03 +08:00
7d7b242968 Merge branch 'master' of https://github.com/gogf/gf 2020-01-21 15:42:19 +08:00
202419202f improve logger feautre, add unit testing cases for ghttp.Server; add SetDefaultLogger function for glog 2020-01-21 15:42:08 +08:00
55078beed1 Merge pull request #484 from chikaku/remove_redundant
Remove redundant code in gsession
2020-01-21 15:40:35 +08:00
d9f4e6eaa6 remove redundant code in gsession 2020-01-21 14:55:08 +08:00
2ba0913bea comment update for gtimer 2020-01-21 14:46:23 +08:00
8f2dcf21ff improve gconv.Map 2020-01-20 21:25:55 +08:00
01b06e0745 improve ghttp.BuildParams 2020-01-20 20:32:39 +08:00
665b5960c8 fix issue in minus number converting for gconv.Int 2020-01-20 20:18:24 +08:00
eb6a7a4728 add function UnmarshalValue feature for package garray/gmap/gset/gtype/gvar/gjson/gconv 2020-01-20 19:56:42 +08:00
7df53ff55e rename all timestamp function names from *Second to Timestamp* 2020-01-20 14:14:11 +08:00
8021f39710 version updates 2020-01-19 21:21:41 +08:00
f2af08270b improve gproc 2020-01-19 20:42:21 +08:00
c2f028848c readme updates 2020-01-18 22:35:07 +08:00
d9c7224861 readme updates 2020-01-18 22:21:37 +08:00
f59a1ada88 improve static service feature, add plugin feature for ghttp.Server 2020-01-17 21:12:52 +08:00
705ab1d33f improve garray; add RemoveValue function for garray 2020-01-17 19:48:50 +08:00
659 changed files with 41987 additions and 23581 deletions

View File

@ -9,7 +9,7 @@ import (
func main() {
// 创建一个对象池过期时间为1000毫秒
p := gpool.New(1000, nil)
p := gpool.New(1000*time.Millisecond, nil)
// 从池中取一个对象返回nil及错误信息
fmt.Println(p.Get())

View File

@ -11,7 +11,7 @@ import (
func main() {
// 创建对象复用池对象过期时间为3000毫秒并给定创建及销毁方法
p := gpool.New(3000, func() (interface{}, error) {
p := gpool.New(3000*time.Millisecond, func() (interface{}, error) {
return gtcp.NewConn("www.baidu.com:80")
}, func(i interface{}) {
glog.Println("expired")

View File

@ -0,0 +1,79 @@
// 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 driver
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
)
// MyDriver is a custom database driver, which is used for testing only.
// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver
// gdb.DriverMysql and overwrites its functions DoQuery and DoExec.
// So if there's any sql execution, it goes through MyDriver.DoQuery/MyDriver.DoExec firstly
// and then gdb.DriverMysql.DoQuery/gdb.DriverMysql.DoExec.
// You can call it sql "HOOK" or "HiJack" as your will.
type MyDriver struct {
*gdb.DriverMysql
}
var (
// customDriverName is my driver name, which is used for registering.
customDriverName = "MyDriver"
)
func init() {
// It here registers my custom driver in package initialization function "init".
// You can later use this type in the database configuration.
if err := gdb.Register(customDriverName, &MyDriver{}); err != nil {
panic(err)
}
}
// New creates and returns a database object for mysql.
// It implements the interface of gdb.Driver for extra database driver installation.
func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
return &MyDriver{
&gdb.DriverMysql{
Core: core,
},
}, nil
}
// DoQuery commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
tsMilli := gtime.TimestampMilli()
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
"sql": gdb.FormatSqlWithArgs(sql, args),
"cost": gtime.TimestampMilli() - tsMilli,
"time": gtime.Now(),
"error": err.Error(),
}); err != nil {
panic(err)
}
return
}
// DoExec commits the query string and its arguments to underlying driver
// through given link object and returns the execution result.
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
tsMilli := gtime.TimestampMilli()
result, err = d.DriverMysql.DoExec(link, sql, args...)
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
"sql": gdb.FormatSqlWithArgs(sql, args),
"cost": gtime.TimestampMilli() - tsMilli,
"time": gtime.Now(),
"error": err.Error(),
}); err != nil {
panic(err)
}
return
}

View File

@ -0,0 +1 @@
package main

View File

@ -1,8 +1,3 @@
[database]
type = "mssql"
host = "127.0.0.1"
port = "1451"
user = "sa"
pass = "eno@123"
name = "frpc"
linkinfo = "mssql:user id=test;password=test1;server=122.152.202.91;port=1433;database=test;encrypt=disable"

View File

@ -2,13 +2,22 @@ package main
import (
"fmt"
"github.com/gogf/gf/os/gtime"
_ "github.com/denisenkom/go-mssqldb"
"github.com/gogf/gf/frame/g"
)
func main() {
r, err := g.DB().GetAll(`SELECT TOP 10 * FROM KF_PatInfo_Emergency`)
fmt.Println(err)
g.Dump(r.ToList())
type Table2 struct {
Id string `orm:"id;pr" json:"id"` //ID
Createtime gtime.Time `orm:"createtime" json:"createtime"` //创建时间
Updatetime gtime.Time `orm:"updatetime" json:"updatetime"` //更新时间
}
var table2 Table2
err := g.DB().Table("table2").Where("id=?", 1).Struct(&table2)
if err != nil {
panic(err)
}
fmt.Println(table2.Createtime)
}

View File

@ -1,8 +1,9 @@
# 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
#[database]
# [[database.default]]

View File

@ -11,11 +11,11 @@ func main() {
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
r, e := db.Table("test").OrderBy("id asc").All()
r, e := db.Table("test").Order("id asc").All()
if e != nil {
panic(e)
fmt.Println(e)
}
if r != nil {
fmt.Println(r.ToList())
fmt.Println(r.List())
}
}

View File

@ -0,0 +1,18 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"time"
)
func main() {
db := g.DB()
db.SetDebug(true)
for {
r, err := db.Table("user").All()
fmt.Println(err)
fmt.Println(r)
time.Sleep(time.Second * 10)
}
}

View File

@ -8,10 +8,5 @@ func main() {
db := g.DB()
db.SetDebug(true)
one, e := db.Table("order.order o").LeftJoin("user.user u", "o.uid=u.id").Where("u.id", 1).One()
if e != nil {
panic(e)
}
g.Dump(one)
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
}

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

@ -100,53 +100,53 @@ func testConvert() {
func testSplitChar() {
var v interface{}
j := gjson.New(nil)
t1 := gtime.Nanosecond()
t1 := gtime.TimestampNano()
j.Set("a.b.c.d.e.f.g.h.i.j.k", 1)
t2 := gtime.Nanosecond()
t2 := gtime.TimestampNano()
fmt.Println(t2 - t1)
t5 := gtime.Nanosecond()
t5 := gtime.TimestampNano()
v = j.Get("a.b.c.d.e.f.g.h.i.j.k")
t6 := gtime.Nanosecond()
t6 := gtime.TimestampNano()
fmt.Println(v)
fmt.Println(t6 - t5)
j.SetSplitChar('#')
t7 := gtime.Nanosecond()
t7 := gtime.TimestampNano()
v = j.Get("a#b#c#d#e#f#g#h#i#j#k")
t8 := gtime.Nanosecond()
t8 := gtime.TimestampNano()
fmt.Println(v)
fmt.Println(t8 - t7)
}
func testViolenceCheck() {
j := gjson.New(nil)
t1 := gtime.Nanosecond()
t1 := gtime.TimestampNano()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t2 := gtime.Nanosecond()
t2 := gtime.TimestampNano()
fmt.Println(t2 - t1)
t3 := gtime.Nanosecond()
t3 := gtime.TimestampNano()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t4 := gtime.Nanosecond()
t4 := gtime.TimestampNano()
fmt.Println(t4 - t3)
t5 := gtime.Nanosecond()
t5 := gtime.TimestampNano()
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
t6 := gtime.Nanosecond()
t6 := gtime.TimestampNano()
fmt.Println(t6 - t5)
j.SetViolenceCheck(false)
t7 := gtime.Nanosecond()
t7 := gtime.TimestampNano()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t8 := gtime.Nanosecond()
t8 := gtime.TimestampNano()
fmt.Println(t8 - t7)
t9 := gtime.Nanosecond()
t9 := gtime.TimestampNano()
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
t10 := gtime.Nanosecond()
t10 := gtime.TimestampNano()
fmt.Println(t10 - t9)
}

View File

@ -3,34 +3,16 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
"io"
)
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
saveDir := "/tmp/"
for _, item := range r.GetMultipartFiles("upload-file") {
file, err := item.Open()
if err != nil {
r.Response.Write(err)
return
}
defer file.Close()
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
if err != nil {
r.Response.Write(err)
return
}
defer f.Close()
if _, err := io.Copy(f, file); err != nil {
r.Response.Write(err)
return
}
saveDirPath := "/tmp/"
files := r.GetUploadFiles("upload-file")
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.Write("upload successfully")
r.Response.WriteExit("upload successfully")
}
// UploadShow shows uploading simgle file page.
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/upload", func(group *ghttp.RouterGroup) {
group.ALL("/", Upload)
group.POST("/", Upload)
group.ALL("/show", UploadShow)
group.ALL("/batch", UploadShowBatch)
})

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

@ -3,34 +3,16 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
"io"
)
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
saveDir := "/tmp/"
for _, item := range r.GetMultipartFiles("upload-file") {
file, err := item.Open()
if err != nil {
r.Response.Write(err)
return
}
defer file.Close()
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
if err != nil {
r.Response.Write(err)
return
}
defer f.Close()
if _, err := io.Copy(f, file); err != nil {
r.Response.Write(err)
return
}
saveDirPath := "/tmp/"
files := r.GetUploadFiles("file")
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.Write("upload successfully")
r.Response.WriteExit("upload successfully")
}
// UploadShow shows uploading simgle file page.
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/upload", func(group *ghttp.RouterGroup) {
group.ALL("/", Upload)
group.POST("/", Upload)
group.ALL("/show", UploadShow)
group.ALL("/batch", UploadShowBatch)
})

View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"io/ioutil"
)
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.BindHandler("/", func(r *ghttp.Request) {
body1 := r.GetBody()
body2, _ := ioutil.ReadAll(r.Body)
fmt.Println(body1)
fmt.Println(body2)
r.Response.Write("hello world")
})
s.SetPort(8999)
s.Run()
}

View File

@ -3,6 +3,7 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func MiddlewareCORS(r *ghttp.Request) {
@ -11,6 +12,7 @@ func MiddlewareCORS(r *ghttp.Request) {
}
func Order(r *ghttp.Request) {
glog.Println("order")
r.Response.Write("GET")
}
@ -18,7 +20,7 @@ func main() {
s := g.Server()
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
group.GET("/order", Order)
})
s.SetPort(8199)
s.Run()

View File

@ -20,7 +20,7 @@ func main() {
s := g.Server()
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
group.GET("/order", Order)
})
s.SetPort(8199)
s.Run()

View File

@ -26,7 +26,7 @@ func main() {
s := g.Server()
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
group.GET("/order", Order)
})
s.SetPort(8199)
s.Run()

View File

@ -19,7 +19,7 @@ func MiddlewareAuth(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/admin", func(group *ghttp.RouterGroup) {
g.MiddlewarePattern("/*action", func(r *ghttp.Request) {
group.Middleware(func(r *ghttp.Request) {
if action := r.GetRouterString("action"); action != "" {
switch action {
case "login":

View File

@ -25,14 +25,12 @@ func MiddlewareCORS(r *ghttp.Request) {
func MiddlewareLog(r *ghttp.Request) {
r.Middleware.Next()
glog.Println(r.Response.Status, r.URL.Path)
g.Log().Println(r.Response.Status, r.URL.Path)
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareLog)
})
s.Use(MiddlewareLog)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
group.ALL("/user/list", func(r *ghttp.Request) {

View File

@ -13,20 +13,20 @@ func main() {
r.Middleware.Next()
r.Response.Write("end")
})
g.Group("/order", func(group *ghttp.RouterGroup) {
g.GET("/list", func(r *ghttp.Request) {
group.Group("/order", func(group *ghttp.RouterGroup) {
group.GET("/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
})
g.Group("/user", func(group *ghttp.RouterGroup) {
g.GET("/info", func(r *ghttp.Request) {
group.Group("/user", func(group *ghttp.RouterGroup) {
group.GET("/info", func(r *ghttp.Request) {
r.Response.Write("info")
})
g.POST("/edit", func(r *ghttp.Request) {
group.POST("/edit", func(r *ghttp.Request) {
r.Response.Write("edit")
})
})
g.Group("/hook", func(group *ghttp.RouterGroup) {
group.Group("/hook", func(group *ghttp.RouterGroup) {
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook any")
})

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

@ -9,14 +9,14 @@ import (
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
g.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
group.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
g.GET("/get", func(r *ghttp.Request) {
group.GET("/get", func(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
})
g.GET("/clear", func(r *ghttp.Request) {
group.GET("/clear", func(r *ghttp.Request) {
r.Session.Clear()
})
})

View File

@ -0,0 +1,29 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetSessionMaxAge(2 * time.Minute)
s.SetSessionStorage(gsession.NewStorageRedis(g.Redis()))
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
group.GET("/get", func(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
})
group.GET("/clear", func(r *ghttp.Request) {
r.Session.Clear()
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,17 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.WriteTplContent(`${.name}`, g.Map{
"name": "john",
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,2 @@
[viewer]
delimiters = ["${", "}"]

View File

@ -8,7 +8,6 @@ import (
func main() {
s := ghttp.GetServer()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World")
r.Response.WriteTpl("index.tpl", g.Map{
"title": "Test",
"name": "John",

View File

@ -27,7 +27,7 @@ func main() {
},
MemUsed: 15560320,
MemTotal: 16333788,
Time: int(gtime.Second()),
Time: int(gtime.Timestamp()),
})
if err != nil {
panic(err)

View File

@ -11,7 +11,7 @@ import (
func main() {
for {
time.Sleep(time.Second)
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, 60000000*1000); err == nil {
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, time.Hour); err == nil {
fmt.Println(f.Name())
f.Close()
} else {

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

@ -0,0 +1,11 @@
package main
import (
"github.com/gogf/gf/os/glog"
)
func main() {
l := glog.New()
l.SetLevelPrefix(glog.LEVEL_DEBU, "debug")
l.Debug("test")
}

View File

@ -1,16 +0,0 @@
package main
import (
"github.com/gogf/gf/os/gres"
_ "github.com/gogf/gf/os/gres/testdata"
)
func main() {
gres.Dump()
//file := gres.Get("www")
//fmt.Println(file.Open())
//g.Dump(gres.ScanDir("/root/image", "*"))
//g.Dump(gres.Scan("/root/image/", "*", true))
//g.Dump(gres.Scan("/template", "*"))
//g.Dump(gres.Scan("/template/layout2", "*.html", true))
}

View File

@ -0,0 +1,20 @@
package main
import (
_ "github.com/gogf/gf/os/gres/testdata/example/boot"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/template", func(r *ghttp.Request) {
r.Response.WriteTplDefault(g.Map{
"name": "GoFrame",
})
})
})
s.Run()
}

View File

@ -9,7 +9,7 @@ import (
)
func main() {
start := gtime.Millisecond()
start := gtime.TimestampMilli()
wg := sync.WaitGroup{}
for i := 0; i < 100000; i++ {
wg.Add(1)
@ -19,5 +19,5 @@ func main() {
}()
}
wg.Wait()
fmt.Println("time spent:", gtime.Millisecond()-start)
fmt.Println("time spent:", gtime.TimestampMilli()-start)
}

View File

@ -10,7 +10,7 @@ import (
)
func main() {
start := gtime.Millisecond()
start := gtime.TimestampMilli()
wg := sync.WaitGroup{}
for i := 0; i < 100000; i++ {
wg.Add(1)
@ -21,5 +21,5 @@ func main() {
}
wg.Wait()
fmt.Println(grpool.Size())
fmt.Println("time spent:", gtime.Millisecond()-start)
fmt.Println("time spent:", gtime.TimestampMilli()-start)
}

View File

@ -9,8 +9,8 @@ import (
func main() {
fmt.Println("Date :", gtime.Date())
fmt.Println("Datetime :", gtime.Datetime())
fmt.Println("Second :", gtime.Second())
fmt.Println("Millisecond:", gtime.Millisecond())
fmt.Println("Microsecond:", gtime.Microsecond())
fmt.Println("Nanosecond :", gtime.Nanosecond())
fmt.Println("Second :", gtime.Timestamp())
fmt.Println("Millisecond:", gtime.TimestampMilli())
fmt.Println("Microsecond:", gtime.TimestampMicro())
fmt.Println("Nanosecond :", gtime.TimestampNano())
}

View File

@ -1,12 +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"
[viewer]
delimiters = ["${", "}"]
autoencode = true

View File

@ -1,11 +0,0 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
)
func main() {
fmt.Println(g.Cfg().FilePath())
g.Cfg().Dump()
}

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

@ -4,13 +4,12 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gpage"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/page/demo", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
page := r.GetPage(100, 10)
buffer, _ := gview.ParseContent(`
<html>
<head>

View File

@ -4,14 +4,13 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gpage"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/page/ajax", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
page.EnableAjax("DoAjax")
page := r.GetPage(100, 10)
page.AjaxActionName = "DoAjax"
buffer, _ := gview.ParseContent(`
<html>
<head>
@ -29,11 +28,17 @@ func main() {
</script>
</head>
<body>
<div>{{.page}}</div>
<div>{{.page1}}</div>
<div>{{.page2}}</div>
<div>{{.page3}}</div>
<div>{{.page4}}</div>
</body>
</html>
`, g.Map{
"page": page.GetContent(1),
"page1": page.GetContent(1),
"page2": page.GetContent(2),
"page3": page.GetContent(3),
"page4": page.GetContent(4),
})
r.Response.Write(buffer)
})

View File

@ -8,7 +8,7 @@ import (
"github.com/gogf/gf/util/gpage"
)
// 分页标签使用li标签包裹
// wrapContent wraps each of the page tag with html li and ul.
func wrapContent(page *gpage.Page) string {
content := page.GetContent(4)
content = gstr.ReplaceByMap(content, map[string]string{
@ -21,9 +21,9 @@ func wrapContent(page *gpage.Page) string {
}
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/page/custom1/*page", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
page := r.GetPage(100, 10)
content := wrapContent(page)
buffer, _ := gview.ParseContent(`
<html>

View File

@ -7,7 +7,7 @@ import (
"github.com/gogf/gf/util/gpage"
)
// 自定义分页名称
// pageContent customizes the page tag name.
func pageContent(page *gpage.Page) string {
page.NextPageTag = "NextPage"
page.PrevPageTag = "PrevPage"
@ -15,16 +15,16 @@ func pageContent(page *gpage.Page) string {
page.LastPageTag = "LastPage"
pageStr := page.FirstPage()
pageStr += page.PrevPage()
pageStr += page.PageBar("current-page")
pageStr += page.PageBar()
pageStr += page.NextPage()
pageStr += page.LastPage()
return pageStr
}
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/page/custom2/*page", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
page := r.GetPage(100, 10)
buffer, _ := gview.ParseContent(`
<html>
<head>

View File

@ -4,13 +4,12 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gpage"
)
func main() {
s := g.Server()
s.BindHandler("/page/static/*page", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
page := r.GetPage(100, 10)
buffer, _ := gview.ParseContent(`
<html>
<head>

View File

@ -4,13 +4,12 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gpage"
)
func main() {
s := g.Server()
s.BindHandler("/:obj/*action/{page}.html", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
page := r.GetPage(100, 10)
buffer, _ := gview.ParseContent(`
<html>
<head>

View File

@ -4,14 +4,13 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gpage"
)
func main() {
s := g.Server()
s.BindHandler("/page/template/{page}.html", func(r *ghttp.Request) {
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
page.SetUrlTemplate("/order/list/{.page}.html")
page := r.GetPage(100, 10)
page.UrlTemplate = "/order/list/{.page}.html"
buffer, _ := gview.ParseContent(`
<html>
<head>

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

@ -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" = "字段不能为空"

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/

View File

@ -4,6 +4,7 @@ go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "1.14.x"
branches:
only:
@ -12,7 +13,7 @@ branches:
- staging
env:
- GF_DEV=1 GO111MODULE=on
- GF_DEBUG=1 GO111MODULE=on
services:
- mysql

View File

@ -2,6 +2,7 @@
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
> If you cannot view the donation image, please click [here](https://goframe.org/images/donate.png).
| Name | Channel | Amount | Comment
|---|---|--- | ---
@ -14,7 +15,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00 |
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00 |
|[arden](https://github.com/arden)|alipay|¥10.00 |
|[macnie](https://www.macnie.com)|wechat|¥100.00 |
|[macnie](https://www.macnie.com)|wechat|¥110.00 |
|lah|wechat|¥100.00 |
|x*z|wechat|¥20.00 |
|潘兄|wechat|¥100.00 |
@ -39,9 +40,57 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|R*s|wechat|¥18.88| 谢谢GF辛苦了
|粟*e|wechat|¥50.00|
|[李超](https://github.com/effortlee)|wechat|¥124.00|
|张炳贤|wechat+qq|¥600.00|
|soidea666|wechat+qq|¥800.00|
|[王哈哈](https://gitee.com/develop1024)|wechat|¥6.66| 希望gf越来越好
|夕景|alipay+qq|¥9.96+3.57|
|struggler|alipay|¥18.80|
|*铁|wechat|¥0.01|
|C*e|wechat|¥66.66| GF越来越好棒👍
|(佚名)|wechat|¥6.66| (名字打不出来也没备注捐赠时间2020-02-21 14:24:34)
|[王飞](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| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
|[李海峰](https://gitee.com/dlhf)|gitee|¥10.00| 希望GF越来越好框架很牛逼
|陆昱天|alipay|¥100.00|
|[Dockercore](https://github.com/dockercore)|wechat|¥200.00| 非常喜欢!简洁好用!文档超级全!
|🚶|wechat|¥6.88| 喝杯冰阔落
|a*l|wechat|¥10.00| gf
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|重庆宝尔威科技|wechat|¥6.66|
|琦玉-QPT|wechat|¥6.66|
|sailsea|wechat|¥11.00|
|[seny0929](https://gitee.com/seny0929)|wechat|¥99.90|
|*华|wechat|¥6.66| 感谢郭强的热心
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|北京京纬互动科技|alipay|¥200.00|
|[米司特包](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|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|
<img src="https://goframe.org/images/donate.png"/>

118
README.MD
View File

@ -1,6 +1,6 @@
# GoFrame
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
@ -9,13 +9,13 @@
English | [简体中文](README_ZH.MD)
`GF(GoFrame)` is a modular, 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,36 +28,122 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.10
golang version >= 1.11
```
# Documentation
# Packages
1. **Primary Package**
* [APIDoc](https://godoc.org/github.com/gogf/gf)
* [中文文档](https://goframe.org)
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.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=10"/>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# 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
* 中文官网: https://goframe.org
* GoDoc API: https://godoc.org/github.com/gogf/gf
# Discussion
- QQ Group[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
- WX GroupAdd friend`389961817` in WeChat, commenting `GF`
- Issueshttps://github.com/gogf/gf/issues
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
# License
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Known Users
Logos are not authorized to be shown due to trademark copyrights.
# 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)?
# Sponsors
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>
<!--
# Sponsor
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.
-->

View File

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

View File

@ -1,3 +1,5 @@
> This markdown is deprecated and will be removed in future. All TODO features are staged in the issue: https://github.com/gogf/gf/issues
# ON THE WAY
1. 增加图形验证码支持,至少支持数字和英文字母;
1. Cookie&Session数据池化处理
@ -10,7 +12,6 @@
1. gjson对大json数据的解析效率问题
1. ghttp增加route name特性并同时支持backend和template(提供内置函数)引用可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则最好做成链式操作
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节)
1. ghttp.Request增加对输入参数的自动HtmlEncode机制
1. 常量命名风格根据golint进行修改
1. 开放rwmutex包并将gjson的互斥锁使用自定义的mutex替换
@ -21,8 +22,6 @@
- ghttp Server&Client basic auth、
- glog分类&日志等级&链式操作、gdb debug自动输出调试信息、gmlock内存锁、
1. 服务注册域名增加对泛域名的支持;
1. Cookie设置中文失效问题
1. 使用gconv将slice映射到struct属性上例如redis hscan的结果集
1. 项目参考:
- https://github.com/namreg/godown
- https://github.com/Masterminds/sprig
@ -31,32 +30,36 @@
1. 路由增加不区分大小写得匹配方式;
1. 改进WebServer获取POST参数处理逻辑当提交非form数据时例如json数据针对某些方法可以直接解析
1. WebServer增加可选择的路由覆盖配置默认情况下不覆盖
1. grpool性能压测结果变慢的问题
1. 增加jumplist的数据结构容器
1. DelayQueue/PriorityQueue
1. 权限管理模块;
1. 从ghttp中剥离SESSION功能构成单独的模块gsession
1. 改进gproc进程间通信处理逻辑提高稳定性以应对进程间大批量的数据发送/接收;
1. ghttp的热重启的本地进程端口监听在不使用该特性时默认关闭掉
1. gtcp增加对TLS加密通信的支持
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持
1. 添加sqlite数据库的单元测试用例
1. gredis增加cluster支持
1. gset.Add/Remove/Contains方法增加批量操作支持
1. gmlock增加手动清理机制当内存锁不再使用时由调用端决定是否清理内存锁
1. gtimer增加DelayAdd*方法返回Entry对象以便DelayAdd*的定时任务也能进行状态控制gcron同理需要改进
1. 改进gdb对pgsql/mssql/oracle的支持使用方法覆盖的方式改进操作而不是完全依靠正则替换的方式
1. gdb的Cache缓存功能增加可自定义缓存接口以便支持外部缓存功能缓存接口可以通过io.ReadWriter接口实现
1. grpool增加支持阻塞添加任务接口
1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用
1. 增加g.Table快捷方法以方便操作数据表但是得考虑后续模型操作设计特别是脚手架的模型管理
1. 改进ghttp分组路由中对hook的支持方式以便格式与BindHookHandler统一
# DONE
1. Cookie设置中文失效问题
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节)
1. grpool性能压测结果变慢的问题
1. ghttp的热重启的本地进程端口监听在不使用该特性时默认关闭掉
1. gtcp增加对TLS加密通信的支持
1. 改进gdb对pgsql/mssql/oracle的支持使用方法覆盖的方式改进操作而不是完全依靠正则替换的方式
1. gdb的Cache缓存功能增加可自定义缓存接口以便支持外部缓存功能缓存接口可以通过io.ReadWriter接口实现
1. 改进ghttp分组路由中对hook的支持方式以便格式与BindHookHandler统一
1. 使用gconv将slice映射到struct属性上例如redis hscan的结果集
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换
2. ghttp.Server请求执行中增加服务退出的方法不再执行后续操作
3. ghttp.Response对象完善并改进数据返回方法(Write/WriteString)

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

@ -9,7 +9,9 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -19,8 +21,11 @@ import (
"github.com/gogf/gf/util/grand"
)
// 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{}
}
@ -41,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),
}
}
@ -76,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,
}
}
@ -88,26 +93,31 @@ 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,
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *Array) Get(index int) interface{} {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *Array) Get(index int) (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return nil, false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *Array) Set(index int, value interface{}) *Array {
func (a *Array) Set(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -153,45 +163,70 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *Array) InsertBefore(index int, value interface{}) *Array {
func (a *Array) InsertBefore(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *Array) InsertAfter(index int, value interface{}) *Array {
func (a *Array) InsertAfter(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]interface{}{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
return nil
}
// Remove removes an item by index.
func (a *Array) Remove(index int) interface{} {
// If the given <index> is out of range of the array, the <found> is false.
func (a *Array) Remove(index int) (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency。
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
if index < 0 || index >= len(a.array) {
return nil, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *Array) RemoveValue(value interface{}) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
}
return false
}
// PushLeft pushes one or multiple items to the beginning of array.
@ -212,52 +247,68 @@ func (a *Array) PushRight(value ...interface{}) *Array {
}
// PopRand randomly pops and return an item out of array.
func (a *Array) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *Array) PopRand() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *Array) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLeft pops and returns an item from the beginning of array.
func (a *Array) PopLeft() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *Array) PopLeft() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return nil, false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *Array) PopRight() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *Array) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return nil, false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *Array) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -268,9 +319,14 @@ func (a *Array) PopLefts(size int) []interface{} {
func (a *Array) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -418,10 +474,11 @@ func (a *Array) Contains(value interface{}) bool {
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) 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 v == value {
@ -429,12 +486,11 @@ func (a *Array) Search(value interface{}) 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 *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
@ -469,32 +525,16 @@ 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>,
// keys starting at the <startIndex> parameter.
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -503,7 +543,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -557,27 +597,27 @@ func (a *Array) Pad(size int, val interface{}) *Array {
}
// Rand randomly returns one item from array(no deleting).
func (a *Array) Rand() interface{} {
func (a *Array) Rand() (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return nil, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *Array) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.
@ -604,6 +644,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))
@ -630,7 +673,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()
@ -642,7 +685,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()
@ -677,7 +720,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)
@ -685,8 +729,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()
@ -696,3 +739,60 @@ func (a *Array) UnmarshalJSON(b []byte) error {
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *Array) UnmarshalValue(value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceAny(value)
}
return nil
}
// FilterNil removes all nil value of the array.
func (a *Array) FilterNil() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *Array) FilterEmpty() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *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

@ -9,6 +9,7 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math"
"sort"
@ -18,8 +19,11 @@ import (
"github.com/gogf/gf/util/grand"
)
// 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
}
@ -35,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),
}
}
@ -60,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,
}
}
@ -72,26 +76,31 @@ 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,
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *IntArray) Get(index int) int {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *IntArray) Get(index int) (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return 0, false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *IntArray) Set(index int, value int) *IntArray {
func (a *IntArray) Set(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -127,8 +136,7 @@ func (a *IntArray) Sum() (sum int) {
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
// The parameter <reverse> controls whether sort in increasing order(default) or decreasing order.
func (a *IntArray) Sort(reverse ...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
@ -156,45 +164,70 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
func (a *IntArray) InsertBefore(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
func (a *IntArray) InsertAfter(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]int{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
return nil
}
// Remove removes an item by index.
func (a *IntArray) Remove(index int) int {
// If the given <index> is out of range of the array, the <found> is false.
func (a *IntArray) Remove(index int) (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
if index < 0 || index >= len(a.array) {
return 0, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *IntArray) RemoveValue(value int) bool {
if i := a.Search(value); i != -1 {
_, found := a.Remove(i)
return found
}
return false
}
// PushLeft pushes one or multiple items to the beginning of array.
@ -215,52 +248,72 @@ func (a *IntArray) PushRight(value ...int) *IntArray {
}
// PopLeft pops and returns an item from the beginning of array.
func (a *IntArray) PopLeft() int {
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopLeft() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return 0, false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *IntArray) PopRight() int {
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return 0, false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *IntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *IntArray) PopRand() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -268,12 +321,19 @@ func (a *IntArray) PopLefts(size int) []int {
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *IntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -430,10 +490,11 @@ func (a *IntArray) Contains(value int) bool {
// Search searches array by <value>, returns the index of <value>,
// 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
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
@ -441,12 +502,11 @@ 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++ {
@ -481,32 +541,16 @@ 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>,
// keys starting at the <startIndex> parameter.
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
func (a *IntArray) Fill(startIndex int, num int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -515,7 +559,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -569,27 +613,27 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
}
// Rand randomly returns one item from array(no deleting).
func (a *IntArray) Rand() int {
func (a *IntArray) Rand() (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return 0, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *IntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]int, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.
@ -616,6 +660,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))
@ -642,7 +689,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()
@ -654,7 +701,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()
@ -672,7 +719,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)
@ -680,8 +728,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()
@ -691,3 +738,45 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *IntArray) UnmarshalValue(value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceInt(value)
}
return nil
}
// FilterEmpty removes all zero value of the array.
func (a *IntArray) FilterEmpty() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *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

@ -9,6 +9,8 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -19,8 +21,11 @@ import (
"github.com/gogf/gf/util/grand"
)
// 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
}
@ -36,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),
}
}
@ -46,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,
}
}
@ -58,26 +63,31 @@ 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,
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *StrArray) Get(index int) string {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *StrArray) Get(index int) (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return "", false
}
return a.array[index], true
}
// Set sets value to specified index.
func (a *StrArray) Set(index int, value string) *StrArray {
func (a *StrArray) Set(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
a.array[index] = value
return a
return nil
}
// SetArray sets the underlying slice array with the given <array>.
@ -142,45 +152,70 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *StrArray) InsertBefore(index int, value string) *StrArray {
func (a *StrArray) InsertBefore(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
return nil
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *StrArray) InsertAfter(index int, value string) *StrArray {
func (a *StrArray) InsertAfter(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
}
rear := append([]string{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
return nil
}
// Remove removes an item by index.
func (a *StrArray) Remove(index int) string {
// If the given <index> is out of range of the array, the <found> is false.
func (a *StrArray) Remove(index int) (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency。
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) {
if index < 0 || index >= len(a.array) {
return "", false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *StrArray) RemoveValue(value string) bool {
if i := a.Search(value); i != -1 {
_, found := a.Remove(i)
return found
}
return false
}
// PushLeft pushes one or multiple items to the beginning of array.
@ -201,52 +236,72 @@ func (a *StrArray) PushRight(value ...string) *StrArray {
}
// PopLeft pops and returns an item from the beginning of array.
func (a *StrArray) PopLeft() string {
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopLeft() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return "", false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *StrArray) PopRight() string {
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return "", false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *StrArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *StrArray) PopRand() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -254,12 +309,19 @@ func (a *StrArray) PopLefts(size int) []string {
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *StrArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -413,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 {
@ -427,11 +506,11 @@ 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++ {
@ -466,32 +545,16 @@ 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>,
// keys starting at the <startIndex> parameter.
func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
func (a *StrArray) Fill(startIndex int, num int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
if startIndex < 0 || startIndex > len(a.array) {
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
@ -500,7 +563,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
a.array[i] = value
}
}
return a
return nil
}
// Chunk splits an array into multiple arrays,
@ -554,27 +617,27 @@ func (a *StrArray) Pad(size int, value string) *StrArray {
}
// Rand randomly returns one item from array(no deleting).
func (a *StrArray) Rand() string {
func (a *StrArray) Rand() (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return "", false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *StrArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]string, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Shuffle randomly shuffles the array.
@ -601,6 +664,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)
@ -627,7 +693,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()
@ -639,7 +705,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()
@ -668,7 +734,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)
@ -676,8 +743,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()
@ -687,3 +753,45 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *StrArray) UnmarshalValue(value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceStr(value)
}
return nil
}
// FilterEmpty removes all empty string value of the array.
func (a *StrArray) FilterEmpty() *StrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *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

@ -10,31 +10,35 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/empty"
"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"
)
// It's using increasing order in default.
// SortedArray is a golang sorted array with rich features.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type 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)
}
// NewSortedArray creates and returns an empty sorted array.
// The parameter <safe> is used to specify whether using array in concurrent-safety, which is false in default.
// The parameter <comparator> used to compare values to sort in array,
// if it returns value < 0, means v1 < v2;
// if it returns value = 0, means v1 = v2;
// if it returns value > 0, means v1 > v2;
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
return NewSortedArraySize(0, comparator, safe...)
}
@ -44,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,
}
@ -73,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
}
@ -93,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
})
}
@ -116,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
}
@ -130,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 {
@ -147,73 +156,106 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedArray) Get(index int) interface{} {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return nil, false
}
return a.array[index], true
}
// Remove removes an item by index.
func (a *SortedArray) Remove(index int) interface{} {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
if index < 0 || index >= len(a.array) {
return nil, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedArray) RemoveValue(value interface{}) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
}
return false
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedArray) PopLeft() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return nil, false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *SortedArray) PopRight() interface{} {
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return nil, false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *SortedArray) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedArray) PopRand() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedArray) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
@ -222,9 +264,13 @@ func (a *SortedArray) PopRands(size int) []interface{} {
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -235,9 +281,14 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -346,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()
@ -383,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
@ -413,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()
}
@ -425,12 +476,15 @@ func (a *SortedArray) SetUnique(unique bool) *SortedArray {
func (a *SortedArray) Unique() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.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++
@ -462,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
}
@ -479,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,
@ -522,33 +566,36 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedArray) Rand() interface{} {
func (a *SortedArray) Rand() (value interface{}, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return nil, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedArray) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.
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))
@ -575,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()
@ -587,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()
@ -622,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()
@ -642,10 +688,103 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &a.array); err != nil {
return err
}
if a.comparator != nil {
if a.comparator != nil && a.array != nil {
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
// Note that the comparator is set as string comparator in default.
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceAny(value)
}
if a.comparator != nil && a.array != nil {
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
}
return err
}
// FilterNil removes all nil value of the array.
func (a *SortedArray) FilterNil() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *SortedArray) FilterEmpty() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
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

@ -13,17 +13,20 @@ import (
"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"
)
// It's using increasing order in default.
// SortedIntArray is a golang sorted int array with rich features.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type 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)
}
@ -47,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,
}
}
@ -93,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
}
@ -103,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
}
@ -116,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 {
@ -133,84 +141,125 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedIntArray) Get(index int) int {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedIntArray) Get(index int) (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return 0, false
}
return a.array[index], true
}
// Remove removes an item by index.
func (a *SortedIntArray) Remove(index int) int {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedIntArray) Remove(index int) (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool) {
if index < 0 || index >= len(a.array) {
return 0, false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedIntArray) RemoveValue(value int) bool {
if i := a.Search(value); i != -1 {
_, found := a.Remove(i)
return found
}
return false
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedIntArray) PopLeft() int {
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopLeft() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return 0, false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *SortedIntArray) PopRight() int {
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return 0, false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *SortedIntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedIntArray) PopRand() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -218,12 +267,19 @@ func (a *SortedIntArray) PopLefts(size int) []int {
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedIntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -375,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
@ -405,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()
}
@ -416,18 +472,21 @@ func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
// Unique uniques the array, clear repeated items.
func (a *SortedIntArray) Unique() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.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++
}
}
a.mu.Unlock()
return a
}
@ -471,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,
@ -514,33 +557,36 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedIntArray) Rand() int {
func (a *SortedIntArray) Rand() (value int, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return 0, false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedIntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]int, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.
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))
@ -567,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()
@ -579,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()
@ -597,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)
@ -605,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()
@ -616,6 +661,76 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &a.array); err != nil {
return err
}
sort.Ints(a.array)
if a.array != nil {
sort.Ints(a.array)
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
a.comparator = defaultComparatorInt
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceInt(value)
}
if a.array != nil {
sort.Ints(a.array)
}
return err
}
// FilterEmpty removes all zero value of the array.
func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
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

@ -12,18 +12,22 @@ import (
"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"
)
// It's using increasing order in default.
// SortedStrArray is a golang sorted string array with rich features.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type 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)
}
@ -47,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,
}
}
@ -60,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
}
@ -78,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
}
@ -88,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
}
@ -101,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 {
@ -118,84 +127,125 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedStrArray) Get(index int) string {
// Get returns the value by the specified index.
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedStrArray) Get(index int) (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
if index < 0 || index >= len(a.array) {
return "", false
}
return a.array[index], true
}
// Remove removes an item by index.
func (a *SortedStrArray) Remove(index int) string {
// If the given <index> is out of range of the array, the <found> is false.
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(index)
}
// doRemoveWithoutLock removes an item by index without lock.
func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
if index < 0 || index >= len(a.array) {
return "", false
}
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
return value, true
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
value = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
return value, true
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *SortedStrArray) RemoveValue(value string) bool {
if i := a.Search(value); i != -1 {
a.Remove(i)
return true
}
return false
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedStrArray) PopLeft() string {
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopLeft() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
if len(a.array) == 0 {
return "", false
}
value = a.array[0]
a.array = a.array[1:]
return value
return value, true
}
// PopRight pops and returns an item from the end of array.
func (a *SortedStrArray) PopRight() string {
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
if index < 0 {
return "", false
}
value = a.array[index]
a.array = a.array[:index]
return value
return value, true
}
// PopRand randomly pops and return an item out of array.
func (a *SortedStrArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
// Note that if the array is empty, the <found> is false.
func (a *SortedStrArray) PopRand() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
if size <= 0 || len(a.array) == 0 {
return nil
}
if size >= len(a.array) {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[0:size]
a.array = a.array[size:]
@ -203,12 +253,19 @@ func (a *SortedStrArray) PopLefts(size int) []string {
}
// PopRights pops and returns <size> items from the end of array.
// If the given <size> is greater than size of the array, it returns all elements of the array.
// Note that if given <size> <= 0 or the array is empty, it returns nil.
func (a *SortedStrArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size <= 0 || len(a.array) == 0 {
return nil
}
index := len(a.array) - size
if index < 0 {
index = 0
if index <= 0 {
array := a.array
a.array = a.array[:0]
return array
}
value := a.array[index:]
a.array = a.array[:index]
@ -345,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) {
@ -360,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
@ -390,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()
}
@ -401,18 +474,21 @@ func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
// Unique uniques the array, clear repeated items.
func (a *SortedStrArray) Unique() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.array) == 0 {
return a
}
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.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++
}
}
a.mu.Unlock()
return a
}
@ -456,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,
@ -499,33 +559,36 @@ func (a *SortedStrArray) Chunk(size int) [][]string {
}
// Rand randomly returns one item from array(no deleting).
func (a *SortedStrArray) Rand() string {
func (a *SortedStrArray) Rand() (value string, found bool) {
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
if len(a.array) == 0 {
return "", false
}
return a.array[grand.Intn(len(a.array))], true
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedStrArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
if size <= 0 || len(a.array) == 0 {
return nil
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
array := make([]string, size)
for i := 0; i < size; i++ {
array[i] = a.array[grand.Intn(len(a.array))]
}
return n
return array
}
// Join joins array elements with a string <glue>.
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)
@ -552,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()
@ -564,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()
@ -593,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)
@ -601,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()
@ -612,6 +674,76 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &a.array); err != nil {
return err
}
sort.Strings(a.array)
if a.array != nil {
sort.Strings(a.array)
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
if a.comparator == nil {
a.comparator = defaultComparatorStr
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
case string, []byte:
err = json.Unmarshal(gconv.Bytes(value), &a.array)
default:
a.array = gconv.SliceStr(value)
}
if a.array != nil {
sort.Strings(a.array)
}
return err
}
// FilterEmpty removes all empty string value of the array.
func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
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

@ -8,51 +8,52 @@ package garray_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/garray"
)
func Example_basic() {
// 创建普通的数组,默认并发安全(带锁)
a := garray.New(true)
// 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())
@ -60,7 +61,7 @@ func Example_basic() {
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6
// 6 true
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
@ -71,31 +72,129 @@ func Example_basic() {
// []
}
func Example_iterate() {
array := garray.NewStrArrayFrom(g.SliceStr{"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 string) 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 string) bool {
fmt.Println(k, v)
return true
})
// Output:
// 0 a
// 1 b
// 2 c
// 2 c
// 1 b
// 0 a
}
func Example_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 Example_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 Example_rand() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// 随机返回两个数据项(不删除)
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 Example_pop() {
func Example_join() {
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
fmt.Println(array.Join(","))
// Output:
// a,b,c,d
}
func Example_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 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
// 1 true
// [2 3]
// 9
// 9 true
// [7 8]
}
func Example_merge() {
array1 := garray.NewFrom([]interface{}{1, 2})
array2 := garray.NewFrom([]interface{}{3, 4})
slice1 := []interface{}{5, 6}
func Example_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]
}
func Example_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 Example_mergeArray() {
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())
@ -110,3 +209,14 @@ func Example_merge() {
// [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,90 +18,153 @@ 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) {
expect := []int{1, 2, 3, 4, 5, 6}
array := garray.NewIntArray()
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
array.Unique()
gtest.Assert(array.Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 2, 3, 4, 5, 6}
array := garray.NewIntArray()
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
array.Unique()
t.Assert(array.Slice(), expect)
})
}
func Test_SortedIntArray1(t *testing.T) {
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 10; i > -1; i-- {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Add().Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 10; i > -1; i-- {
array.Add(i)
}
t.Assert(array.Slice(), expect)
t.Assert(array.Add().Slice(), expect)
})
}
func Test_SortedIntArray2(t *testing.T) {
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 0; i <= 10; i++ {
array.Add(i)
}
gtest.Assert(array.Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
array := garray.NewSortedIntArray()
for i := 0; i <= 10; i++ {
array.Add(i)
}
t.Assert(array.Slice(), expect)
})
}
func Test_SortedStrArray1(t *testing.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array1 := garray.NewSortedStrArray()
array2 := garray.NewSortedStrArray(true)
for i := 10; i > -1; i-- {
array1.Add(gconv.String(i))
array2.Add(gconv.String(i))
}
gtest.Assert(array1.Slice(), expect)
gtest.Assert(array2.Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array1 := garray.NewSortedStrArray()
array2 := garray.NewSortedStrArray(true)
for i := 10; i > -1; i-- {
array1.Add(gconv.String(i))
array2.Add(gconv.String(i))
}
t.Assert(array1.Slice(), expect)
t.Assert(array2.Slice(), expect)
})
}
func Test_SortedStrArray2(t *testing.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedStrArray()
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
array.Add()
gtest.Assert(array.Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedStrArray()
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
}
t.Assert(array.Slice(), expect)
array.Add()
t.Assert(array.Slice(), expect)
})
}
func Test_SortedArray1(t *testing.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
})
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
t.Assert(array.Slice(), expect)
})
for i := 10; i > -1; i-- {
array.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
}
func Test_SortedArray2(t *testing.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array := garray.NewSortedArray(func1)
array2 := garray.NewSortedArray(func1, true)
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
array2.Add(gconv.String(i))
}
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Add().Slice(), expect)
gtest.Assert(array2.Slice(), expect)
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array := garray.NewSortedArray(func1)
array2 := garray.NewSortedArray(func1, true)
for i := 0; i <= 10; i++ {
array.Add(gconv.String(i))
array2.Add(gconv.String(i))
}
t.Assert(array.Slice(), expect)
t.Assert(array.Add().Slice(), expect)
t.Assert(array2.Slice(), expect)
})
}
func TestNewFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
array1 := garray.NewFromCopy(a1)
gtest.AssertIN(array1.PopRands(2), a1)
gtest.Assert(len(array1.PopRands(1)), 1)
gtest.Assert(len(array1.PopRands(9)), 3)
t.AssertIN(array1.PopRands(2), a1)
t.Assert(len(array1.PopRands(1)), 1)
t.Assert(len(array1.PopRands(9)), 3)
})
}

View File

@ -20,39 +20,62 @@ import (
)
func Test_Array_Basic(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
array2 := garray.NewArrayFrom(expect)
array3 := garray.NewArrayFrom([]interface{}{})
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array3.Search(100), -1)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array2.Remove(3), 3)
gtest.Assert(array2.Remove(1), 1)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
gtest.Assert(array.Contains(100), false)
v, ok = array.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Search(100), 0)
t.Assert(array3.Search(100), -1)
t.Assert(array.Contains(100), true)
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, nil)
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, nil)
t.Assert(ok, false)
v, ok = array2.Remove(3)
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array2.Remove(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
t.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
})
}
func TestArray_Sort(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect1 := []interface{}{0, 1, 2, 3}
expect2 := []interface{}{3, 2, 1, 0}
array := garray.NewArray()
@ -62,78 +85,168 @@ func TestArray_Sort(t *testing.T) {
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) < v2.(int)
})
gtest.Assert(array.Slice(), expect1)
t.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) > v2.(int)
})
gtest.Assert(array.Slice(), expect2)
t.Assert(array.Slice(), expect2)
})
}
func TestArray_Unique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{1, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
})
}
func TestArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.Assert(array.Len(), 0)
t.Assert(array.Slice(), expect)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []interface{}{1, 2})
t.Assert(ok, true)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []interface{}{1, 2})
t.Assert(array.Slice(), []interface{}{1, 2})
})
}
func TestArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
gtest.AssertIN(array.PopRands(2), []interface{}{100, 200, 300, 400, 500, 600})
t.AssertIN(array.PopRands(2), []interface{}{100, 200, 300, 400, 500, 600})
})
}
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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
gtest.C(t, func(t *gtest.T) {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
value2 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{0, 1})
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
gtest.Assert(array1.PopRights(2), []interface{}{5, 6})
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4})
gtest.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
gtest.Assert(array2.Slice(), []interface{}{})
t.Assert(array1.PopLefts(2), []interface{}{0, 1})
t.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []interface{}{5, 6})
t.Assert(array1.Slice(), []interface{}{2, 3, 4})
t.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
t.Assert(array1.Slice(), []interface{}{})
t.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []interface{}{})
})
}
func TestArray_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value1, true)
gtest.Assert(array1.Range(0, 1), []interface{}{0})
gtest.Assert(array1.Range(1, 2), []interface{}{1})
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
gtest.Assert(array1.Range(10, 2), nil)
gtest.Assert(array2.Range(1, 3), []interface{}{1, 2})
t.Assert(array1.Range(0, 1), []interface{}{0})
t.Assert(array1.Range(1, 2), []interface{}{1})
t.Assert(array1.Range(0, 2), []interface{}{0, 1})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 2), nil)
t.Assert(array2.Range(1, 3), []interface{}{1, 2})
})
}
func TestArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
@ -145,7 +258,7 @@ func TestArray_Merge(t *testing.T) {
i2 := []interface{}{4, 5, 6, 7}
array1 := garray.NewArrayFrom(i1)
array2 := garray.NewArrayFrom(i2)
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
t.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
//s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
@ -157,157 +270,192 @@ func TestArray_Merge(t *testing.T) {
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewArrayFrom(i1)
gtest.Assert(a1.Merge(s2).Len(), 6)
gtest.Assert(a1.Merge(i3).Len(), 9)
gtest.Assert(a1.Merge(i4).Len(), 10)
gtest.Assert(a1.Merge(s3).Len(), 12)
gtest.Assert(a1.Merge(s4).Len(), 14)
gtest.Assert(a1.Merge(s5).Len(), 16)
gtest.Assert(a1.Merge(s6).Len(), 19)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i3).Len(), 9)
t.Assert(a1.Merge(i4).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestArray_Fill(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0}
a2 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2, true)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100})
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []interface{}{100, 100})
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []interface{}{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []interface{}{100, 100})
})
}
func TestArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []interface{}{1, 2})
gtest.Assert(chunks[1], []interface{}{3, 4})
gtest.Assert(chunks[2], []interface{}{5})
gtest.Assert(array1.Chunk(0), nil)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestArray_Pad(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
})
}
func TestArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a1, true)
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
gtest.Assert(array1.SubSlice(9, 1), nil)
gtest.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
gtest.Assert(array1.SubSlice(-9, 2), nil)
gtest.Assert(array1.SubSlice(1, -2), nil)
gtest.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
t.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
t.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
t.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
t.Assert(array1.SubSlice(9, 1), nil)
t.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
t.Assert(array1.SubSlice(-9, 2), nil)
t.Assert(array1.SubSlice(1, -2), nil)
t.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
})
}
func TestArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1)
i1 := a1.Rand()
gtest.Assert(a1.Contains(i1), true)
gtest.Assert(a1.Len(), 4)
i1, ok := a1.Rand()
t.Assert(ok, true)
t.Assert(a1.Contains(i1), true)
t.Assert(a1.Len(), 4)
})
}
func TestArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
t.Assert(array1.Shuffle().Len(), 7)
})
}
func TestArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
t.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
})
}
func TestArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, `"a"`, `\a`}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Join("."), `0.1."a".\a`)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
}
func TestArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
})
}
func TestArray_Replace(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"m", "n", "p", "z", "x", "y", "d", "u"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Replace(a2)
gtest.Assert(array2.Len(), 7)
gtest.Assert(array2.Contains("b"), true)
gtest.Assert(array2.Contains(4), true)
gtest.Assert(array2.Contains("v"), false)
t.Assert(array2.Len(), 7)
t.Assert(array2.Contains("b"), true)
t.Assert(array2.Contains(4), true)
t.Assert(array2.Contains("v"), false)
array3 := array1.Replace(a3)
gtest.Assert(array3.Len(), 7)
gtest.Assert(array3.Contains(4), false)
gtest.Assert(array3.Contains("p"), true)
gtest.Assert(array3.Contains("u"), false)
t.Assert(array3.Len(), 7)
t.Assert(array3.Contains(4), false)
t.Assert(array3.Contains("p"), true)
t.Assert(array3.Contains("u"), false)
})
}
func TestArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
a2 := []interface{}{"a", "b", "c"}
array1 := garray.NewArrayFrom(a1)
array1 = array1.SetArray(a2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("b"), true)
gtest.Assert(array1.Contains("5"), false)
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("b"), true)
t.Assert(array1.Contains("5"), false)
})
}
func TestArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{"a", "b", "c"}
a3 := []interface{}{"a", "1", "2"}
@ -316,39 +464,39 @@ func TestArray_Sum(t *testing.T) {
array2 := garray.NewArrayFrom(a2)
array3 := garray.NewArrayFrom(a3)
gtest.Assert(array1.Sum(), 6)
gtest.Assert(array2.Sum(), 0)
gtest.Assert(array3.Sum(), 3)
t.Assert(array1.Sum(), 6)
t.Assert(array2.Sum(), 0)
t.Assert(array3.Sum(), 3)
})
}
func TestArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, 2, 3}
array1 := garray.NewArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1.Len(), 4)
gtest.Assert(array2.Sum(), 6)
gtest.AssertEQ(array1, array2)
t.Assert(array1.Len(), 4)
t.Assert(array2.Sum(), 6)
t.AssertEQ(array1, array2)
})
}
func TestArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "b", "c", "d", "e", "d"}
array1 := garray.NewArrayFrom(a1)
array2 := array1.CountValues()
gtest.Assert(len(array2), 5)
gtest.Assert(array2["b"], 1)
gtest.Assert(array2["d"], 2)
t.Assert(len(array2), 5)
t.Assert(array2["b"], 1)
t.Assert(array2["d"], 2)
})
}
func TestArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1, true)
@ -374,13 +522,13 @@ func TestArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "c", "d"}
a1 := garray.NewArrayFrom(s1, true)
@ -406,32 +554,52 @@ func TestArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestArray_Json(t *testing.T) {
gtest.Case(t, func() {
// pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.Unmarshal(b2, &a2)
gtest.Assert(err2, nil)
gtest.Assert(a2.Slice(), s1)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
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)
gtest.Case(t, func() {
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
Scores *garray.Array
@ -441,59 +609,151 @@ func TestArray_Json(t *testing.T) {
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.Assert(user.Scores, data["Scores"])
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)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
}
func TestArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewArrayFrom(slice)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestArray_RemoveValue(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.Array
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[1,2,3]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{1, 2, 3},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewArrayFromCopy(values)
t.Assert(array.FilterNil().Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
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

@ -21,35 +21,54 @@ import (
)
func Test_IntArray_Basic(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3}
expect2 := []int{}
array := garray.NewIntArrayFrom(expect)
array2 := garray.NewIntArrayFrom(expect2)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array2.Search(100), -1)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Search(100), 0)
t.Assert(array2.Search(100), -1)
t.Assert(array.Contains(100), true)
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
t.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
})
}
func TestIntArray_Sort(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect1 := []int{0, 1, 2, 3}
expect2 := []int{3, 2, 1, 0}
array := garray.NewIntArray()
@ -59,69 +78,108 @@ func TestIntArray_Sort(t *testing.T) {
array2.Append(i)
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
t.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
gtest.Assert(array2.Slice(), expect2)
t.Assert(array.Slice(), expect2)
t.Assert(array2.Slice(), expect2)
})
}
func TestIntArray_Unique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
t.Assert(array.Unique().Slice(), []int{1, 2, 3})
})
}
func TestIntArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.Assert(array.Len(), 0)
t.Assert(array.Slice(), expect)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []int{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []int{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []int{1, 2})
t.Assert(array.Slice(), []int{1, 2})
})
}
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArray()
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
gtest.C(t, func(t *gtest.T) {
value1 := []int{0, 1, 2, 3, 4, 5, 6}
value2 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(value1)
array2 := garray.NewIntArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []int{0, 1})
gtest.Assert(array1.Slice(), []int{2, 3, 4, 5, 6})
gtest.Assert(array1.PopRights(2), []int{5, 6})
gtest.Assert(array1.Slice(), []int{2, 3, 4})
gtest.Assert(array1.PopRights(20), []int{2, 3, 4})
gtest.Assert(array1.Slice(), []int{})
gtest.Assert(array2.PopLefts(20), []int{0, 1, 2, 3, 4, 5, 6})
gtest.Assert(array2.Slice(), []int{})
t.Assert(array1.PopLefts(2), []int{0, 1})
t.Assert(array1.Slice(), []int{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []int{5, 6})
t.Assert(array1.Slice(), []int{2, 3, 4})
t.Assert(array1.PopRights(20), []int{2, 3, 4})
t.Assert(array1.Slice(), []int{})
t.Assert(array2.PopLefts(20), []int{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []int{})
})
}
func TestIntArray_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
value1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(value1)
array2 := garray.NewIntArrayFrom(value1, true)
gtest.Assert(array1.Range(0, 1), []int{0})
gtest.Assert(array1.Range(1, 2), []int{1})
gtest.Assert(array1.Range(0, 2), []int{0, 1})
gtest.Assert(array1.Range(10, 2), nil)
gtest.Assert(array1.Range(-1, 10), value1)
gtest.Assert(array2.Range(1, 2), []int{1})
t.Assert(array1.Range(0, 1), []int{0})
t.Assert(array1.Range(1, 2), []int{1})
t.Assert(array1.Range(0, 2), []int{0, 1})
t.Assert(array1.Range(10, 2), nil)
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array2.Range(1, 2), []int{1})
})
}
func TestIntArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
@ -145,232 +203,332 @@ func TestIntArray_Merge(t *testing.T) {
a7 := garray.NewSortedStrArrayFrom(s1)
a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
gtest.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
gtest.Assert(a1.Merge(a3).Len(), 10)
gtest.Assert(a1.Merge(a4).Len(), 13)
gtest.Assert(a1.Merge(a5).Len(), 15)
gtest.Assert(a1.Merge(a6).Len(), 18)
gtest.Assert(a1.Merge(a7).Len(), 21)
gtest.Assert(a1.Merge(a8).Len(), 23)
t.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
t.Assert(a1.Merge(a3).Len(), 10)
t.Assert(a1.Merge(a4).Len(), 13)
t.Assert(a1.Merge(a5).Len(), 15)
t.Assert(a1.Merge(a6).Len(), 18)
t.Assert(a1.Merge(a7).Len(), 21)
t.Assert(a1.Merge(a8).Len(), 23)
})
}
func TestIntArray_Fill(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0}
a2 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100})
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []int{100, 100})
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []int{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []int{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []int{100, 100})
})
}
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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []int{1, 2})
gtest.Assert(chunks[1], []int{3, 4})
gtest.Assert(chunks[2], []int{5})
gtest.Assert(array1.Chunk(0), nil)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []int{1, 2})
t.Assert(chunks[1], []int{3, 4})
t.Assert(chunks[2], []int{5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int{1, 2, 3})
t.Assert(chunks[1], []int{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []int{1, 2})
t.Assert(chunks[1], []int{3, 4})
t.Assert(chunks[2], []int{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int{1, 2, 3})
t.Assert(chunks[1], []int{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestIntArray_Pad(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0, 1, 1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1, 0, 1, 1})
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []int{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []int{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []int{1, 0, 1, 1})
})
}
func TestIntArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a1, true)
gtest.Assert(array1.SubSlice(6), []int{6})
gtest.Assert(array1.SubSlice(5), []int{5, 6})
gtest.Assert(array1.SubSlice(8), nil)
gtest.Assert(array1.SubSlice(0, 2), []int{0, 1})
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
gtest.Assert(array1.SubSlice(5, 8), []int{5, 6})
gtest.Assert(array1.SubSlice(-1, 1), []int{6})
gtest.Assert(array1.SubSlice(-1, 9), []int{6})
gtest.Assert(array1.SubSlice(-2, 3), []int{5, 6})
gtest.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2})
gtest.Assert(array1.SubSlice(-8, 3), nil)
gtest.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5})
gtest.Assert(array1.SubSlice(-9, 3), nil)
gtest.Assert(array1.SubSlice(1, -1), []int{0})
gtest.Assert(array1.SubSlice(1, -3), nil)
gtest.Assert(array2.SubSlice(0, 2), []int{0, 1})
t.Assert(array1.SubSlice(6), []int{6})
t.Assert(array1.SubSlice(5), []int{5, 6})
t.Assert(array1.SubSlice(8), nil)
t.Assert(array1.SubSlice(0, 2), []int{0, 1})
t.Assert(array1.SubSlice(2, 2), []int{2, 3})
t.Assert(array1.SubSlice(5, 8), []int{5, 6})
t.Assert(array1.SubSlice(-1, 1), []int{6})
t.Assert(array1.SubSlice(-1, 9), []int{6})
t.Assert(array1.SubSlice(-2, 3), []int{5, 6})
t.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2})
t.Assert(array1.SubSlice(-8, 3), nil)
t.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5})
t.Assert(array1.SubSlice(-9, 3), nil)
t.Assert(array1.SubSlice(1, -1), []int{0})
t.Assert(array1.SubSlice(1, -3), nil)
t.Assert(array2.SubSlice(0, 2), []int{0, 1})
})
}
func TestIntArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), 2)
gtest.Assert(len(array1.Rands(10)), 7)
gtest.AssertIN(array1.Rands(1)[0], a1)
gtest.AssertIN(array1.Rand(), a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
v, ok := array1.Rand()
t.AssertIN(v, a1)
t.Assert(ok, true)
})
}
func TestIntArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{100, 200, 300, 400, 500, 600}
array := garray.NewIntArrayFrom(a1)
ns1 := array.PopRands(2)
gtest.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
gtest.Assert(len(ns1), 2)
t.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
t.Assert(len(ns1), 2)
ns2 := array.PopRands(7)
gtest.Assert(len(ns2), 4)
gtest.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
t.Assert(len(ns2), 4)
t.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
})
}
func TestIntArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
t.Assert(array1.Shuffle().Len(), 7)
})
}
func TestIntArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
t.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
})
}
func TestIntArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestIntArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.String(), "[0,1,2,3,4,5,6]")
t.Assert(array1.String(), "[0,1,2,3,4,5,6]")
})
}
func TestIntArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
a2 := []int{6, 7}
array1 := garray.NewIntArrayFrom(a1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1, []int{6, 7})
t.Assert(array1.Len(), 2)
t.Assert(array1, []int{6, 7})
})
}
func TestIntArray_Replace(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
a2 := []int{6, 7}
a3 := []int{9, 10, 11, 12, 13}
array1 := garray.NewIntArrayFrom(a1)
array1.Replace(a2)
gtest.Assert(array1, []int{6, 7, 3, 5})
t.Assert(array1, []int{6, 7, 3, 5})
array1.Replace(a3)
gtest.Assert(array1, []int{9, 10, 11, 12})
t.Assert(array1, []int{9, 10, 11, 12})
})
}
func TestIntArray_Clear(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
t.Assert(array1.Len(), 0)
})
}
func TestIntArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
t.Assert(array1, array2)
})
}
func TestArray_Get(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Get(2), 3)
gtest.Assert(array1.Len(), 4)
v, ok := array1.Get(2)
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
})
}
func TestIntArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Sum(), 11)
t.Assert(array1.Sum(), 11)
})
}
func TestIntArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5, 3}
array1 := garray.NewIntArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 4)
gtest.Assert(m1[1], 1)
gtest.Assert(m1[3], 2)
t.Assert(len(m1), 4)
t.Assert(m1[1], 1)
t.Assert(m1[3], 2)
})
}
func TestNewIntArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5, 3}
array1 := garray.NewIntArrayFromCopy(a1)
gtest.Assert(array1.Len(), 5)
gtest.Assert(array1, a1)
t.Assert(array1.Len(), 5)
t.Assert(array1, a1)
})
}
func TestIntArray_Remove(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 5, 4}
array1 := garray.NewIntArrayFrom(a1)
n1 := array1.Remove(1)
gtest.Assert(n1, 2)
gtest.Assert(array1.Len(), 4)
v, ok := array1.Remove(1)
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
n1 = array1.Remove(0)
gtest.Assert(n1, 1)
gtest.Assert(array1.Len(), 3)
v, ok = array1.Remove(0)
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
n1 = array1.Remove(2)
gtest.Assert(n1, 4)
gtest.Assert(array1.Len(), 2)
v, ok = array1.Remove(2)
t.Assert(v, 4)
t.Assert(ok, true)
t.Assert(array1.Len(), 2)
})
}
func TestIntArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}
a1 := garray.NewIntArrayFrom(s1, true)
@ -396,26 +554,26 @@ func TestIntArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains(6), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains(6), true)
})
}
func TestIntArray_SortFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
a1 := garray.NewIntArrayFrom(s1)
func1 := func(v1, v2 int) bool {
return v1 < v2
}
a11 := a1.SortFunc(func1)
gtest.Assert(a11, []int{1, 2, 3, 4})
t.Assert(a11, []int{1, 2, 3, 4})
})
}
func TestIntArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}
a1 := garray.NewIntArrayFrom(s1, true)
@ -441,31 +599,50 @@ func TestIntArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
gtest.Assert(a1.Contains(6), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains(6), true)
})
}
func TestIntArray_Json(t *testing.T) {
gtest.Case(t, func() {
// array pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewIntArray()
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s1)
t.Assert(a2.Slice(), s1)
var a3 garray.IntArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
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)
gtest.Case(t, func() {
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
Scores *garray.IntArray
@ -475,59 +652,140 @@ func TestIntArray_Json(t *testing.T) {
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.Assert(user.Scores, data["Scores"])
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)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
}
func TestIntArray_Iterator(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewIntArrayFrom(slice)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestIntArray_RemoveValue(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewIntArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue(99), false)
t.Assert(array.RemoveValue(20), true)
t.Assert(array.RemoveValue(10), true)
t.Assert(array.RemoveValue(20), false)
t.Assert(array.RemoveValue(88), false)
t.Assert(array.Len(), 2)
})
}
func TestIntArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.IntArray
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[1,2,3]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{1, 2, 3},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestIntArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3, 4})
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

@ -21,36 +21,61 @@ import (
)
func Test_StrArray_Basic(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStrArrayFrom(expect)
array2 := garray.NewStrArrayFrom(expect, true)
array3 := garray.NewStrArrayFrom([]string{})
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, "100")
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search("100"), 0)
gtest.Assert(array.Contains("100"), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains("100"), false)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
t.Assert(array.Search("100"), 0)
t.Assert(array.Contains("100"), true)
v, ok = array.Remove(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, "")
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.Contains("100"), false)
array.Append("4")
gtest.Assert(array.Len(), 4)
t.Assert(array.Len(), 4)
array.InsertBefore(0, "100")
array.InsertAfter(0, "200")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
t.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
array.InsertBefore(5, "300")
array.InsertAfter(6, "400")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
gtest.Assert(array.Clear().Len(), 0)
gtest.Assert(array2.Slice(), expect)
gtest.Assert(array3.Search("100"), -1)
t.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
t.Assert(array.Clear().Len(), 0)
t.Assert(array2.Slice(), expect)
t.Assert(array3.Search("100"), -1)
})
}
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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect1 := []string{"0", "1", "2", "3"}
expect2 := []string{"3", "2", "1", "0"}
array := garray.NewStrArray()
@ -58,73 +83,167 @@ func TestStrArray_Sort(t *testing.T) {
array.Append(gconv.String(i))
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
t.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
t.Assert(array.Slice(), expect2)
})
}
func TestStrArray_Unique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []string{"1", "1", "2", "3"}
array := garray.NewStrArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})
}
func TestStrArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStrArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), "0")
gtest.Assert(array.PopRight(), "3")
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.Assert(array.Len(), 0)
t.Assert(array.Slice(), expect)
v, ok := array.PopLeft()
t.Assert(v, "0")
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, "3")
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []string{"1", "2"})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []string{"1", "2"})
t.Assert(ok, true)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.Len(), 0)
array.PushLeft("1").PushRight("2")
gtest.Assert(array.Slice(), []string{"1", "2"})
t.Assert(array.Slice(), []string{"1", "2"})
})
}
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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArray()
v, ok := array.PopLeft()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
gtest.C(t, func(t *gtest.T) {
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
value2 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(value1)
array2 := garray.NewStrArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
gtest.Assert(array1.PopRights(2), []interface{}{"5", "6"})
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
gtest.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
gtest.Assert(array2.Slice(), []interface{}{})
t.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
t.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
t.Assert(array1.PopRights(2), []interface{}{"5", "6"})
t.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
t.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
t.Assert(array1.Slice(), []interface{}{})
t.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
t.Assert(array2.Slice(), []interface{}{})
})
}
func TestString_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(value1)
array2 := garray.NewStrArrayFrom(value1, true)
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
gtest.Assert(array1.Range(-1, 10), value1)
gtest.Assert(array1.Range(10, 1), nil)
gtest.Assert(array2.Range(0, 1), []interface{}{"0"})
t.Assert(array1.Range(0, 1), []interface{}{"0"})
t.Assert(array1.Range(1, 2), []interface{}{"1"})
t.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 1), nil)
t.Assert(array2.Range(0, 1), []interface{}{"0"})
})
}
func TestStrArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a11 := []string{"0", "1", "2", "3"}
a21 := []string{"4", "5", "6", "7"}
array1 := garray.NewStrArrayFrom(a11)
array2 := garray.NewStrArrayFrom(a21)
gtest.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
t.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
@ -143,239 +262,273 @@ func TestStrArray_Merge(t *testing.T) {
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewStrArrayFrom(s1)
gtest.Assert(a1.Merge(s2).Len(), 6)
gtest.Assert(a1.Merge(i1).Len(), 9)
gtest.Assert(a1.Merge(i2).Len(), 10)
gtest.Assert(a1.Merge(s3).Len(), 12)
gtest.Assert(a1.Merge(s4).Len(), 14)
gtest.Assert(a1.Merge(s5).Len(), 16)
gtest.Assert(a1.Merge(s6).Len(), 19)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i1).Len(), 9)
t.Assert(a1.Merge(i2).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestStrArray_Fill(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0"}
a2 := []string{"0"}
array1 := garray.NewStrArrayFrom(a1)
array2 := garray.NewStrArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0", "100", "100"})
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100", "100"})
s1 := array2.Fill(-1, 2, "100")
gtest.Assert(s1.Len(), 2)
t.Assert(array1.Fill(1, 2, "100"), nil)
t.Assert(array1.Slice(), []string{"0", "100", "100"})
t.Assert(array2.Fill(0, 2, "100"), nil)
t.Assert(array2.Slice(), []string{"100", "100"})
t.AssertNE(array2.Fill(-1, 2, "100"), nil)
t.Assert(array2.Len(), 2)
})
}
func TestStrArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5"}
array1 := garray.NewStrArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []string{"1", "2"})
gtest.Assert(chunks[1], []string{"3", "4"})
gtest.Assert(chunks[2], []string{"5"})
gtest.Assert(len(array1.Chunk(0)), 0)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []string{"1", "2"})
t.Assert(chunks[1], []string{"3", "4"})
t.Assert(chunks[2], []string{"5"})
t.Assert(len(array1.Chunk(0)), 0)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5"}
array1 := garray.NewStrArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []string{"1", "2", "3"})
t.Assert(chunks[1], []string{"4", "5"})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []string{"1", "2"})
t.Assert(chunks[1], []string{"3", "4"})
t.Assert(chunks[2], []string{"5", "6"})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []string{"1", "2", "3"})
t.Assert(chunks[1], []string{"4", "5", "6"})
t.Assert(array1.Chunk(0), nil)
})
}
func TestStrArray_Pad(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0", "1", "1"})
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1", "0", "1", "1"})
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1", "0", "1", "1"})
t.Assert(array1.Pad(3, "1").Slice(), []string{"0", "1", "1"})
t.Assert(array1.Pad(-4, "1").Slice(), []string{"1", "0", "1", "1"})
t.Assert(array1.Pad(3, "1").Slice(), []string{"1", "0", "1", "1"})
})
}
func TestStrArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
array2 := garray.NewStrArrayFrom(a1, true)
gtest.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
gtest.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
gtest.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
gtest.Assert(array1.SubSlice(8, 2), nil)
gtest.Assert(array1.SubSlice(1, -2), nil)
gtest.Assert(array1.SubSlice(-5, 2), []string{"2", "3"})
gtest.Assert(array1.SubSlice(-10, 1), nil)
gtest.Assert(array2.SubSlice(0, 2), []string{"0", "1"})
t.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
t.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
t.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
t.Assert(array1.SubSlice(8, 2), nil)
t.Assert(array1.SubSlice(1, -2), nil)
t.Assert(array1.SubSlice(-5, 2), []string{"2", "3"})
t.Assert(array1.SubSlice(-10, 1), nil)
t.Assert(array2.SubSlice(0, 2), []string{"0", "1"})
})
}
func TestStrArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(len(array1.Rands(2)), "2")
gtest.Assert(len(array1.Rands(10)), "7")
gtest.AssertIN(array1.Rands(1)[0], a1)
gtest.Assert(len(array1.Rand()), 1)
gtest.AssertIN(array1.Rand(), a1)
t.Assert(len(array1.Rands(2)), "2")
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
v, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(v, a1)
})
}
func TestStrArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "b", "c", "d", "e", "f", "g"}
array1 := garray.NewStrArrayFrom(a1)
gtest.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
gtest.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
gtest.AssertNI(array1.PopRands(1), array1.Slice())
gtest.AssertNI(array1.PopRands(1), array1.Slice())
gtest.Assert(len(array1.PopRands(10)), 3)
t.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
t.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
t.AssertNI(array1.PopRands(1), array1.Slice())
t.AssertNI(array1.PopRands(1), array1.Slice())
t.Assert(len(array1.PopRands(10)), 3)
})
}
func TestStrArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
t.Assert(array1.Shuffle().Len(), 7)
})
}
func TestStrArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
t.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
})
}
func TestStrArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", `"a"`, `\a`}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Join("."), `0.1."a".\a`)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
}
func TestStrArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
t.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
})
}
func TestNewStrArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := garray.NewStrArrayFromCopy(a1)
a3 := garray.NewStrArrayFromCopy(a1, true)
gtest.Assert(a2.Contains("1"), true)
gtest.Assert(a2.Len(), 7)
gtest.Assert(a2, a3)
t.Assert(a2.Contains("1"), true)
t.Assert(a2.Len(), 7)
t.Assert(a2, a3)
})
}
func TestStrArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"a", "b", "c", "d"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Contains("2"), true)
gtest.Assert(array1.Len(), 7)
t.Assert(array1.Contains("2"), true)
t.Assert(array1.Len(), 7)
array1 = array1.SetArray(a2)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1.Len(), 4)
t.Assert(array1.Contains("2"), false)
t.Assert(array1.Contains("c"), true)
t.Assert(array1.Len(), 4)
})
}
func TestStrArray_Replace(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"a", "b", "c", "d"}
a3 := []string{"o", "p", "q", "x", "y", "z", "w", "r", "v"}
array1 := garray.NewStrArrayFrom(a1)
gtest.Assert(array1.Contains("2"), true)
gtest.Assert(array1.Len(), 7)
t.Assert(array1.Contains("2"), true)
t.Assert(array1.Len(), 7)
array1 = array1.Replace(a2)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1.Contains("5"), true)
gtest.Assert(array1.Len(), 7)
t.Assert(array1.Contains("2"), false)
t.Assert(array1.Contains("c"), true)
t.Assert(array1.Contains("5"), true)
t.Assert(array1.Len(), 7)
array1 = array1.Replace(a3)
gtest.Assert(array1.Contains("2"), false)
gtest.Assert(array1.Contains("c"), false)
gtest.Assert(array1.Contains("5"), false)
gtest.Assert(array1.Contains("p"), true)
gtest.Assert(array1.Contains("r"), false)
gtest.Assert(array1.Len(), 7)
t.Assert(array1.Contains("2"), false)
t.Assert(array1.Contains("c"), false)
t.Assert(array1.Contains("5"), false)
t.Assert(array1.Contains("p"), true)
t.Assert(array1.Contains("r"), false)
t.Assert(array1.Len(), 7)
})
}
func TestStrArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
a2 := []string{"0", "a", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
array2 := garray.NewStrArrayFrom(a2)
gtest.Assert(array1.Sum(), 21)
gtest.Assert(array2.Sum(), 18)
t.Assert(array1.Sum(), 21)
t.Assert(array2.Sum(), 18)
})
}
func TestStrArray_PopRand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
str1 := array1.PopRand()
gtest.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
gtest.Assert(array1.Len(), 6)
str1, ok := array1.PopRand()
t.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
t.Assert(array1.Len(), 6)
t.Assert(ok, true)
})
}
func TestStrArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array2, array1)
gtest.Assert(array2.Len(), 7)
t.Assert(array2, array1)
t.Assert(array2.Len(), 7)
})
}
func TestStrArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"0", "1", "2", "3", "4", "4", "6"}
array1 := garray.NewStrArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 6)
gtest.Assert(m1["2"], 1)
gtest.Assert(m1["4"], 2)
t.Assert(len(m1), 6)
t.Assert(m1["2"], 1)
t.Assert(m1["4"], 2)
})
}
func TestStrArray_Remove(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewStrArrayFrom(a1)
s1 := array1.Remove(1)
gtest.Assert(s1, "a")
gtest.Assert(array1.Len(), 4)
s1 = array1.Remove(3)
gtest.Assert(s1, "c")
gtest.Assert(array1.Len(), 3)
s1, ok := array1.Remove(1)
t.Assert(s1, "a")
t.Assert(ok, true)
t.Assert(array1.Len(), 4)
s1, ok = array1.Remove(3)
t.Assert(s1, "c")
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
})
}
func TestStrArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewStrArrayFrom(s1, true)
@ -401,25 +554,25 @@ func TestStrArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestStrArray_SortFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "d", "c", "b"}
a1 := garray.NewStrArrayFrom(s1)
func1 := func(v1, v2 string) bool {
return v1 < v2
}
a11 := a1.SortFunc(func1)
gtest.Assert(a11, []string{"a", "b", "c", "d"})
t.Assert(a11, []string{"a", "b", "c", "d"})
})
}
func TestStrArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewStrArrayFrom(s1, true)
@ -445,31 +598,50 @@ func TestStrArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestStrArray_Json(t *testing.T) {
gtest.Case(t, func() {
// array pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewStrArray()
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s1)
t.Assert(a2.Slice(), s1)
var a3 garray.StrArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
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)
gtest.Case(t, func() {
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
Scores *garray.StrArray
@ -479,59 +651,139 @@ func TestStrArray_Json(t *testing.T) {
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.Assert(user.Scores, data["Scores"])
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)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
}
func TestStrArray_Iterator(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewStrArrayFrom(slice)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestStrArray_RemoveValue(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewStrArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestStrArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.StrArray
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`["1","2","3"]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.SliceStr{"1", "2", "3"},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
}
func TestStrArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
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

@ -22,7 +22,7 @@ import (
)
func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"h", "j", "i", "k"}
func1 := func(v1, v2 interface{}) int {
@ -34,16 +34,16 @@ func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a2, func2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []interface{}{"k", "i", "j", "h"})
t.Assert(array2.Len(), 4)
t.Assert(array2, []interface{}{"k", "i", "j", "h"})
})
}
func TestNewSortedArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
@ -54,15 +54,15 @@ func TestNewSortedArrayFromCopy(t *testing.T) {
}
array1 := garray.NewSortedArrayFromCopy(a1, func1)
array2 := garray.NewSortedArrayFromCopy(a1, func2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(array2, []interface{}{"c", "f", "a"})
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1.Len(), 3)
t.Assert(array2, []interface{}{"c", "f", "a"})
})
}
func TestSortedArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
a2 := []interface{}{"e", "h", "g", "k"}
@ -72,168 +72,234 @@ func TestSortedArray_SetArray(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1, []interface{}{"e", "g", "h", "k"})
t.Assert(array1.Len(), 4)
t.Assert(array1, []interface{}{"e", "g", "h", "k"})
})
}
func TestSortedArray_Sort(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.Sort()
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "f"})
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "f"})
})
}
func TestSortedArray_Get(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Get(2), "f")
gtest.Assert(array1.Get(1), "c")
v, ok := array1.Get(2)
t.Assert(v, "f")
t.Assert(ok, true)
v, ok = array1.Get(1)
t.Assert(v, "c")
t.Assert(ok, true)
})
}
func TestSortedArray_Remove(t *testing.T) {
gtest.Case(t, func() {
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)
i1 := array1.Remove(1)
gtest.Assert(gconv.String(i1), "b")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("b"), false)
i1, ok := array1.Remove(1)
t.Assert(ok, true)
t.Assert(gconv.String(i1), "b")
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("b"), false)
i2 := array1.Remove(0)
gtest.Assert(gconv.String(i2), "a")
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1.Contains("a"), false)
v, ok := array1.Remove(-1)
t.Assert(v, nil)
t.Assert(ok, false)
i3 := array1.Remove(1)
gtest.Assert(gconv.String(i3), "d")
gtest.Assert(array1.Len(), 1)
gtest.Assert(array1.Contains("d"), false)
v, ok = array1.Remove(100000)
t.Assert(v, nil)
t.Assert(ok, false)
i2, ok := array1.Remove(0)
t.Assert(ok, true)
t.Assert(gconv.String(i2), "a")
t.Assert(array1.Len(), 2)
t.Assert(array1.Contains("a"), false)
i3, ok := array1.Remove(1)
t.Assert(ok, true)
t.Assert(gconv.String(i3), "d")
t.Assert(array1.Len(), 1)
t.Assert(array1.Contains("d"), false)
})
}
func TestSortedArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
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)
i1 := array1.PopLeft()
gtest.Assert(gconv.String(i1), "a")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"b", "c", "d"})
gtest.C(t, func(t *gtest.T) {
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.Case(t, func() {
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)
i1 := array1.PopRight()
gtest.Assert(gconv.String(i1), "d")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "b", "c"})
gtest.C(t, func(t *gtest.T) {
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) {
gtest.Case(t, func() {
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)
i1 := array1.PopRand()
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 3)
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
t.Assert(array1.Len(), 3)
})
}
func TestSortedArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
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)
i1 := array1.PopRands(2)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 2)
t.Assert(len(i1), 2)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
t.Assert(array1.Len(), 2)
i2 := array1.PopRands(3)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
gtest.Assert(array1.Len(), 0)
t.Assert(len(i1), 2)
t.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArray(gutil.ComparatorInt)
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
func TestSortedArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopLefts(2)
gtest.Assert(len(i1), 2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
gtest.Assert(array1.Len(), 4)
t.Assert(len(i1), 2)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopLefts(5)
gtest.Assert(len(i2), 4)
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
gtest.Assert(array1.Len(), 0)
t.Assert(len(i2), 4)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.PopRights(2)
gtest.Assert(len(i1), 2)
gtest.Assert(i1, []interface{}{"e", "f"})
gtest.Assert(array1.Len(), 4)
t.Assert(len(i1), 2)
t.Assert(i1, []interface{}{"e", "f"})
t.Assert(array1.Len(), 4)
i2 := array1.PopRights(10)
gtest.Assert(len(i2), 4)
t.Assert(len(i2), 4)
t.Assert(array1.Len(), 0)
})
}
func TestSortedArray_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
@ -241,25 +307,25 @@ func TestSortedArray_Range(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a1, func1, true)
i1 := array1.Range(2, 5)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
gtest.Assert(array1.Len(), 6)
t.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(array1.Len(), 6)
i2 := array1.Range(7, 5)
gtest.Assert(len(i2), 0)
t.Assert(len(i2), 0)
i2 = array1.Range(-1, 2)
gtest.Assert(i2, []interface{}{"a", "b"})
t.Assert(i2, []interface{}{"a", "b"})
i2 = array1.Range(4, 10)
gtest.Assert(len(i2), 2)
gtest.Assert(i2, []interface{}{"e", "f"})
t.Assert(len(i2), 2)
t.Assert(i2, []interface{}{"e", "f"})
gtest.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
t.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
})
}
func TestSortedArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
a2 := []interface{}{"1", "2", "3", "b", "e", "f"}
a3 := []interface{}{"4", "5", "6"}
@ -269,15 +335,15 @@ func TestSortedArray_Sum(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a2, func1)
array3 := garray.NewSortedArrayFrom(a3, func1)
gtest.Assert(array1.Sum(), 0)
gtest.Assert(array2.Sum(), 6)
gtest.Assert(array3.Sum(), 15)
t.Assert(array1.Sum(), 0)
t.Assert(array2.Sum(), 6)
t.Assert(array3.Sum(), 15)
})
}
func TestSortedArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
@ -285,30 +351,30 @@ func TestSortedArray_Clone(t *testing.T) {
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
t.Assert(array1, array2)
array1.Remove(1)
gtest.AssertNE(array1, array2)
t.AssertNE(array1, array2)
})
}
func TestSortedArray_Clear(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Len(), 6)
t.Assert(array1.Len(), 6)
array1.Clear()
gtest.Assert(array1.Len(), 0)
t.Assert(array1.Len(), 0)
})
}
func TestSortedArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
@ -316,17 +382,45 @@ func TestSortedArray_Chunk(t *testing.T) {
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Chunk(2)
gtest.Assert(len(i1), 3)
gtest.Assert(i1[0], []interface{}{"a", "b"})
gtest.Assert(i1[2], []interface{}{"e"})
t.Assert(len(i1), 3)
t.Assert(i1[0], []interface{}{"a", "b"})
t.Assert(i1[2], []interface{}{"e"})
i1 = array1.Chunk(0)
gtest.Assert(len(i1), 0)
t.Assert(len(i1), 0)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []interface{}{1, 2})
t.Assert(chunks[1], []interface{}{3, 4})
t.Assert(chunks[2], []interface{}{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []interface{}{1, 2, 3})
t.Assert(chunks[1], []interface{}{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestSortedArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b", "e"}
func1 := func(v1, v2 interface{}) int {
@ -335,42 +429,43 @@ func TestSortedArray_SubSlice(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, func1)
array2 := garray.NewSortedArrayFrom(a1, func1, true)
i1 := array1.SubSlice(2, 3)
gtest.Assert(len(i1), 3)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(len(i1), 3)
t.Assert(i1, []interface{}{"c", "d", "e"})
i1 = array1.SubSlice(2, 6)
gtest.Assert(len(i1), 3)
gtest.Assert(i1, []interface{}{"c", "d", "e"})
t.Assert(len(i1), 3)
t.Assert(i1, []interface{}{"c", "d", "e"})
i1 = array1.SubSlice(7, 2)
gtest.Assert(len(i1), 0)
t.Assert(len(i1), 0)
s1 := array1.SubSlice(1, -2)
gtest.Assert(s1, nil)
t.Assert(s1, nil)
s1 = array1.SubSlice(-9, 2)
gtest.Assert(s1, nil)
gtest.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
t.Assert(s1, nil)
t.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
})
}
func TestSortedArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rand()
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
gtest.Assert(array1.Len(), 3)
i1, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.Assert(array1.Len(), 3)
})
}
func TestSortedArray_Rands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
@ -378,43 +473,43 @@ func TestSortedArray_Rands(t *testing.T) {
}
array1 := garray.NewSortedArrayFrom(a1, func1)
i1 := array1.Rands(2)
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
gtest.Assert(len(i1), 2)
gtest.Assert(array1.Len(), 3)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.Assert(len(i1), 2)
t.Assert(array1.Len(), 3)
i1 = array1.Rands(4)
gtest.Assert(len(i1), 3)
t.Assert(len(i1), 4)
})
}
func TestSortedArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
gtest.Assert(array1.Join(","), `a,c,d`)
gtest.Assert(array1.Join("."), `a.c.d`)
t.Assert(array1.Join(","), `a,c,d`)
t.Assert(array1.Join("."), `a.c.d`)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, `"a"`, `\a`}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
gtest.Assert(array1.Join("."), `"a".0.1.\a`)
t.Assert(array1.Join("."), `"a".0.1.\a`)
})
}
func TestSortedArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{0, 1, "a", "b"}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
gtest.Assert(array1.String(), `[0,1,"a","b"]`)
t.Assert(array1.String(), `[0,1,"a","b"]`)
})
}
func TestSortedArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
@ -422,15 +517,15 @@ func TestSortedArray_CountValues(t *testing.T) {
}
array1 := garray.NewSortedArrayFrom(a1, func1)
m1 := array1.CountValues()
gtest.Assert(len(m1), 3)
gtest.Assert(m1["c"], 2)
gtest.Assert(m1["a"], 1)
t.Assert(len(m1), 3)
t.Assert(m1["c"], 2)
t.Assert(m1["a"], 1)
})
}
func TestSortedArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
@ -438,13 +533,13 @@ func TestSortedArray_SetUnique(t *testing.T) {
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1.SetUnique(true)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1, []interface{}{"a", "c", "d"})
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "d"})
})
}
func TestSortedArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
@ -473,13 +568,13 @@ func TestSortedArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
@ -508,13 +603,13 @@ func TestSortedArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候不会被阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候不会被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
@ -533,38 +628,59 @@ func TestSortedArray_Merge(t *testing.T) {
a1 := garray.NewSortedArrayFrom(s1, func1)
gtest.Assert(a1.Merge(s2).Len(), 6)
gtest.Assert(a1.Merge(i1).Len(), 9)
gtest.Assert(a1.Merge(i2).Len(), 10)
gtest.Assert(a1.Merge(s3).Len(), 12)
gtest.Assert(a1.Merge(s4).Len(), 14)
gtest.Assert(a1.Merge(s5).Len(), 16)
gtest.Assert(a1.Merge(s6).Len(), 19)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i1).Len(), 9)
t.Assert(a1.Merge(i2).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestSortedArray_Json(t *testing.T) {
gtest.Case(t, func() {
// array pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedArray(gutil.ComparatorString)
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
gtest.Assert(a3.Interfaces(), s1)
t.Assert(err, nil)
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)
gtest.Case(t, func() {
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
Scores *garray.SortedArray
@ -574,63 +690,185 @@ func TestSortedArray_Json(t *testing.T) {
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.AssertNE(user.Scores, nil)
gtest.Assert(user.Scores.Len(), 3)
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
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)
})
// 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)
})
}
func TestSortedArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestSortedArray_RemoveValue(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestSortedArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.SortedArray
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[2,3,1]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{2, 3, 1},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestSortedArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
t.Assert(array.FilterNil().Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestSortedArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
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

@ -21,323 +21,431 @@ import (
)
func TestNewSortedIntArrayFrom(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 3, 2, 1, 4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1, true)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
gtest.Assert(array1.Slice(), a1)
gtest.Assert(array1.Interfaces(), a1)
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
t.Assert(array1.Slice(), a1)
t.Assert(array1.Interfaces(), a1)
})
}
func TestNewSortedIntArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 5, 2, 1, 4, 3, 6}
array1 := garray.NewSortedIntArrayFromCopy(a1, false)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}
func TestSortedIntArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3}
a2 := []int{4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.SetArray(a2)
gtest.Assert(array2.Len(), 3)
gtest.Assert(array2.Search(3), -1)
gtest.Assert(array2.Search(5), 1)
gtest.Assert(array2.Search(6), 2)
t.Assert(array2.Len(), 3)
t.Assert(array2.Search(3), -1)
t.Assert(array2.Search(5), 1)
t.Assert(array2.Search(6), 2)
})
}
func TestSortedIntArray_Sort(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 3, 2, 1}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.Sort()
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []int{0, 1, 2, 3})
t.Assert(array2.Len(), 4)
t.Assert(array2, []int{0, 1, 2, 3})
})
}
func TestSortedIntArray_Get(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
gtest.Assert(array1.Get(0), 0)
gtest.Assert(array1.Get(1), 1)
gtest.Assert(array1.Get(3), 5)
v, ok := array1.Get(0)
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array1.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
v, ok = array1.Get(3)
t.Assert(v, 5)
t.Assert(ok, true)
})
}
func TestSortedIntArray_Remove(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 0}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.Remove(2)
gtest.Assert(i1, 3)
gtest.Assert(array1.Search(5), 2)
// 再次删除剩下的数组中的第一个
i2 := array1.Remove(0)
gtest.Assert(i2, 0)
gtest.Assert(array1.Search(5), 1)
v, ok := array1.Remove(-1)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array1.Remove(-100000)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array1.Remove(2)
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array1.Search(5), 2)
v, ok = array1.Remove(0)
t.Assert(v, 0)
t.Assert(ok, true)
t.Assert(array1.Search(5), 1)
a2 := []int{1, 3, 4}
array2 := garray.NewSortedIntArrayFrom(a2)
i3 := array2.Remove(1)
gtest.Assert(array2.Search(1), 0)
gtest.Assert(i3, 3)
i3 = array2.Remove(1)
gtest.Assert(array2.Search(4), -1)
gtest.Assert(i3, 4)
v, ok = array2.Remove(1)
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array2.Search(1), 0)
v, ok = array2.Remove(1)
t.Assert(v, 4)
t.Assert(ok, true)
t.Assert(array2.Search(4), -1)
})
}
func TestSortedIntArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopLeft()
gtest.Assert(i1, 1)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(1), -1)
v, ok := array1.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
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) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRight()
gtest.Assert(i1, 5)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(5), -1)
v, ok := array1.PopRight()
t.Assert(v, 5)
t.Assert(ok, true)
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) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
i1 := array1.PopRand()
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Search(i1), -1)
gtest.AssertIN(i1, []int{1, 3, 5, 2})
i1, ok := array1.PopRand()
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(i1), -1)
t.AssertIN(i1, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopRands(2)
gtest.Assert(array1.Len(), 2)
gtest.AssertIN(ns1, []int{1, 3, 5, 2})
t.Assert(array1.Len(), 2)
t.AssertIN(ns1, []int{1, 3, 5, 2})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopRands(5)
gtest.Assert(array2.Len(), 0)
gtest.Assert(len(ns2), 4)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
t.Assert(array2.Len(), 0)
t.Assert(len(ns2), 4)
t.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArray()
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
func TestSortedIntArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopLefts(2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(ns1, []int{1, 2})
t.Assert(array1.Len(), 2)
t.Assert(ns1, []int{1, 2})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopLefts(5)
gtest.Assert(array2.Len(), 0)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
t.Assert(array2.Len(), 0)
t.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.PopRights(2)
gtest.Assert(array1.Len(), 2)
gtest.Assert(ns1, []int{3, 5})
t.Assert(array1.Len(), 2)
t.Assert(ns1, []int{3, 5})
a2 := []int{1, 3, 5, 2}
array2 := garray.NewSortedIntArrayFrom(a2)
ns2 := array2.PopRights(5)
gtest.Assert(array2.Len(), 0)
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
t.Assert(array2.Len(), 0)
t.AssertIN(ns2, []int{1, 3, 5, 2})
})
}
func TestSortedIntArray_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5, 2, 6, 7}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := garray.NewSortedIntArrayFrom(a1, true)
ns1 := array1.Range(1, 4)
gtest.Assert(len(ns1), 3)
gtest.Assert(ns1, []int{2, 3, 5})
t.Assert(len(ns1), 3)
t.Assert(ns1, []int{2, 3, 5})
ns2 := array1.Range(5, 4)
gtest.Assert(len(ns2), 0)
t.Assert(len(ns2), 0)
ns3 := array1.Range(-1, 4)
gtest.Assert(len(ns3), 4)
t.Assert(len(ns3), 4)
nsl := array1.Range(5, 8)
gtest.Assert(len(nsl), 1)
gtest.Assert(array2.Range(1, 2), []int{2})
t.Assert(len(nsl), 1)
t.Assert(array2.Range(1, 2), []int{2})
})
}
func TestSortedIntArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
n1 := array1.Sum()
gtest.Assert(n1, 9)
t.Assert(n1, 9)
})
}
func TestSortedIntArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
gtest.Assert(array1.Join("."), `1.3.5`)
t.Assert(array1.Join("."), `1.3.5`)
})
}
func TestSortedIntArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
gtest.Assert(array1.String(), `[1,3,5]`)
t.Assert(array1.String(), `[1,3,5]`)
})
}
func TestSortedIntArray_Contains(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
gtest.Assert(array1.Contains(4), false)
t.Assert(array1.Contains(4), false)
})
}
func TestSortedIntArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array2.Len(), 3)
gtest.Assert(array2, array1)
t.Assert(array2.Len(), 3)
t.Assert(array2, array1)
})
}
func TestSortedIntArray_Clear(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
t.Assert(array1.Len(), 0)
})
}
func TestSortedIntArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
ns2 := array1.Chunk(-1)
gtest.Assert(len(ns1), 3)
gtest.Assert(ns1[0], []int{1, 2})
gtest.Assert(ns1[2], []int{5})
gtest.Assert(len(ns2), 0)
t.Assert(len(ns1), 3)
t.Assert(ns1[0], []int{1, 2})
t.Assert(ns1[2], []int{5})
t.Assert(len(ns2), 0)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int{1, 2, 3})
t.Assert(chunks[1], []int{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []int{1, 2})
t.Assert(chunks[1], []int{3, 4})
t.Assert(chunks[2], []int{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 6}
array1 := garray.NewSortedIntArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []int{1, 2, 3})
t.Assert(chunks[1], []int{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestSortedIntArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array2 := garray.NewSortedIntArrayFrom(a1, true)
ns1 := array1.SubSlice(1, 2)
gtest.Assert(len(ns1), 2)
gtest.Assert(ns1, []int{2, 3})
t.Assert(len(ns1), 2)
t.Assert(ns1, []int{2, 3})
ns2 := array1.SubSlice(7, 2)
gtest.Assert(len(ns2), 0)
t.Assert(len(ns2), 0)
ns3 := array1.SubSlice(3, 5)
gtest.Assert(len(ns3), 2)
gtest.Assert(ns3, []int{4, 5})
t.Assert(len(ns3), 2)
t.Assert(ns3, []int{4, 5})
ns4 := array1.SubSlice(3, 1)
gtest.Assert(len(ns4), 1)
gtest.Assert(ns4, []int{4})
gtest.Assert(array1.SubSlice(-1, 1), []int{5})
gtest.Assert(array1.SubSlice(-9, 1), nil)
gtest.Assert(array1.SubSlice(1, -9), nil)
gtest.Assert(array2.SubSlice(1, 2), []int{2, 3})
t.Assert(len(ns4), 1)
t.Assert(ns4, []int{4})
t.Assert(array1.SubSlice(-1, 1), []int{5})
t.Assert(array1.SubSlice(-9, 1), nil)
t.Assert(array1.SubSlice(1, -9), nil)
t.Assert(array2.SubSlice(1, 2), []int{2, 3})
})
}
func TestSortedIntArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rand() //按每几个元素切成一个数组
gtest.AssertIN(ns1, a1)
ns1, ok := array1.Rand()
t.AssertIN(ns1, a1)
t.Assert(ok, true)
})
}
func TestSortedIntArray_Rands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.Rands(2) //按每几个元素切成一个数组
gtest.AssertIN(ns1, a1)
gtest.Assert(len(ns1), 2)
ns1 := array1.Rands(2)
t.AssertIN(ns1, a1)
t.Assert(len(ns1), 2)
ns2 := array1.Rands(6) //按每几个元素切成一个数组
gtest.AssertIN(ns2, a1)
gtest.Assert(len(ns2), 5)
ns2 := array1.Rands(6)
t.Assert(len(ns2), 6)
})
}
func TestSortedIntArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3}
array1 := garray.NewSortedIntArrayFrom(a1)
ns1 := array1.CountValues() //按每几个元素切成一个数组
gtest.Assert(len(ns1), 5)
gtest.Assert(ns1[2], 1)
gtest.Assert(ns1[3], 2)
t.Assert(len(ns1), 5)
t.Assert(ns1[2], 1)
t.Assert(ns1[3], 2)
})
}
func TestSortedIntArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.SetUnique(true)
gtest.Assert(array1.Len(), 5)
gtest.Assert(array1, []int{1, 2, 3, 4, 5})
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
})
}
func TestSortedIntArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}
a1 := garray.NewSortedIntArrayFrom(s1, true)
ch1 := make(chan int64, 3)
@ -362,13 +470,13 @@ func TestSortedIntArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains(6), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains(6), true)
})
}
func TestSortedIntArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}
a1 := garray.NewSortedIntArrayFrom(s1, true)
@ -394,13 +502,13 @@ func TestSortedIntArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
gtest.Assert(a1.Contains(6), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains(6), true)
})
}
func TestSortedIntArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
@ -417,37 +525,57 @@ func TestSortedIntArray_Merge(t *testing.T) {
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewSortedIntArrayFrom(i0)
gtest.Assert(a1.Merge(s2).Len(), 6)
gtest.Assert(a1.Merge(i1).Len(), 9)
gtest.Assert(a1.Merge(i2).Len(), 10)
gtest.Assert(a1.Merge(s3).Len(), 12)
gtest.Assert(a1.Merge(s4).Len(), 14)
gtest.Assert(a1.Merge(s5).Len(), 16)
gtest.Assert(a1.Merge(s6).Len(), 19)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i1).Len(), 9)
t.Assert(a1.Merge(i2).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestSortedIntArray_Json(t *testing.T) {
gtest.Case(t, func() {
// array pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedIntArray()
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedIntArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
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)
gtest.Case(t, func() {
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
Scores *garray.SortedIntArray
@ -457,59 +585,140 @@ func TestSortedIntArray_Json(t *testing.T) {
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.Assert(user.Scores, []int{98, 99, 100})
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)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []int{98, 99, 100})
})
}
func TestSortedIntArray_Iterator(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewSortedIntArrayFrom(slice)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestSortedIntArray_RemoveValue(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewSortedIntArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue(99), false)
t.Assert(array.RemoveValue(20), true)
t.Assert(array.RemoveValue(10), true)
t.Assert(array.RemoveValue(20), false)
t.Assert(array.RemoveValue(88), false)
t.Assert(array.Len(), 2)
})
}
func TestSortedIntArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.SortedIntArray
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[2,3,1]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{2, 3, 1},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestSortedIntArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})
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

@ -20,330 +20,450 @@ import (
)
func TestNewSortedStrArrayFrom(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
s1 := garray.NewSortedStrArrayFrom(a1, true)
gtest.Assert(s1, []string{"a", "b", "c", "d"})
t.Assert(s1, []string{"a", "b", "c", "d"})
s2 := garray.NewSortedStrArrayFrom(a1, false)
gtest.Assert(s2, []string{"a", "b", "c", "d"})
t.Assert(s2, []string{"a", "b", "c", "d"})
})
}
func TestNewSortedStrArrayFromCopy(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
s1 := garray.NewSortedStrArrayFromCopy(a1, true)
gtest.Assert(s1.Len(), 4)
gtest.Assert(s1, []string{"a", "b", "c", "d"})
t.Assert(s1.Len(), 4)
t.Assert(s1, []string{"a", "b", "c", "d"})
})
}
func TestSortedStrArray_SetArray(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
a2 := []string{"f", "g", "h"}
array1 := garray.NewSortedStrArrayFrom(a1)
array1.SetArray(a2)
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("d"), false)
gtest.Assert(array1.Contains("b"), false)
gtest.Assert(array1.Contains("g"), true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("d"), false)
t.Assert(array1.Contains("b"), false)
t.Assert(array1.Contains("g"), true)
})
}
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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1, []string{"a", "b", "c", "d"})
t.Assert(array1, []string{"a", "b", "c", "d"})
array1.Sort()
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("c"), true)
gtest.Assert(array1, []string{"a", "b", "c", "d"})
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("c"), true)
t.Assert(array1, []string{"a", "b", "c", "d"})
})
}
func TestSortedStrArray_Get(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.Get(2), "c")
gtest.Assert(array1.Get(0), "a")
v, ok := array1.Get(2)
t.Assert(v, "c")
t.Assert(ok, true)
v, ok = array1.Get(0)
t.Assert(v, "a")
t.Assert(ok, true)
})
}
func TestSortedStrArray_Remove(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.Remove(2), "c")
gtest.Assert(array1.Get(2), "d")
gtest.Assert(array1.Len(), 3)
gtest.Assert(array1.Contains("c"), false)
gtest.Assert(array1.Remove(0), "a")
gtest.Assert(array1.Len(), 2)
gtest.Assert(array1.Contains("a"), false)
v, ok := array1.Remove(-1)
t.Assert(v, "")
t.Assert(ok, false)
// 此时array1里的元素只剩下2个
gtest.Assert(array1.Remove(1), "d")
gtest.Assert(array1.Len(), 1)
v, ok = array1.Remove(100000)
t.Assert(v, "")
t.Assert(ok, false)
v, ok = array1.Remove(2)
t.Assert(v, "c")
t.Assert(ok, true)
v, ok = array1.Get(2)
t.Assert(v, "d")
t.Assert(ok, true)
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("c"), false)
v, ok = array1.Remove(0)
t.Assert(v, "a")
t.Assert(ok, true)
t.Assert(array1.Len(), 2)
t.Assert(array1.Contains("a"), false)
v, ok = array1.Remove(1)
t.Assert(v, "d")
t.Assert(ok, true)
t.Assert(array1.Len(), 1)
})
}
func TestSortedStrArray_PopLeft(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopLeft()
gtest.Assert(s1, "a")
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("a"), false)
v, ok := array1.PopLeft()
t.Assert(v, "a")
t.Assert(ok, true)
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) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRight()
gtest.Assert(s1, "e")
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains("e"), false)
v, ok := array1.PopRight()
t.Assert(v, "e")
t.Assert(ok, ok)
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) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRand()
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
gtest.Assert(array1.Len(), 4)
gtest.Assert(array1.Contains(s1), false)
s1, ok := array1.PopRand()
t.Assert(ok, true)
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains(s1), false)
})
}
func TestSortedStrArray_PopRands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRands(2)
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(len(s1), 2)
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
t.Assert(array1.Len(), 3)
t.Assert(len(s1), 2)
s1 = array1.PopRands(4)
gtest.Assert(len(s1), 3)
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
t.Assert(len(s1), 3)
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
})
}
func TestSortedStrArray_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArray()
v, ok := array.PopLeft()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
}
func TestSortedStrArray_PopLefts(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopLefts(2)
gtest.Assert(s1, []string{"a", "b"})
gtest.Assert(array1.Len(), 3)
gtest.Assert(len(s1), 2)
t.Assert(s1, []string{"a", "b"})
t.Assert(array1.Len(), 3)
t.Assert(len(s1), 2)
s1 = array1.PopLefts(4)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"c", "d", "e"})
t.Assert(len(s1), 3)
t.Assert(s1, []string{"c", "d", "e"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedStrArray_PopRights(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.PopRights(2)
gtest.Assert(s1, []string{"f", "g"})
gtest.Assert(array1.Len(), 5)
gtest.Assert(len(s1), 2)
t.Assert(s1, []string{"f", "g"})
t.Assert(array1.Len(), 5)
t.Assert(len(s1), 2)
s1 = array1.PopRights(6)
gtest.Assert(len(s1), 5)
gtest.Assert(s1, []string{"a", "b", "c", "d", "e"})
gtest.Assert(array1.Len(), 0)
t.Assert(len(s1), 5)
t.Assert(s1, []string{"a", "b", "c", "d", "e"})
t.Assert(array1.Len(), 0)
})
}
func TestSortedStrArray_Range(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := garray.NewSortedStrArrayFrom(a1, true)
s1 := array1.Range(2, 4)
gtest.Assert(len(s1), 2)
gtest.Assert(s1, []string{"c", "d"})
t.Assert(len(s1), 2)
t.Assert(s1, []string{"c", "d"})
s1 = array1.Range(-1, 2)
gtest.Assert(len(s1), 2)
gtest.Assert(s1, []string{"a", "b"})
t.Assert(len(s1), 2)
t.Assert(s1, []string{"a", "b"})
s1 = array1.Range(4, 8)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"e", "f", "g"})
gtest.Assert(array1.Range(10, 2), nil)
t.Assert(len(s1), 3)
t.Assert(s1, []string{"e", "f", "g"})
t.Assert(array1.Range(10, 2), nil)
s2 := array2.Range(2, 4)
gtest.Assert(s2, []string{"c", "d"})
t.Assert(s2, []string{"c", "d"})
})
}
func TestSortedStrArray_Sum(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
a2 := []string{"1", "2", "3", "4", "a"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := garray.NewSortedStrArrayFrom(a2)
gtest.Assert(array1.Sum(), 0)
gtest.Assert(array2.Sum(), 10)
t.Assert(array1.Sum(), 0)
t.Assert(array2.Sum(), 10)
})
}
func TestSortedStrArray_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := array1.Clone()
gtest.Assert(array1, array2)
t.Assert(array1, array2)
array1.Remove(1)
gtest.Assert(array2.Len(), 7)
t.Assert(array2.Len(), 7)
})
}
func TestSortedStrArray_Clear(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
array1.Clear()
gtest.Assert(array1.Len(), 0)
t.Assert(array1.Len(), 0)
})
}
func TestSortedStrArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := garray.NewSortedStrArrayFrom(a1, true)
s1 := array1.SubSlice(1, 3)
gtest.Assert(len(s1), 3)
gtest.Assert(s1, []string{"b", "c", "d"})
gtest.Assert(array1.Len(), 7)
t.Assert(len(s1), 3)
t.Assert(s1, []string{"b", "c", "d"})
t.Assert(array1.Len(), 7)
s2 := array1.SubSlice(1, 10)
gtest.Assert(len(s2), 6)
t.Assert(len(s2), 6)
s3 := array1.SubSlice(10, 2)
gtest.Assert(len(s3), 0)
t.Assert(len(s3), 0)
s3 = array1.SubSlice(-5, 2)
gtest.Assert(s3, []string{"c", "d"})
t.Assert(s3, []string{"c", "d"})
s3 = array1.SubSlice(-10, 2)
gtest.Assert(s3, nil)
t.Assert(s3, nil)
s3 = array1.SubSlice(1, -2)
gtest.Assert(s3, nil)
t.Assert(s3, nil)
gtest.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
t.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
})
}
func TestSortedStrArray_Len(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.Len(), 7)
t.Assert(array1.Len(), 7)
})
}
func TestSortedStrArray_Rand(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.AssertIN(array1.Rand(), []string{"e", "a", "d"})
v, ok := array1.Rand()
t.AssertIN(v, []string{"e", "a", "d"})
t.Assert(ok, true)
})
}
func TestSortedStrArray_Rands(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
s1 := array1.Rands(2)
gtest.AssertIN(s1, []string{"e", "a", "d"})
gtest.Assert(len(s1), 2)
t.AssertIN(s1, []string{"e", "a", "d"})
t.Assert(len(s1), 2)
s1 = array1.Rands(4)
gtest.AssertIN(s1, []string{"e", "a", "d"})
gtest.Assert(len(s1), 3)
t.Assert(len(s1), 4)
})
}
func TestSortedStrArray_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.Join(","), `a,d,e`)
gtest.Assert(array1.Join("."), `a.d.e`)
t.Assert(array1.Join(","), `a,d,e`)
t.Assert(array1.Join("."), `a.d.e`)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", `"b"`, `\c`}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.Join("."), `"b".\c.a`)
t.Assert(array1.Join("."), `"b".\c.a`)
})
}
func TestSortedStrArray_String(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
gtest.Assert(array1.String(), `["a","d","e"]`)
t.Assert(array1.String(), `["a","d","e"]`)
})
}
func TestSortedStrArray_CountValues(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStrArrayFrom(a1)
m1 := array1.CountValues()
gtest.Assert(m1["a"], 2)
gtest.Assert(m1["d"], 1)
t.Assert(m1["a"], 2)
t.Assert(m1["d"], 1)
})
}
func TestSortedStrArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := array1.Chunk(2)
gtest.Assert(len(array2), 3)
gtest.Assert(len(array2[0]), 2)
gtest.Assert(array2[1], []string{"c", "d"})
gtest.Assert(array1.Chunk(0), nil)
t.Assert(len(array2), 3)
t.Assert(len(array2[0]), 2)
t.Assert(array2[1], []string{"c", "d"})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5"}
array1 := garray.NewSortedStrArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []string{"1", "2", "3"})
t.Assert(chunks[1], []string{"4", "5"})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5", "6"}
array1 := garray.NewSortedStrArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []string{"1", "2"})
t.Assert(chunks[1], []string{"3", "4"})
t.Assert(chunks[2], []string{"5", "6"})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "2", "3", "4", "5", "6"}
array1 := garray.NewSortedStrArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []string{"1", "2", "3"})
t.Assert(chunks[1], []string{"4", "5", "6"})
t.Assert(array1.Chunk(0), nil)
})
}
func TestSortedStrArray_SetUnique(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := array1.SetUnique(true)
gtest.Assert(array2.Len(), 4)
gtest.Assert(array2, []string{"a", "c", "d", "e"})
t.Assert(array2.Len(), 4)
t.Assert(array2, []string{"a", "c", "d", "e"})
})
}
func TestSortedStrArray_LockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedStrArrayFrom(s1, true)
@ -369,13 +489,13 @@ func TestSortedStrArray_LockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedStrArray_RLockFunc(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "c", "d"}
a1 := garray.NewSortedStrArrayFrom(s1, true)
@ -401,13 +521,13 @@ func TestSortedStrArray_RLockFunc(t *testing.T) {
<-ch2 //等待go1完成
// 防止ci抖动,以豪秒为单位
gtest.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
gtest.Assert(a1.Contains("g"), true)
t.AssertLT(t2-t1, 20) //go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestSortedStrArray_Merge(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
@ -425,39 +545,61 @@ func TestSortedStrArray_Merge(t *testing.T) {
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewSortedStrArrayFrom(s1)
gtest.Assert(a1.Merge(s2).Len(), 6)
gtest.Assert(a1.Merge(i1).Len(), 9)
gtest.Assert(a1.Merge(i2).Len(), 10)
gtest.Assert(a1.Merge(s3).Len(), 12)
gtest.Assert(a1.Merge(s4).Len(), 14)
gtest.Assert(a1.Merge(s5).Len(), 16)
gtest.Assert(a1.Merge(s6).Len(), 19)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i1).Len(), 9)
t.Assert(a1.Merge(i2).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestSortedStrArray_Json(t *testing.T) {
gtest.Case(t, func() {
// array pointer
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)
gtest.Assert(b1, b2)
gtest.Assert(err1, err2)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedStrArray()
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s2)
gtest.Assert(a2.Interfaces(), s2)
t.Assert(a2.Slice(), s2)
t.Assert(a2.Interfaces(), s2)
var a3 garray.SortedStrArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
gtest.Assert(a3.Interfaces(), s1)
t.Assert(err, nil)
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)
gtest.Case(t, func() {
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
Scores *garray.SortedStrArray
@ -467,59 +609,139 @@ func TestSortedStrArray_Json(t *testing.T) {
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
gtest.Assert(err, nil)
gtest.Assert(user.Name, data["Name"])
gtest.Assert(user.Scores, []string{"A", "A", "A+"})
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)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []string{"A", "A", "A+"})
})
}
func TestSortedStrArray_Iterator(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedStrArrayFrom(slice)
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
t.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
t.Assert(index, 1)
})
}
func TestSortedStrArray_RemoveValue(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedStrArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestSortedStrArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.SortedStrArray
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`["1","3","2"]`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.SliceStr{"1", "3", "2"},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
}
func TestSortedStrArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
t.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
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.Case(t, func() {
ch := gchan.New(10)
gtest.Assert(ch.Cap(), 10)
gtest.Assert(ch.Push(1), nil)
gtest.Assert(ch.Len(), 1)
gtest.Assert(ch.Size(), 1)
ch.Pop()
gtest.Assert(ch.Len(), 0)
gtest.Assert(ch.Size(), 0)
ch.Close()
gtest.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
}
gtest.Assert(v, i)
i++
}
}()
for index := 0; index < 10; index++ {
ch.Push(index)
}
ch.Close()
gtest.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/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
@ -438,3 +525,21 @@ func (l *List) UnmarshalJSON(b []byte) error {
l.PushBacks(array)
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for list.
func (l *List) UnmarshalValue(value interface{}) (err error) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
switch value.(type) {
case string, []byte:
err = json.Unmarshal(gconv.Bytes(value), &array)
default:
array = gconv.SliceAny(value)
}
l.PushBacks(array)
return err
}

View File

@ -10,6 +10,7 @@ import (
"container/list"
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/glist"
)
@ -96,5 +97,30 @@ func Example_iterate() {
//10987654321
//12345678910
//10987654321
//[1,2,3,4,5,"M",7,8,9,10]
//[1,2,3,4,5,M,7,8,9,10]
}
func Example_popItem() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBack())
fmt.Println(l.PopBacks(2))
fmt.Println(l.PopFront())
fmt.Println(l.PopFronts(2))
// Output:
// 9
// [8 7]
// 1
// [2 3]
}
func Example_join() {
var l glist.List
l.PushBacks(g.Slice{"a", "b", "c", "d"})
fmt.Println(l.Join(","))
// Output:
// a,b,c,d
}

View File

@ -15,8 +15,7 @@ import (
"testing"
)
// 检查链表长度
func checkListLen(t *testing.T, l *List, len int) bool {
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)
return false
@ -24,8 +23,7 @@ func checkListLen(t *testing.T, l *List, len int) bool {
return true
}
// 检查指针地址
func checkListPointers(t *testing.T, l *List, es []*Element) {
func checkListPointers(t *gtest.T, l *List, es []*Element) {
if !checkListLen(t, l, len(es)) {
return
}
@ -41,6 +39,44 @@ func checkListPointers(t *testing.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)
@ -80,87 +116,89 @@ func TestBasic(t *testing.T) {
}
func TestList(t *testing.T) {
l := New()
checkListPointers(t, l, []*Element{})
gtest.C(t, func(t *gtest.T) {
l := New()
checkListPointers(t, l, []*Element{})
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
l.Remove(e)
checkListPointers(t, l, []*Element{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Element{e1, e4, e3})
e2 = l.InsertBefore(e1, 2) // insert before front
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(e4, 2) // insert before middle
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(e3, 2) // insert before back
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(e1, 2) // insert after front
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(e4, 2) // insert after middle
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(e3, 2) // insert after back
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
l.Remove(e2)
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
if i, ok := e.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
var next *Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
checkListPointers(t, l, []*Element{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Element{e1, e4, e3})
e2 = l.InsertBefore(e1, 2) // insert before front
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(e4, 2) // insert before middle
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(e3, 2) // insert before back
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(e1, 2) // insert after front
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(e4, 2) // insert after middle
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(e3, 2) // insert after back
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
l.Remove(e2)
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
if i, ok := e.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
var next *Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
})
}
func checkList(t *testing.T, l *List, es []interface{}) {
func checkList(t *gtest.T, l *List, es []interface{}) {
if !checkListLen(t, l, len(es)) {
return
}
@ -193,60 +231,64 @@ func checkList(t *testing.T, l *List, es []interface{}) {
}
func TestExtending(t *testing.T) {
l1 := New()
l2 := New()
gtest.C(t, func(t *gtest.T) {
l1 := New()
l2 := New()
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l2.PushBack(4)
l2.PushBack(5)
l2.PushBack(4)
l2.PushBack(5)
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
})
}
func TestRemove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Element{e1, e2})
//e := l.Front()
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
gtest.C(t, func(t *gtest.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Element{e1, e2})
//e := l.Front()
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
})
}
func TestIssue4103(t *testing.T) {
@ -289,346 +331,427 @@ func TestIssue6349(t *testing.T) {
}
func TestMove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
gtest.C(t, func(t *gtest.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
})
}
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
func TestZeroList(t *testing.T) {
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
gtest.C(t, func(t *gtest.T) {
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
})
}
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
gtest.C(t, func(t *gtest.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
})
}
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
gtest.C(t, func(t *gtest.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(new(Element), 1)
checkList(t, l, []interface{}{1, 2, 3})
})
}
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
l1 := New()
e1 := l1.PushBack(1)
gtest.C(t, func(t *gtest.T) {
l1 := New()
e1 := l1.PushBack(1)
l2 := New()
e2 := l2.PushBack(2)
l2 := New()
e2 := l2.PushBack(2)
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
})
}
func TestList_RemoveAll(t *testing.T) {
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
gtest.C(t, func(t *gtest.T) {
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
})
}
func TestList_PushFronts(t *testing.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushFronts(a1)
checkList(t, l, []interface{}{2, 1})
a1 = []interface{}{3, 4, 5}
l.PushFronts(a1)
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushFronts(a1)
checkList(t, l, []interface{}{2, 1})
a1 = []interface{}{3, 4, 5}
l.PushFronts(a1)
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
})
}
func TestList_PushBacks(t *testing.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2})
a1 = []interface{}{3, 4, 5}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2})
a1 = []interface{}{3, 4, 5}
l.PushBacks(a1)
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
})
}
func TestList_PopBacks(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a2 := []interface{}{"a", "c", "b", "e"}
l.PushFronts(a1)
i1 := l.PopBacks(2)
gtest.Assert(i1, []interface{}{1, 2})
t.Assert(i1, []interface{}{1, 2})
l.PushBacks(a2) //4.3,a,c,b,e
i1 = l.PopBacks(3)
gtest.Assert(i1, []interface{}{"e", "b", "c"})
t.Assert(i1, []interface{}{"e", "b", "c"})
})
}
func TestList_PopFronts(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFronts(2)
gtest.Assert(i1, []interface{}{4, 3})
gtest.Assert(l.Len(), 2)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFronts(2)
t.Assert(i1, []interface{}{4, 3})
t.Assert(l.Len(), 2)
})
}
func TestList_PopBackAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopBackAll()
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
gtest.Assert(l.Len(), 0)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopBackAll()
t.Assert(i1, []interface{}{1, 2, 3, 4})
t.Assert(l.Len(), 0)
})
}
func TestList_PopFrontAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFrontAll()
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
gtest.Assert(l.Len(), 0)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFrontAll()
t.Assert(i1, []interface{}{4, 3, 2, 1})
t.Assert(l.Len(), 0)
})
}
func TestList_FrontAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontAll()
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
gtest.Assert(l.Len(), 4)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontAll()
t.Assert(i1, []interface{}{4, 3, 2, 1})
t.Assert(l.Len(), 4)
})
}
func TestList_BackAll(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackAll()
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
gtest.Assert(l.Len(), 4)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackAll()
t.Assert(i1, []interface{}{1, 2, 3, 4})
t.Assert(l.Len(), 4)
})
}
func TestList_FrontValue(t *testing.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontValue()
gtest.Assert(gconv.Int(i1), 4)
gtest.Assert(l.Len(), 4)
gtest.C(t, func(t *gtest.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.FrontValue()
t.Assert(gconv.Int(i1), 4)
t.Assert(l.Len(), 4)
i1 = l2.FrontValue()
gtest.Assert(i1, nil)
i1 = l2.FrontValue()
t.Assert(i1, nil)
})
}
func TestList_BackValue(t *testing.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackValue()
gtest.Assert(gconv.Int(i1), 1)
gtest.Assert(l.Len(), 4)
i1 = l2.FrontValue()
gtest.Assert(i1, nil)
gtest.C(t, func(t *gtest.T) {
l := New()
l2 := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.BackValue()
t.Assert(gconv.Int(i1), 1)
t.Assert(l.Len(), 4)
i1 = l2.FrontValue()
t.Assert(i1, nil)
})
}
func TestList_Back(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
gtest.Assert(e1.Value, 1)
gtest.Assert(l.Len(), 4)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
t.Assert(e1.Value, 1)
t.Assert(l.Len(), 4)
})
}
func TestList_Size(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
gtest.Assert(l.Size(), 4)
l.PopFront()
gtest.Assert(l.Size(), 3)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
t.Assert(l.Size(), 4)
l.PopFront()
t.Assert(l.Size(), 3)
})
}
func TestList_Removes(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
l.Removes([]*Element{e1})
gtest.Assert(l.Len(), 3)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
l.Removes([]*Element{e1})
t.Assert(l.Len(), 3)
e2 := l.Back()
l.Removes([]*Element{e2})
gtest.Assert(l.Len(), 2)
checkList(t, l, []interface{}{4, 3})
e2 := l.Back()
l.Removes([]*Element{e2})
t.Assert(l.Len(), 2)
checkList(t, l, []interface{}{4, 3})
})
}
func TestList_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
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) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
l.Clear()
gtest.Assert(l.Len(), 0)
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
l.Clear()
t.Assert(l.Len(), 0)
})
}
func TestList_IteratorAsc(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 5, 6, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 2 {
return true
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 5, 6, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 2 {
return true
}
return false
}
return false
}
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
l.IteratorAsc(fun1)
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
l.IteratorAsc(fun1)
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
})
}
func TestList_IteratorDesc(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 6 {
return true
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.Int(e1.Value) > 6 {
return true
}
return false
}
return false
}
l.IteratorDesc(fun1)
gtest.Assert(l.Len(), 4)
checkList(t, l, []interface{}{4, 3, 2, 1})
l.IteratorDesc(fun1)
t.Assert(l.Len(), 4)
checkList(t, l, []interface{}{4, 3, 2, 1})
})
}
func TestList_Iterator(t *testing.T) {
l := New()
a1 := []interface{}{"a", "b", "c", "d", "e"}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.String(e1.Value) > "c" {
return true
gtest.C(t, func(t *gtest.T) {
l := New()
a1 := []interface{}{"a", "b", "c", "d", "e"}
l.PushFronts(a1)
e1 := l.Back()
fun1 := func(e *Element) bool {
if gconv.String(e1.Value) > "c" {
return true
}
return false
}
return false
}
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
l.Iterator(fun1)
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
l.Iterator(fun1)
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
})
}
func TestList_Join(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
gtest.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
gtest.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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
gtest.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
t.Assert(l.String(), `[1,2,a,"b",\c]`)
})
}
func TestList_Json(t *testing.T) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a := []interface{}{"a", "b", "c"}
l := New()
l.PushBacks(a)
b1, err1 := json.Marshal(l)
b2, err2 := json.Marshal(a)
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
a := []interface{}{"a", "b", "c"}
l := New()
b, err := json.Marshal(a)
gtest.Assert(err, nil)
t.Assert(err, nil)
err = json.Unmarshal(b, l)
gtest.Assert(err, nil)
gtest.Assert(l.FrontAll(), a)
t.Assert(err, nil)
t.Assert(l.FrontAll(), a)
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
var l List
a := []interface{}{"a", "b", "c"}
b, err := json.Marshal(a)
gtest.Assert(err, nil)
t.Assert(err, nil)
err = json.Unmarshal(b, &l)
gtest.Assert(err, nil)
gtest.Assert(l.FrontAll(), a)
t.Assert(err, nil)
t.Assert(l.FrontAll(), a)
})
}
func TestList_UnmarshalValue(t *testing.T) {
type TList struct {
Name string
List *List
}
// JSON
gtest.C(t, func(t *gtest.T) {
var tlist *TList
err := gconv.Struct(map[string]interface{}{
"name": "john",
"list": []byte(`[1,2,3]`),
}, &tlist)
t.Assert(err, nil)
t.Assert(tlist.Name, "john")
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var tlist *TList
err := gconv.Struct(map[string]interface{}{
"name": "john",
"list": []interface{}{1, 2, 3},
}, &tlist)
t.Assert(err, nil)
t.Assert(tlist.Name, "john")
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
})
}

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

@ -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()
@ -421,12 +464,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
@ -436,3 +478,16 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
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
}
return
}

View File

@ -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()
@ -421,14 +464,31 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
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)
default:
for k, v := range gconv.Map(value) {
m.data[gconv.Int(k)] = v
}
}
return
}

View File

@ -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()
@ -398,14 +435,31 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
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)
default:
for k, v := range gconv.Map(value) {
m.data[gconv.Int(k)] = gconv.Int(v)
}
}
return
}

View File

@ -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()
@ -399,14 +435,31 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
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)
default:
for k, v := range gconv.Map(value) {
m.data[gconv.Int(k)] = gconv.String(v)
}
}
return
}

View File

@ -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()
@ -417,14 +460,21 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.Map(value)
return
}

View File

@ -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()
@ -400,14 +438,31 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
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)
default:
for k, v := range gconv.Map(value) {
m.data[k] = gconv.Int(v)
}
}
return
}

View File

@ -9,6 +9,7 @@ package gmap
import (
"encoding/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/empty"
@ -16,7 +17,7 @@ import (
)
type StrStrMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]string
}
@ -26,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...),
}
}
@ -35,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()
@ -96,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 {
@ -109,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()
}
@ -116,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()
}
@ -126,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.
@ -161,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
@ -182,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
}
@ -220,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
@ -262,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()
}
@ -273,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++
@ -306,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++
@ -319,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.
@ -383,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()
@ -401,14 +438,21 @@ 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
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.MapStrStr(value)
return
}

View File

@ -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()
@ -471,13 +517,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
@ -491,3 +536,21 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
for k, v := range gconv.Map(value) {
if e, ok := m.data[k]; !ok {
m.data[k] = m.list.PushBack(&gListMapNode{k, v})
} else {
e.Value = &gListMapNode{k, v}
}
}
return
}

View File

@ -7,6 +7,7 @@
package gmap_test
import (
"github.com/gogf/gf/util/gutil"
"testing"
"github.com/gogf/gf/container/gmap"
@ -17,110 +18,170 @@ 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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.New()
m.Set("key1", "val1")
gtest.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Keys(), []interface{}{"key1"})
gtest.Assert(m.Get("key1"), "val1")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
gtest.Assert(m.Remove("key2"), "val2")
gtest.Assert(m.Contains("key2"), false)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
gtest.AssertIN("key3", m.Keys())
gtest.AssertIN("key1", m.Keys())
gtest.AssertIN("val3", m.Values())
gtest.AssertIN("val1", m.Values())
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_Map_Set_Fun(t *testing.T) {
m := gmap.New()
m.GetOrSetFunc("fun", getValue)
m.GetOrSetFuncLock("funlock", getValue)
gtest.Assert(m.Get("funlock"), 3)
gtest.Assert(m.Get("fun"), 3)
m.GetOrSetFunc("fun", getValue)
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
gtest.C(t, func(t *gtest.T) {
m := gmap.New()
m.GetOrSetFunc("fun", getValue)
m.GetOrSetFuncLock("funlock", getValue)
t.Assert(m.Get("funlock"), 3)
t.Assert(m.Get("fun"), 3)
m.GetOrSetFunc("fun", getValue)
t.Assert(m.SetIfNotExistFunc("fun", getValue), false)
t.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
})
}
func Test_Map_Batch(t *testing.T) {
m := gmap.New()
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
gtest.C(t, func(t *gtest.T) {
m := gmap.New()
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
t.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
})
}
func Test_Map_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
gtest.C(t, func(t *gtest.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
m := gmap.NewFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
t.Assert(i, 2)
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_Map_Lock(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
gtest.C(t, func(t *gtest.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {
t.Assert(m, expect)
})
m.RLockFunc(func(m map[interface{}]interface{}) {
t.Assert(m, expect)
})
})
}
func Test_Map_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
t.AssertIN(1, m_clone.Keys())
m_clone.Remove("key1")
//修改clone map,原 map 不影响
gtest.AssertIN("key1", m.Keys())
m_clone.Remove("key1")
//修改clone map,原 map 不影响
t.AssertIN("key1", m.Keys())
})
}
func Test_Map_Basic_Merge(t *testing.T) {
m1 := gmap.New()
m2 := gmap.New()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.New()
m2 := gmap.New()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
})
}

View File

@ -1,12 +1,19 @@
// 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 Example_Normal_Basic() {
func Example_normalBasic() {
m := gmap.New()
//Add data
@ -59,13 +66,97 @@ func Example_Normal_Basic() {
m.Clear()
fmt.Println(m.Size())
}
func Example_Normal_Merge() {
m1 := gmap.New()
m2 := gmap.New()
func Example_keysValues() {
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 Example_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 Example_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 Example_filter() {
m1 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m2 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m1.FilterEmpty()
m2.FilterNil()
fmt.Println(m1.Map())
fmt.Println(m2.Map())
// Output:
// map[k4:1]
// map[k1: k3:0 k4:1]
}
func Example_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 Example_normalMerge() {
var m1, m2 gmap.Map
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
m1.Merge(&m2)
fmt.Println(m1.Map())
// May Output:
// map[key1:val1 key2:val2]
}

View File

@ -9,179 +9,232 @@ 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/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) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, "2"), "2")
gtest.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
gtest.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.SetIfNotExist(3, 3), true)
gtest.Assert(m.Remove(2), "2")
gtest.Assert(m.Contains(2), false)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
m2 := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
})
}
func Test_AnyAnyMap_Set_Fun(t *testing.T) {
m := gmap.NewAnyAnyMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.GetOrSetFunc(1, getAny)
m.GetOrSetFuncLock(2, getAny)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
m.GetOrSetFunc(1, getAny)
m.GetOrSetFuncLock(2, getAny)
t.Assert(m.Get(1), 123)
t.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
})
}
func Test_AnyAnyMap_Batch(t *testing.T) {
m := gmap.NewAnyAnyMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Sets(map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
m.Removes([]interface{}{1, 2})
gtest.Assert(m.Map(), map[interface{}]interface{}{3: 3})
m.Sets(map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
m.Removes([]interface{}{1, 2})
t.Assert(m.Map(), map[interface{}]interface{}{3: 3})
})
}
func Test_AnyAnyMap_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, 2: "2"}
m := gmap.NewAnyAnyMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
gtest.C(t, func(t *gtest.T) {
expect := map[interface{}]interface{}{1: 1, 2: "2"}
m := gmap.NewAnyAnyMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
t.Assert(i, "2")
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, "2")
gtest.Assert(j, 1)
}
func Test_AnyAnyMap_Lock(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, 2: "2"}
m := gmap.NewAnyAnyMapFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[interface{}]interface{}) {
gtest.Assert(m, expect)
gtest.C(t, func(t *gtest.T) {
expect := map[interface{}]interface{}{1: 1, 2: "2"}
m := gmap.NewAnyAnyMapFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {
t.Assert(m, expect)
})
m.RLockFunc(func(m map[interface{}]interface{}) {
t.Assert(m, expect)
})
})
}
func Test_AnyAnyMap_Clone(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
t.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
t.AssertIN(2, m.Keys())
})
}
func Test_AnyAnyMap_Merge(t *testing.T) {
m1 := gmap.NewAnyAnyMap()
m2 := gmap.NewAnyAnyMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.NewAnyAnyMap()
m2 := gmap.NewAnyAnyMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)
t.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
})
}
func Test_AnyAnyMap_Map(t *testing.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.Map()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), 3)
m.Set(4, 4)
gtest.Assert(data[4], 4)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.Map()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), 3)
m.Set(4, 4)
t.Assert(data[4], 4)
})
}
func Test_AnyAnyMap_MapCopy(t *testing.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.MapCopy()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), nil)
m.Set(4, 4)
gtest.Assert(data[4], nil)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.MapCopy()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), nil)
m.Set(4, 4)
t.Assert(data[4], nil)
})
}
func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
m.FilterEmpty()
gtest.Assert(m.Get(1), nil)
gtest.Assert(m.Get(2), 2)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
m.FilterEmpty()
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) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
@ -189,89 +242,125 @@ func Test_AnyAnyMap_Json(t *testing.T) {
m1 := gmap.NewAnyAnyMapFrom(data)
b1, err1 := json.Marshal(m1)
b2, err2 := json.Marshal(gconv.Map(data))
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
}
b, err := json.Marshal(gconv.Map(data))
gtest.Assert(err, nil)
t.Assert(err, nil)
m := gmap.New()
err = json.Unmarshal(b, m)
gtest.Assert(err, nil)
gtest.Assert(m.Get("k1"), data["k1"])
gtest.Assert(m.Get("k2"), data["k2"])
t.Assert(err, nil)
t.Assert(m.Get("k1"), data["k1"])
t.Assert(m.Get("k2"), data["k2"])
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
}
b, err := json.Marshal(gconv.Map(data))
gtest.Assert(err, nil)
t.Assert(err, nil)
var m gmap.Map
err = json.Unmarshal(b, &m)
gtest.Assert(err, nil)
gtest.Assert(m.Get("k1"), data["k1"])
gtest.Assert(m.Get("k2"), data["k2"])
t.Assert(err, nil)
t.Assert(m.Get("k1"), data["k1"])
t.Assert(m.Get("k2"), data["k2"])
})
}
func Test_AnyAnyMap_Pop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
k1, v1 := m.Pop()
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 1)
t.AssertIN(k1, g.Slice{"k1", "k2"})
t.AssertIN(v1, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 1)
k2, v2 := m.Pop()
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 0)
t.AssertIN(k2, g.Slice{"k1", "k2"})
t.AssertIN(v2, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 0)
gtest.AssertNE(k1, k2)
gtest.AssertNE(v1, v2)
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
})
}
func Test_AnyAnyMap_Pops(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
})
gtest.Assert(m.Size(), 3)
t.Assert(m.Size(), 3)
kArray := garray.New()
vArray := garray.New()
for k, v := range m.Pops(1) {
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
for k, v := range m.Pops(2) {
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 0)
t.Assert(m.Size(), 0)
gtest.Assert(kArray.Unique().Len(), 3)
gtest.Assert(vArray.Unique().Len(), 3)
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
})
}
func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Map *gmap.Map
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": []byte(`{"k1":"v1","k2":"v2"}`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get("k1"), "v1")
t.Assert(v.Map.Get("k2"), "v2")
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": g.Map{
"k1": "v1",
"k2": "v2",
},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get("k1"), "v1")
t.Assert(v.Map.Get("k2"), "v2")
})
}

View File

@ -10,6 +10,7 @@ import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"testing"
"github.com/gogf/gf/container/gmap"
@ -19,165 +20,211 @@ 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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, "2"), "2")
gtest.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
gtest.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.SetIfNotExist(3, 3), true)
gtest.Assert(m.Remove(2), "2")
gtest.Assert(m.Contains(2), false)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
})
}
func Test_IntAnyMap_Set_Fun(t *testing.T) {
m := gmap.NewIntAnyMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.GetOrSetFunc(1, getAny)
m.GetOrSetFuncLock(2, getAny)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
m.GetOrSetFunc(1, getAny)
m.GetOrSetFuncLock(2, getAny)
t.Assert(m.Get(1), 123)
t.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
})
}
func Test_IntAnyMap_Batch(t *testing.T) {
m := gmap.NewIntAnyMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
t.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
m.Removes([]int{1, 2})
t.Assert(m.Map(), map[int]interface{}{3: 3})
})
}
func Test_IntAnyMap_Iterator(t *testing.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m.Iterator(func(k int, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
gtest.C(t, func(t *gtest.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m.Iterator(func(k int, v interface{}) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v interface{}) bool {
i++
return true
})
m.Iterator(func(k int, v interface{}) bool {
j++
return false
})
t.Assert(i, "2")
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v interface{}) bool {
i++
return true
})
m.Iterator(func(k int, v interface{}) bool {
j++
return false
})
gtest.Assert(i, "2")
gtest.Assert(j, 1)
}
func Test_IntAnyMap_Lock(t *testing.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m.LockFunc(func(m map[int]interface{}) {
gtest.Assert(m, expect)
})
m.RLockFunc(func(m map[int]interface{}) {
gtest.Assert(m, expect)
gtest.C(t, func(t *gtest.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m.LockFunc(func(m map[int]interface{}) {
t.Assert(m, expect)
})
m.RLockFunc(func(m map[int]interface{}) {
t.Assert(m, expect)
})
})
}
func Test_IntAnyMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
t.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
t.AssertIN(2, m.Keys())
})
}
func Test_IntAnyMap_Merge(t *testing.T) {
m1 := gmap.NewIntAnyMap()
m2 := gmap.NewIntAnyMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.NewIntAnyMap()
m2 := gmap.NewIntAnyMap()
m1.Set(1, 1)
m2.Set(2, "2")
m1.Merge(m2)
t.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
})
}
func Test_IntAnyMap_Map(t *testing.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.Map()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), 3)
m.Set(4, 4)
gtest.Assert(data[4], 4)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.Map()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), 3)
m.Set(4, 4)
t.Assert(data[4], 4)
})
}
func Test_IntAnyMap_MapCopy(t *testing.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.MapCopy()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), nil)
m.Set(4, 4)
gtest.Assert(data[4], nil)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.MapCopy()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), nil)
m.Set(4, 4)
t.Assert(data[4], nil)
})
}
func Test_IntAnyMap_FilterEmpty(t *testing.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Size(), 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
m.FilterEmpty()
gtest.Assert(m.Size(), 1)
gtest.Assert(m.Get(2), 2)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Size(), 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
m.FilterEmpty()
t.Assert(m.Size(), 1)
t.Assert(m.Get(2), 2)
})
}
func Test_IntAnyMap_Json(t *testing.T) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntAny{
1: "v1",
2: "v2",
@ -185,75 +232,111 @@ func Test_IntAnyMap_Json(t *testing.T) {
m1 := gmap.NewIntAnyMapFrom(data)
b1, err1 := json.Marshal(m1)
b2, err2 := json.Marshal(data)
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntAny{
1: "v1",
2: "v2",
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
m := gmap.NewIntAnyMap()
err = json.Unmarshal(b, m)
gtest.Assert(err, nil)
gtest.Assert(m.Get(1), data[1])
gtest.Assert(m.Get(2), data[2])
t.Assert(err, nil)
t.Assert(m.Get(1), data[1])
t.Assert(m.Get(2), data[2])
})
}
func Test_IntAnyMap_Pop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
1: "v1",
2: "v2",
})
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
k1, v1 := m.Pop()
gtest.AssertIN(k1, g.Slice{1, 2})
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 1)
t.AssertIN(k1, g.Slice{1, 2})
t.AssertIN(v1, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 1)
k2, v2 := m.Pop()
gtest.AssertIN(k2, g.Slice{1, 2})
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 0)
t.AssertIN(k2, g.Slice{1, 2})
t.AssertIN(v2, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 0)
gtest.AssertNE(k1, k2)
gtest.AssertNE(v1, v2)
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
})
}
func Test_IntAnyMap_Pops(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
1: "v1",
2: "v2",
3: "v3",
})
gtest.Assert(m.Size(), 3)
t.Assert(m.Size(), 3)
kArray := garray.New()
vArray := garray.New()
for k, v := range m.Pops(1) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
for k, v := range m.Pops(2) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 0)
t.Assert(m.Size(), 0)
gtest.Assert(kArray.Unique().Len(), 3)
gtest.Assert(vArray.Unique().Len(), 3)
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
})
}
func TestIntAnyMap_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Map *gmap.IntAnyMap
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": []byte(`{"1":"v1","2":"v2"}`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "v1")
t.Assert(v.Map.Get(2), "v2")
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": g.MapIntAny{
1: "v1",
2: "v2",
},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "v1")
t.Assert(v.Map.Get(2), "v2")
})
}

View File

@ -10,6 +10,7 @@ import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"testing"
"github.com/gogf/gf/container/gmap"
@ -19,168 +20,217 @@ 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.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.Set(1, 1)
gtest.Assert(m.Get(1), 1)
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, 2), 2)
gtest.Assert(m.SetIfNotExist(2, 2), false)
t.Assert(m.GetOrSet(2, 2), 2)
t.Assert(m.SetIfNotExist(2, 2), false)
gtest.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.SetIfNotExist(3, 3), true)
gtest.Assert(m.Remove(2), 2)
gtest.Assert(m.Contains(2), false)
t.Assert(m.Remove(2), 2)
t.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN(3, m.Values())
gtest.AssertIN(1, m.Values())
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
gtest.Assert(m.Map(), map[int]int{1: 1, 3: 3})
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
})
}
func Test_IntIntMap_Set_Fun(t *testing.T) {
m := gmap.NewIntIntMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.GetOrSetFunc(1, getInt)
m.GetOrSetFuncLock(2, getInt)
gtest.Assert(m.Get(1), 123)
gtest.Assert(m.Get(2), 123)
gtest.Assert(m.SetIfNotExistFunc(1, getInt), false)
gtest.Assert(m.SetIfNotExistFunc(3, getInt), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
m.GetOrSetFunc(1, getInt)
m.GetOrSetFuncLock(2, getInt)
t.Assert(m.Get(1), 123)
t.Assert(m.Get(2), 123)
t.Assert(m.SetIfNotExistFunc(1, getInt), false)
t.Assert(m.SetIfNotExistFunc(3, getInt), true)
t.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
t.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
})
}
func Test_IntIntMap_Batch(t *testing.T) {
m := gmap.NewIntIntMap()
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
m.Iterator(intIntCallBack)
gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]int{3: 3})
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
m.Iterator(intIntCallBack)
t.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
m.Removes([]int{1, 2})
t.Assert(m.Map(), map[int]int{3: 3})
})
}
func Test_IntIntMap_Iterator(t *testing.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m.Iterator(func(k int, v int) bool {
gtest.Assert(expect[k], v)
return true
gtest.C(t, func(t *gtest.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m.Iterator(func(k int, v int) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v int) bool {
i++
return true
})
m.Iterator(func(k int, v int) bool {
j++
return false
})
t.Assert(i, 2)
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v int) bool {
i++
return true
})
m.Iterator(func(k int, v int) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_IntIntMap_Lock(t *testing.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m.LockFunc(func(m map[int]int) {
gtest.Assert(m, expect)
gtest.C(t, func(t *gtest.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m.LockFunc(func(m map[int]int) {
t.Assert(m, expect)
})
m.RLockFunc(func(m map[int]int) {
t.Assert(m, expect)
})
})
m.RLockFunc(func(m map[int]int) {
gtest.Assert(m, expect)
})
}
func Test_IntIntMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
t.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
t.AssertIN(2, m.Keys())
})
}
func Test_IntIntMap_Merge(t *testing.T) {
m1 := gmap.NewIntIntMap()
m2 := gmap.NewIntIntMap()
m1.Set(1, 1)
m2.Set(2, 2)
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.NewIntIntMap()
m2 := gmap.NewIntIntMap()
m1.Set(1, 1)
m2.Set(2, 2)
m1.Merge(m2)
t.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
})
}
func Test_IntIntMap_Map(t *testing.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.Map()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), 3)
m.Set(4, 4)
gtest.Assert(data[4], 4)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.Map()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), 3)
m.Set(4, 4)
t.Assert(data[4], 4)
})
}
func Test_IntIntMap_MapCopy(t *testing.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
data := m.MapCopy()
gtest.Assert(data[1], 0)
gtest.Assert(data[2], 2)
data[3] = 3
gtest.Assert(m.Get(3), 0)
m.Set(4, 4)
gtest.Assert(data[4], 0)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
data := m.MapCopy()
t.Assert(data[1], 0)
t.Assert(data[2], 2)
data[3] = 3
t.Assert(m.Get(3), 0)
m.Set(4, 4)
t.Assert(data[4], 0)
})
}
func Test_IntIntMap_FilterEmpty(t *testing.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
gtest.Assert(m.Size(), 2)
gtest.Assert(m.Get(1), 0)
gtest.Assert(m.Get(2), 2)
m.FilterEmpty()
gtest.Assert(m.Size(), 1)
gtest.Assert(m.Get(2), 2)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
m.Set(1, 0)
m.Set(2, 2)
t.Assert(m.Size(), 2)
t.Assert(m.Get(1), 0)
t.Assert(m.Get(2), 2)
m.FilterEmpty()
t.Assert(m.Size(), 1)
t.Assert(m.Get(2), 2)
})
}
func Test_IntIntMap_Json(t *testing.T) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntInt{
1: 10,
2: 20,
@ -188,75 +238,111 @@ func Test_IntIntMap_Json(t *testing.T) {
m1 := gmap.NewIntIntMapFrom(data)
b1, err1 := json.Marshal(m1)
b2, err2 := json.Marshal(data)
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntInt{
1: 10,
2: 20,
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
m := gmap.NewIntIntMap()
err = json.Unmarshal(b, m)
gtest.Assert(err, nil)
gtest.Assert(m.Get(1), data[1])
gtest.Assert(m.Get(2), data[2])
t.Assert(err, nil)
t.Assert(m.Get(1), data[1])
t.Assert(m.Get(2), data[2])
})
}
func Test_IntIntMap_Pop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMapFrom(g.MapIntInt{
1: 11,
2: 22,
})
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
k1, v1 := m.Pop()
gtest.AssertIN(k1, g.Slice{1, 2})
gtest.AssertIN(v1, g.Slice{11, 22})
gtest.Assert(m.Size(), 1)
t.AssertIN(k1, g.Slice{1, 2})
t.AssertIN(v1, g.Slice{11, 22})
t.Assert(m.Size(), 1)
k2, v2 := m.Pop()
gtest.AssertIN(k2, g.Slice{1, 2})
gtest.AssertIN(v2, g.Slice{11, 22})
gtest.Assert(m.Size(), 0)
t.AssertIN(k2, g.Slice{1, 2})
t.AssertIN(v2, g.Slice{11, 22})
t.Assert(m.Size(), 0)
gtest.AssertNE(k1, k2)
gtest.AssertNE(v1, v2)
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
})
}
func Test_IntIntMap_Pops(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMapFrom(g.MapIntInt{
1: 11,
2: 22,
3: 33,
})
gtest.Assert(m.Size(), 3)
t.Assert(m.Size(), 3)
kArray := garray.New()
vArray := garray.New()
for k, v := range m.Pops(1) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{11, 22, 33})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{11, 22, 33})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
for k, v := range m.Pops(2) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{11, 22, 33})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{11, 22, 33})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 0)
t.Assert(m.Size(), 0)
gtest.Assert(kArray.Unique().Len(), 3)
gtest.Assert(vArray.Unique().Len(), 3)
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
})
}
func TestIntIntMap_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Map *gmap.IntIntMap
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": []byte(`{"1":1,"2":2}`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "1")
t.Assert(v.Map.Get(2), "2")
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": g.MapIntAny{
1: 1,
2: 2,
},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "1")
t.Assert(v.Map.Get(2), "2")
})
}

View File

@ -10,6 +10,7 @@ import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"testing"
"github.com/gogf/gf/container/gmap"
@ -19,169 +20,215 @@ import (
func getStr() string {
return "z"
}
func intStrCallBack(int, string) bool {
return true
}
func Test_IntStrMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewIntStrMap()
func Test_IntStrMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntStrMap
m.Set(1, "a")
gtest.Assert(m.Get(1), "a")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get(1), "a")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet(2, "b"), "b")
gtest.Assert(m.SetIfNotExist(2, "b"), false)
t.Assert(m.GetOrSet(2, "b"), "b")
t.Assert(m.SetIfNotExist(2, "b"), false)
gtest.Assert(m.SetIfNotExist(3, "c"), true)
t.Assert(m.SetIfNotExist(3, "c"), true)
gtest.Assert(m.Remove(2), "b")
gtest.Assert(m.Contains(2), false)
t.Assert(m.Remove(2), "b")
t.Assert(m.Contains(2), false)
gtest.AssertIN(3, m.Keys())
gtest.AssertIN(1, m.Keys())
gtest.AssertIN("a", m.Values())
gtest.AssertIN("c", m.Values())
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN("a", m.Values())
t.AssertIN("c", m.Values())
//反转之后不成为以下 map,flip 操作只是翻转原 map
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
m_f := gmap.NewIntStrMap()
m_f.Set(1, "2")
m_f.Flip()
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
t.Assert(m_f.Map(), map[int]string{2: "1"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
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()
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())
//反转之后不成为以下 map,flip 操作只是翻转原 map
//t.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
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)
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
})
}
func Test_IntStrMap_Set_Fun(t *testing.T) {
m := gmap.NewIntStrMap()
m.GetOrSetFunc(1, getStr)
m.GetOrSetFuncLock(2, getStr)
gtest.Assert(m.Get(1), "z")
gtest.Assert(m.Get(2), "z")
gtest.Assert(m.SetIfNotExistFunc(1, getStr), false)
gtest.Assert(m.SetIfNotExistFunc(3, getStr), true)
gtest.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
gtest.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.GetOrSetFunc(1, getStr)
m.GetOrSetFuncLock(2, getStr)
t.Assert(m.Get(1), "z")
t.Assert(m.Get(2), "z")
t.Assert(m.SetIfNotExistFunc(1, getStr), false)
t.Assert(m.SetIfNotExistFunc(3, getStr), true)
t.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
t.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
})
}
func Test_IntStrMap_Batch(t *testing.T) {
m := gmap.NewIntStrMap()
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
t.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
m.Removes([]int{1, 2})
t.Assert(m.Map(), map[int]interface{}{3: "c"})
})
}
func Test_IntStrMap_Iterator(t *testing.T) {
expect := map[int]string{1: "a", 2: "b"}
m := gmap.NewIntStrMapFrom(expect)
m.Iterator(func(k int, v string) bool {
gtest.Assert(expect[k], v)
return true
gtest.C(t, func(t *gtest.T) {
expect := map[int]string{1: "a", 2: "b"}
m := gmap.NewIntStrMapFrom(expect)
m.Iterator(func(k int, v string) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v string) bool {
i++
return true
})
m.Iterator(func(k int, v string) bool {
j++
return false
})
t.Assert(i, 2)
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k int, v string) bool {
i++
return true
})
m.Iterator(func(k int, v string) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_IntStrMap_Lock(t *testing.T) {
expect := map[int]string{1: "a", 2: "b", 3: "c"}
m := gmap.NewIntStrMapFrom(expect)
m.LockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
gtest.C(t, func(t *gtest.T) {
expect := map[int]string{1: "a", 2: "b", 3: "c"}
m := gmap.NewIntStrMapFrom(expect)
m.LockFunc(func(m map[int]string) {
t.Assert(m, expect)
})
m.RLockFunc(func(m map[int]string) {
t.Assert(m, expect)
})
})
m.RLockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
})
}
func Test_IntStrMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
t.AssertIN(1, m_clone.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
gtest.AssertIN(2, m.Keys())
m_clone.Remove(2)
//修改clone map,原 map 不影响
t.AssertIN(2, m.Keys())
})
}
func Test_IntStrMap_Merge(t *testing.T) {
m1 := gmap.NewIntStrMap()
m2 := gmap.NewIntStrMap()
m1.Set(1, "a")
m2.Set(2, "b")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.NewIntStrMap()
m2 := gmap.NewIntStrMap()
m1.Set(1, "a")
m2.Set(2, "b")
m1.Merge(m2)
t.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
})
}
func Test_IntStrMap_Map(t *testing.T) {
m := gmap.NewIntStrMap()
m.Set(1, "0")
m.Set(2, "2")
gtest.Assert(m.Get(1), "0")
gtest.Assert(m.Get(2), "2")
data := m.Map()
gtest.Assert(data[1], "0")
gtest.Assert(data[2], "2")
data[3] = "3"
gtest.Assert(m.Get(3), "3")
m.Set(4, "4")
gtest.Assert(data[4], "4")
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.Set(1, "0")
m.Set(2, "2")
t.Assert(m.Get(1), "0")
t.Assert(m.Get(2), "2")
data := m.Map()
t.Assert(data[1], "0")
t.Assert(data[2], "2")
data[3] = "3"
t.Assert(m.Get(3), "3")
m.Set(4, "4")
t.Assert(data[4], "4")
})
}
func Test_IntStrMap_MapCopy(t *testing.T) {
m := gmap.NewIntStrMap()
m.Set(1, "0")
m.Set(2, "2")
gtest.Assert(m.Get(1), "0")
gtest.Assert(m.Get(2), "2")
data := m.MapCopy()
gtest.Assert(data[1], "0")
gtest.Assert(data[2], "2")
data[3] = "3"
gtest.Assert(m.Get(3), "")
m.Set(4, "4")
gtest.Assert(data[4], "")
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.Set(1, "0")
m.Set(2, "2")
t.Assert(m.Get(1), "0")
t.Assert(m.Get(2), "2")
data := m.MapCopy()
t.Assert(data[1], "0")
t.Assert(data[2], "2")
data[3] = "3"
t.Assert(m.Get(3), "")
m.Set(4, "4")
t.Assert(data[4], "")
})
}
func Test_IntStrMap_FilterEmpty(t *testing.T) {
m := gmap.NewIntStrMap()
m.Set(1, "")
m.Set(2, "2")
gtest.Assert(m.Size(), 2)
gtest.Assert(m.Get(2), "2")
m.FilterEmpty()
gtest.Assert(m.Size(), 1)
gtest.Assert(m.Get(2), "2")
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.Set(1, "")
m.Set(2, "2")
t.Assert(m.Size(), 2)
t.Assert(m.Get(2), "2")
m.FilterEmpty()
t.Assert(m.Size(), 1)
t.Assert(m.Get(2), "2")
})
}
func Test_IntStrMap_Json(t *testing.T) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntStr{
1: "v1",
2: "v2",
@ -189,75 +236,111 @@ func Test_IntStrMap_Json(t *testing.T) {
m1 := gmap.NewIntStrMapFrom(data)
b1, err1 := json.Marshal(m1)
b2, err2 := json.Marshal(data)
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapIntStr{
1: "v1",
2: "v2",
}
b, err := json.Marshal(data)
gtest.Assert(err, nil)
t.Assert(err, nil)
m := gmap.NewIntStrMap()
err = json.Unmarshal(b, m)
gtest.Assert(err, nil)
gtest.Assert(m.Get(1), data[1])
gtest.Assert(m.Get(2), data[2])
t.Assert(err, nil)
t.Assert(m.Get(1), data[1])
t.Assert(m.Get(2), data[2])
})
}
func Test_IntStrMap_Pop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(g.MapIntStr{
1: "v1",
2: "v2",
})
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
k1, v1 := m.Pop()
gtest.AssertIN(k1, g.Slice{1, 2})
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 1)
t.AssertIN(k1, g.Slice{1, 2})
t.AssertIN(v1, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 1)
k2, v2 := m.Pop()
gtest.AssertIN(k2, g.Slice{1, 2})
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 0)
t.AssertIN(k2, g.Slice{1, 2})
t.AssertIN(v2, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 0)
gtest.AssertNE(k1, k2)
gtest.AssertNE(v1, v2)
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
})
}
func Test_IntStrMap_Pops(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(g.MapIntStr{
1: "v1",
2: "v2",
3: "v3",
})
gtest.Assert(m.Size(), 3)
t.Assert(m.Size(), 3)
kArray := garray.New()
vArray := garray.New()
for k, v := range m.Pops(1) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
for k, v := range m.Pops(2) {
gtest.AssertIN(k, g.Slice{1, 2, 3})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{1, 2, 3})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 0)
t.Assert(m.Size(), 0)
gtest.Assert(kArray.Unique().Len(), 3)
gtest.Assert(vArray.Unique().Len(), 3)
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
})
}
func TestIntStrMap_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Map *gmap.IntStrMap
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": []byte(`{"1":"v1","2":"v2"}`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "v1")
t.Assert(v.Map.Get(2), "v2")
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": g.MapIntAny{
1: "v1",
2: "v2",
},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get(1), "v1")
t.Assert(v.Map.Get(2), "v2")
})
}

View File

@ -17,126 +17,173 @@ import (
"github.com/gogf/gf/test/gtest"
)
func Test_ListMap_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewListMap()
func Test_ListMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.ListMap
m.Set("key1", "val1")
gtest.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Keys(), []interface{}{"key1"})
gtest.Assert(m.Get("key1"), "val1")
gtest.Assert(m.Size(), 1)
gtest.Assert(m.IsEmpty(), false)
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
gtest.Assert(m.Remove("key2"), "val2")
gtest.Assert(m.Contains("key2"), false)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
gtest.AssertIN("key3", m.Keys())
gtest.AssertIN("key1", m.Keys())
gtest.AssertIN("val3", m.Values())
gtest.AssertIN("val1", m.Values())
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
gtest.Assert(m.Size(), 0)
gtest.Assert(m.IsEmpty(), true)
m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
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()
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)
m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_ListMap_Set_Fun(t *testing.T) {
m := gmap.NewListMap()
m.GetOrSetFunc("fun", getValue)
m.GetOrSetFuncLock("funlock", getValue)
gtest.Assert(m.Get("funlock"), 3)
gtest.Assert(m.Get("fun"), 3)
m.GetOrSetFunc("fun", getValue)
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.GetOrSetFunc("fun", getValue)
m.GetOrSetFuncLock("funlock", getValue)
t.Assert(m.Get("funlock"), 3)
t.Assert(m.Get("fun"), 3)
m.GetOrSetFunc("fun", getValue)
t.Assert(m.SetIfNotExistFunc("fun", getValue), false)
t.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
})
}
func Test_ListMap_Batch(t *testing.T) {
m := gmap.NewListMap()
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
m.Removes([]interface{}{"key1", 1})
t.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
})
}
func Test_ListMap_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
gtest.C(t, func(t *gtest.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewListMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
m := gmap.NewListMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
t.Assert(expect[k], v)
return true
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
t.Assert(i, 2)
t.Assert(j, 1)
})
// 断言返回值对遍历控制
i := 0
j := 0
m.Iterator(func(k interface{}, v interface{}) bool {
i++
return true
})
m.Iterator(func(k interface{}, v interface{}) bool {
j++
return false
})
gtest.Assert(i, 2)
gtest.Assert(j, 1)
}
func Test_ListMap_Clone(t *testing.T) {
//clone 方法是深克隆
m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
gtest.AssertIN(1, m_clone.Keys())
gtest.C(t, func(t *gtest.T) {
//clone 方法是深克隆
m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
m_clone := m.Clone()
m.Remove(1)
//修改原 map,clone 后的 map 不影响
t.AssertIN(1, m_clone.Keys())
m_clone.Remove("key1")
//修改clone map,原 map 不影响
gtest.AssertIN("key1", m.Keys())
m_clone.Remove("key1")
//修改clone map,原 map 不影响
t.AssertIN("key1", m.Keys())
})
}
func Test_ListMap_Basic_Merge(t *testing.T) {
m1 := gmap.NewListMap()
m2 := gmap.NewListMap()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
gtest.C(t, func(t *gtest.T) {
m1 := gmap.NewListMap()
m2 := gmap.NewListMap()
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
})
}
func Test_ListMap_Order(t *testing.T) {
m := gmap.NewListMap()
m.Set("k1", "v1")
m.Set("k2", "v2")
m.Set("k3", "v3")
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Set("k1", "v1")
m.Set("k2", "v2")
m.Set("k3", "v3")
t.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
t.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
})
}
func Test_ListMap_FilterEmpty(t *testing.T) {
m := gmap.NewListMap()
m.Set(1, "")
m.Set(2, "2")
gtest.Assert(m.Size(), 2)
gtest.Assert(m.Get(2), "2")
m.FilterEmpty()
gtest.Assert(m.Size(), 1)
gtest.Assert(m.Get(2), "2")
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Set(1, "")
m.Set(2, "2")
t.Assert(m.Size(), 2)
t.Assert(m.Get(2), "2")
m.FilterEmpty()
t.Assert(m.Size(), 1)
t.Assert(m.Get(2), "2")
})
}
func Test_ListMap_Json(t *testing.T) {
// Marshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
@ -144,90 +191,126 @@ func Test_ListMap_Json(t *testing.T) {
m1 := gmap.NewListMapFrom(data)
b1, err1 := json.Marshal(m1)
b2, err2 := json.Marshal(gconv.Map(data))
gtest.Assert(err1, err2)
gtest.Assert(b1, b2)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
}
b, err := json.Marshal(gconv.Map(data))
gtest.Assert(err, nil)
t.Assert(err, nil)
m := gmap.NewListMap()
err = json.Unmarshal(b, m)
gtest.Assert(err, nil)
gtest.Assert(m.Get("k1"), data["k1"])
gtest.Assert(m.Get("k2"), data["k2"])
t.Assert(err, nil)
t.Assert(m.Get("k1"), data["k1"])
t.Assert(m.Get("k2"), data["k2"])
})
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
data := g.MapAnyAny{
"k1": "v1",
"k2": "v2",
}
b, err := json.Marshal(gconv.Map(data))
gtest.Assert(err, nil)
t.Assert(err, nil)
var m gmap.ListMap
err = json.Unmarshal(b, &m)
gtest.Assert(err, nil)
gtest.Assert(m.Get("k1"), data["k1"])
gtest.Assert(m.Get("k2"), data["k2"])
t.Assert(err, nil)
t.Assert(m.Get("k1"), data["k1"])
t.Assert(m.Get("k2"), data["k2"])
})
}
func Test_ListMap_Pop(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMapFrom(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
k1, v1 := m.Pop()
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 1)
t.AssertIN(k1, g.Slice{"k1", "k2"})
t.AssertIN(v1, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 1)
k2, v2 := m.Pop()
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
gtest.Assert(m.Size(), 0)
t.AssertIN(k2, g.Slice{"k1", "k2"})
t.AssertIN(v2, g.Slice{"v1", "v2"})
t.Assert(m.Size(), 0)
gtest.AssertNE(k1, k2)
gtest.AssertNE(v1, v2)
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
})
}
func Test_ListMap_Pops(t *testing.T) {
gtest.Case(t, func() {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMapFrom(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
})
gtest.Assert(m.Size(), 3)
t.Assert(m.Size(), 3)
kArray := garray.New()
vArray := garray.New()
for k, v := range m.Pops(1) {
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 2)
t.Assert(m.Size(), 2)
for k, v := range m.Pops(2) {
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
kArray.Append(k)
vArray.Append(v)
}
gtest.Assert(m.Size(), 0)
t.Assert(m.Size(), 0)
gtest.Assert(kArray.Unique().Len(), 3)
gtest.Assert(vArray.Unique().Len(), 3)
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
})
}
func TestListMap_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Map *gmap.ListMap
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": []byte(`{"1":"v1","2":"v2"}`),
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get("1"), "v1")
t.Assert(v.Map.Get("2"), "v2")
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(map[string]interface{}{
"name": "john",
"map": g.MapIntAny{
1: "v1",
2: "v2",
},
}, &v)
t.Assert(err, nil)
t.Assert(v.Name, "john")
t.Assert(v.Map.Size(), 2)
t.Assert(v.Map.Get("1"), "v1")
t.Assert(v.Map.Get("2"), "v2")
})
}

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