mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
1014 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a80091fef | |||
| 742653ce75 | |||
| a8c3d07d9f | |||
| df1ef5db78 | |||
| 4b15ab5e99 | |||
| cdc97e9b2b | |||
| bd84b97614 | |||
| d7eb1cca07 | |||
| 5856f74d83 | |||
| 563509c4a6 | |||
| d0753fa527 | |||
| 524e3bedc7 | |||
| 30b60754e3 | |||
| 88c49dc2cb | |||
| bb3dcc2622 | |||
| 5cca0640d9 | |||
| fb48ceeeee | |||
| 90f4bba8fd | |||
| 802187abc4 | |||
| 49dd17c047 | |||
| a3b94c24de | |||
| ac4485dc84 | |||
| 4d1e25cdab | |||
| ec00e99477 | |||
| a73dca3e50 | |||
| ed71d2b2ac | |||
| 3911c2e0a2 | |||
| 1b6765db50 | |||
| 345fb69521 | |||
| 83a6abc70f | |||
| cfef726205 | |||
| de0f9e728c | |||
| d9754a532b | |||
| dc51023c61 | |||
| 87ce5c5419 | |||
| c4fc9f9618 | |||
| 8d7aa5db83 | |||
| 9c70fe3be8 | |||
| 78027d2ec6 | |||
| d4e4b9addf | |||
| b2acd22f58 | |||
| ae5ecb5bfa | |||
| 6f1340ce36 | |||
| 473fdba14f | |||
| 93a01a1aaf | |||
| 7c51ff4707 | |||
| bb6883213f | |||
| 54395b39a6 | |||
| 6deb817fd0 | |||
| 482e093331 | |||
| 042f903157 | |||
| c423ad4830 | |||
| 958b109a12 | |||
| 113fffdd69 | |||
| 4aaf09fded | |||
| 2f3df76f37 | |||
| cf7706b16d | |||
| 36963c05a2 | |||
| 48d840a1b8 | |||
| e3ebc908bb | |||
| 9ed8d8c113 | |||
| 9eab5daf19 | |||
| 6a24b595f0 | |||
| 150f237f13 | |||
| 17233084f1 | |||
| 41f2138b39 | |||
| 6376b8aaa6 | |||
| 58362ad143 | |||
| d72d23c2eb | |||
| 7702c5bfde | |||
| 20f2a6c003 | |||
| 0d4c1c47d5 | |||
| 4d32733790 | |||
| 0e58b6e95b | |||
| 19bfc48dca | |||
| ae0cc5a4b6 | |||
| ba74e0beb2 | |||
| e5ca4e788e | |||
| 1c7f034135 | |||
| 4c78ce6e3f | |||
| 7803d557b3 | |||
| 204fea395c | |||
| bd13de2b39 | |||
| d0f649b328 | |||
| 04e42d2175 | |||
| ffc88eaaa7 | |||
| 4b4cc5ebb9 | |||
| 0e6cddb547 | |||
| a69aec9070 | |||
| d2bd37962e | |||
| eb6763b0fd | |||
| d330afdd36 | |||
| 5db4bbc186 | |||
| e00f2666ff | |||
| 0238cdd5ec | |||
| 2c34d96b9d | |||
| 65b3630f6d | |||
| 54b629561e | |||
| 285ad36e7d | |||
| 5adde275fc | |||
| a3fa10d820 | |||
| acf47f3907 | |||
| b4d5335e43 | |||
| 39fb842e9b | |||
| 7a5d86b44d | |||
| 397e11e124 | |||
| f4314c318e | |||
| 9bb5536163 | |||
| 2344701f22 | |||
| 3bb909ba4f | |||
| 8ae0bd148b | |||
| 72251b880a | |||
| fefde4c290 | |||
| 3b2bae6128 | |||
| ae559b57de | |||
| 6135085d61 | |||
| 40bdc76af1 | |||
| d4f982a9cf | |||
| 813841bb68 | |||
| 930e63e6b9 | |||
| ce40d139e7 | |||
| 8368e11827 | |||
| e6b4662ec2 | |||
| 28f83d3d32 | |||
| 3e33d66ab4 | |||
| 13248d6736 | |||
| 8bd24724e7 | |||
| 80248e9a6e | |||
| 2734903886 | |||
| 2451b40d3e | |||
| 9fb6227461 | |||
| a2b322a31b | |||
| 69e1628a0d | |||
| 6307af1096 | |||
| 6e4e1abf1e | |||
| 2c15aad0e7 | |||
| 152c472bc2 | |||
| 8f6f17c341 | |||
| f2fe084988 | |||
| f5ba665c59 | |||
| 9cc5d7a691 | |||
| 94adc50487 | |||
| 1c93c2b000 | |||
| 6bad31f1be | |||
| 24e2c7926e | |||
| e3be3bac92 | |||
| c72c7dbe9a | |||
| 52c17dfce0 | |||
| c01b520ba6 | |||
| 796774efe4 | |||
| 0baa52afee | |||
| 4c6d9f5eff | |||
| 99dd889ff0 | |||
| b89d8d2740 | |||
| cc1e340585 | |||
| 3f2ae3ba62 | |||
| e622ece861 | |||
| 9f2e69a9e6 | |||
| 9b02f5220a | |||
| 2dd2144dcd | |||
| a5f53f158a | |||
| 2c7a257b5a | |||
| f579c724c6 | |||
| ab9d7ed509 | |||
| 9524263803 | |||
| c27ddb1d79 | |||
| ce640048b8 | |||
| e222d92fb5 | |||
| f1ed9b31b6 | |||
| da0669c739 | |||
| 5679972d8d | |||
| 0aca2f0501 | |||
| 0fb32b63e7 | |||
| b38a4610eb | |||
| 59285709a6 | |||
| a304ca8f5b | |||
| 333e5b27aa | |||
| 5f4293a803 | |||
| fe80881ec9 | |||
| e1418a2caa | |||
| 68247acab1 | |||
| 6d05512c2f | |||
| bf6d6a29c0 | |||
| 353286fd84 | |||
| c801df497f | |||
| 93a648ba15 | |||
| 826b9b1b1f | |||
| e661b160d8 | |||
| 8b6d00b193 | |||
| 093034acd1 | |||
| 6f968a125e | |||
| 5c9cee7fb9 | |||
| 7676837337 | |||
| c5145dc4f6 | |||
| 91e29e23a4 | |||
| 1e309e570d | |||
| 8362322d63 | |||
| aefa54f04b | |||
| 2c5e6b379f | |||
| c20f3f0595 | |||
| 5b17108f71 | |||
| 1fee3eb5f0 | |||
| bc4d84b60f | |||
| 6a67dceca7 | |||
| 9c8a68f742 | |||
| 2d67d31f90 | |||
| c1b52e0f35 | |||
| 55a7c01f73 | |||
| 6cf48f9479 | |||
| f9905f2bb5 | |||
| c07c74bf1b | |||
| 14536e9abc | |||
| 73c68e48a1 | |||
| a9aa021914 | |||
| 2ae32ed2c2 | |||
| 8ee3793f8f | |||
| 6f5b0c393e | |||
| a4152347e5 | |||
| bb2dad6d5e | |||
| 92b791eb08 | |||
| 8365ce9d29 | |||
| e141b8e098 | |||
| c7ce8cf943 | |||
| 788ade2db0 | |||
| a9f332fdd6 | |||
| c6f1ae9426 | |||
| b12c909fd6 | |||
| 1a62f22a5b | |||
| 5c2574da7c | |||
| 9fbdb9712b | |||
| 7bc42e6eaa | |||
| b6b1bc8813 | |||
| a62d2589bc | |||
| e4069bdb93 | |||
| 1e100ac0ec | |||
| 9d865e4ac6 | |||
| b3b1418e11 | |||
| 3ab32faccc | |||
| 2eb09efc81 | |||
| dd2dfbf58d | |||
| d549311210 | |||
| 8a91592839 | |||
| 4d962c5aa5 | |||
| d9bd3153ea | |||
| 4991e14dff | |||
| 5629020538 | |||
| 361742c4a0 | |||
| 4272ac16c7 | |||
| 6c08d5fd81 | |||
| 2a9c20bfa2 | |||
| 8dab319a7f | |||
| 5d01c9fff3 | |||
| 020b6bde68 | |||
| 036bc03ebf | |||
| 86e70ad55c | |||
| bdf23ef48f | |||
| 820befa1a0 | |||
| 0d5b93bd07 | |||
| dbafe01064 | |||
| 51d9f7ff12 | |||
| 4beeeb92ac | |||
| 662f1ed6b7 | |||
| 05703ec3d7 | |||
| bfab4a4952 | |||
| 9d25e17fcb | |||
| 4828ddcdd7 | |||
| d25a3909d1 | |||
| 695d333d2f | |||
| 5f28adec36 | |||
| c893817e56 | |||
| 65785db659 | |||
| 85a7723b13 | |||
| ac827d3154 | |||
| 95b09aa5fe | |||
| 4e79b90863 | |||
| 306c02bcd4 | |||
| fc66a0715a | |||
| 9c7aecf0fd | |||
| 9f5a51e854 | |||
| a7f19e9e45 | |||
| 9b5862b61f | |||
| 176533e1a5 | |||
| c243637d44 | |||
| 7c52a6f9f6 | |||
| 446f8e7110 | |||
| 7db1cfa898 | |||
| 9a6aa01115 | |||
| 4a88e0255d | |||
| edc56949b7 | |||
| 63bc06d0fe | |||
| 9b58b66172 | |||
| 3517295e96 | |||
| c685876e6f | |||
| cb2c9c43a8 | |||
| 751a567e84 | |||
| 0a99bb9a7d | |||
| a2e7aec37f | |||
| 102e2d07d9 | |||
| 5400d22bc2 | |||
| 8d3fd21be5 | |||
| bb39ed136f | |||
| 363f6eba44 | |||
| 0ca305a1bf | |||
| 1d1e64b834 | |||
| 688e327f15 | |||
| 8c7ec0e7d9 | |||
| 84fef8dea3 | |||
| c1d2ad68b3 | |||
| a577605726 | |||
| 71444736ae | |||
| b7e41ec32c | |||
| 7fa09596b0 | |||
| 7316e6648f | |||
| e9d346ce4f | |||
| 4b91e709f7 | |||
| 7de89286da | |||
| 569a953b43 | |||
| 1e7f795c69 | |||
| d2ae383b83 | |||
| 8978112433 | |||
| 117eaea720 | |||
| 278e85357d | |||
| 60ec59fa4a | |||
| 0aa82ad020 | |||
| 7bd319ddc7 | |||
| 7ceb667486 | |||
| 80c4786afd | |||
| 2f741d3b24 | |||
| d2b65f0f91 | |||
| c226782f23 | |||
| 57a82ebcc0 | |||
| 416885a726 | |||
| 9b4d2d9172 | |||
| d4b2bf20bb | |||
| ce9a0555c5 | |||
| 5171250a9d | |||
| 80b629916a | |||
| ecaf0da228 | |||
| 8c0a905a9f | |||
| 5a0326f666 | |||
| 790a651ac1 | |||
| 6f93bd17f2 | |||
| 3419d66c4b | |||
| cabf684ec9 | |||
| 2b6e6ce28e | |||
| 32101189a2 | |||
| 600c081801 | |||
| 0899a9d49a | |||
| c56f4eabca | |||
| bfe89e0b12 | |||
| 6cb38cfa92 | |||
| 09bb0c9397 | |||
| fa47b0306d | |||
| 55429ad589 | |||
| 9592fb099f | |||
| 18ec6116ad | |||
| 9d0f306684 | |||
| 3eba8d690f | |||
| c0b59007ce | |||
| 3485ba2a5d | |||
| 43ecfc7484 | |||
| 5ba53e56c9 | |||
| af1d14ace6 | |||
| c02bf715c5 | |||
| 0c0e902b07 | |||
| 750b53d7aa | |||
| 1d807c095a | |||
| 69b5873bf9 | |||
| 60fc9b6417 | |||
| 0bc8944a08 | |||
| 33292f54e0 | |||
| fc215ef0b2 | |||
| 65c67427d4 | |||
| bc8142974f | |||
| fadb7a8f8f | |||
| e1bfe90833 | |||
| 042dc0b33f | |||
| 8ca535dbf0 | |||
| e20183e7a1 | |||
| df86ffb61e | |||
| bfcf133c91 | |||
| 24a377d3a8 | |||
| baf51bc68f | |||
| 5f4b585164 | |||
| 17a11187b0 | |||
| 7caf7976cf | |||
| 1a31792c14 | |||
| d56eb49e41 | |||
| a1236b5e16 | |||
| 8f278be0dc | |||
| f8ab71e7f0 | |||
| 9cca3a3ec1 | |||
| 97bcf2a438 | |||
| 85dd2e9f4f | |||
| 23d62da87f | |||
| b4d947fecd | |||
| 650c95af31 | |||
| 943116d495 | |||
| 644df7c16e | |||
| 638773b216 | |||
| 889e7914e2 | |||
| 68cc85f2b2 | |||
| c273ce576b | |||
| f8d57096a8 | |||
| d7542e87ae | |||
| b178210a31 | |||
| 3e6a23b0e1 | |||
| ee8d2afe58 | |||
| 11e102e137 | |||
| e06b62ecf2 | |||
| d178102f82 | |||
| e1dd5cce7d | |||
| 1edc1f35fb | |||
| 4df47be521 | |||
| 9cb88bca5a | |||
| fa8cc1d3f4 | |||
| 9ae8a7ca33 | |||
| f4da179140 | |||
| 13330658cb | |||
| 9f04e46166 | |||
| 1072ea3fb0 | |||
| ea9e8055a4 | |||
| 784abf2a30 | |||
| ed4a70deff | |||
| fef20d10a2 | |||
| 176dcdc7cc | |||
| 12ed05f846 | |||
| 5999f22f76 | |||
| c056fd2a06 | |||
| cb422f043e | |||
| 4ae89dc9f6 | |||
| 4f6f07db1d | |||
| cd981c7294 | |||
| 557d2967fa | |||
| a7a70636dd | |||
| 2215661f89 | |||
| a22b590b43 | |||
| 1b0b209662 | |||
| 2a2761c54f | |||
| 1c83d72f39 | |||
| fcea774b59 | |||
| da2ec21571 | |||
| ebb8d8a2f7 | |||
| 9706a9c768 | |||
| cee67a8d4e | |||
| 87650557fd | |||
| d3bf52f12f | |||
| 6b13a4849b | |||
| 8e380c0d9d | |||
| 0caf4bfcec | |||
| 9c3b978b50 | |||
| ab689a7792 | |||
| 846646d92d | |||
| 2eb2b89432 | |||
| 43441a8218 | |||
| 561a541fa1 | |||
| ffe9ecc141 | |||
| 77f7884604 | |||
| 0a203d1e22 | |||
| f4f4550483 | |||
| e87226a092 | |||
| 391a3ec9bd | |||
| dd5cd31ef5 | |||
| de92e804fe | |||
| 7725d9aaaf | |||
| bd3e25adea | |||
| 0b1d49af4b | |||
| 7efa9e351e | |||
| 0b0141954b | |||
| 82b531fbfb | |||
| 67fb626339 | |||
| 9044d5f05d | |||
| 47663aa1f1 | |||
| 63c0aab19c | |||
| 261216f5e4 | |||
| f88b799d67 | |||
| 9c48dd172c | |||
| 09ce105eee | |||
| 651aa895f8 | |||
| 0509e41198 | |||
| 1b40d6a53a | |||
| f9189d48d1 | |||
| 849874a247 | |||
| 3f6510bae7 | |||
| a585a26c39 | |||
| 9943966a86 | |||
| 3617e51c01 | |||
| c931032f08 | |||
| 37b286eaa4 | |||
| 619287c273 | |||
| aeb9b68298 | |||
| bae8f6315b | |||
| a6f70f8935 | |||
| acca6f4009 | |||
| 727fdd2391 | |||
| 1d174e00c0 | |||
| a5e3e2f5ba | |||
| da43c2d52f | |||
| 262f27748c | |||
| a29aef7e1c | |||
| 1671391195 | |||
| 28b0d59c61 | |||
| 86433cef25 | |||
| e96ccd5f71 | |||
| 50a087bb3d | |||
| 1ec049c52f | |||
| ec38805542 | |||
| 4c8517d075 | |||
| edf06da6ea | |||
| eb43a2040e | |||
| 9d0ecc7d3e | |||
| ad943c5e02 | |||
| bdb4fd0d25 | |||
| 2440e05457 | |||
| 1337c6c0d1 | |||
| 957689e07c | |||
| 3952d74f87 | |||
| 6dc4b81693 | |||
| 9cd953b7be | |||
| 631810dda2 | |||
| 8c12bc5506 | |||
| d4091a4826 | |||
| a7c269886b | |||
| f54593037b | |||
| 0415cf6a08 | |||
| 87c22d32b0 | |||
| 27dd15b403 | |||
| dd452c19ce | |||
| acc0846cf3 | |||
| 5a6738841f | |||
| bea451c9d6 | |||
| 676e904ec6 | |||
| 49aa5c61bc | |||
| a2272b852c | |||
| 1fa77630f9 | |||
| a841c4cc05 | |||
| 1874808e3b | |||
| 149b67916b | |||
| 7fb6f58162 | |||
| a309114a18 | |||
| 35cbde9530 | |||
| b9211b182a | |||
| 81ec499ae9 | |||
| f2e276eabd | |||
| f6dbaba1f8 | |||
| 65dcff052a | |||
| e3861567c7 | |||
| e89a20c725 | |||
| a8acc6bd28 | |||
| eb5efc735e | |||
| 737af527cd | |||
| 875d2b7e63 | |||
| bf1cb0e1bd | |||
| 2bfeb1b06c | |||
| 820e4302b7 | |||
| 84d761b418 | |||
| e5e27f2ac4 | |||
| efca9b18a8 | |||
| 607821ecbc | |||
| 7cc1b239d4 | |||
| efa8de34da | |||
| bcf45e3c5a | |||
| 0a3cd1d2ab | |||
| 3e621856c8 | |||
| 32e4d64ddb | |||
| 46e45ca84b | |||
| fcb13bd8ee | |||
| eacad9b453 | |||
| ed479e2a13 | |||
| 19937cb75d | |||
| a95093222c | |||
| 7b599d1882 | |||
| 4d501fd2f4 | |||
| c3d3053ded | |||
| 14d5fd3e11 | |||
| 9f79453334 | |||
| c7f1c881c0 | |||
| c39c032b4e | |||
| fa96c18308 | |||
| d90145a47f | |||
| 4871f86346 | |||
| 919eaf1e9a | |||
| 8a84ca16d1 | |||
| 12b4fdd692 | |||
| 3d8451d5d0 | |||
| cf88f28519 | |||
| 15d99eee46 | |||
| 6d68277db8 | |||
| 3e3b5557f7 | |||
| ba1a9d9f8e | |||
| 3e1a7953ec | |||
| 073fb2d717 | |||
| 7f33021184 | |||
| b396096721 | |||
| 0a5c6d832f | |||
| d279566114 | |||
| 2693cbb136 | |||
| f7a9be4292 | |||
| a926033b66 | |||
| dbcdd06b19 | |||
| ff5dab5c70 | |||
| 84b576418f | |||
| 253b124903 | |||
| 33dc5ddf79 | |||
| a456fa537c | |||
| 9e1fb93e08 | |||
| 5d24f702be | |||
| ca8e2c2986 | |||
| 2f0e5a45c5 | |||
| c4b28b0bc4 | |||
| 6cc4747965 | |||
| 91cd34b26a | |||
| e35a2f028c | |||
| 9cff2bd10f | |||
| 6d406498db | |||
| 939ae37baf | |||
| 4d6c8744e5 | |||
| e252d8b740 | |||
| 9b8d63e21b | |||
| 437fc04620 | |||
| 04dee090a3 | |||
| 245c6d24a1 | |||
| e92fd05f9f | |||
| 937f8e6919 | |||
| f489e6273e | |||
| 4f99bdbc87 | |||
| 1250b33220 | |||
| b57aee4595 | |||
| d9da51933d | |||
| 9fca93e7d8 | |||
| 854b2ed185 | |||
| cfac03bc40 | |||
| a3cb4a6ae8 | |||
| 2798fa4444 | |||
| 8bac0614f5 | |||
| 646280a6a9 | |||
| 208bdffdf7 | |||
| 9e7291903f | |||
| d56835fc00 | |||
| 1d5e717a80 | |||
| 2f44d9ae18 | |||
| 0627ab81d6 | |||
| 8167a398fc | |||
| 6291751014 | |||
| ee5ddaab52 | |||
| 207476be1f | |||
| b9b470c2ae | |||
| 52b6e8ef9d | |||
| 48c84bf74a | |||
| 5be30b3684 | |||
| 54a2b13825 | |||
| d44ddae3dc | |||
| 7bbc2459ba | |||
| 0cb77caa2a | |||
| 3f5f76458d | |||
| 835e07e8de | |||
| 3fc5e43abe | |||
| 9c8cb26bd6 | |||
| 540f4d2d0c | |||
| 51be255821 | |||
| 7d278fea25 | |||
| ca72d3b23a | |||
| 534cd3be1c | |||
| 78536de1b5 | |||
| edc67d9ec3 | |||
| 4dd12434b7 | |||
| f654bb2eda | |||
| 205f98cfeb | |||
| 69fa5bf464 | |||
| e7dc58ac6c | |||
| 2033299632 | |||
| 639d34d5d9 | |||
| 605181da32 | |||
| 183f631190 | |||
| 64c99b9871 | |||
| d29b0a27ff | |||
| aaa726e6dc | |||
| 05cc0c4644 | |||
| e1c0a92e60 | |||
| c770e4779a | |||
| c135122ca1 | |||
| 1d87df2afe | |||
| 8efc0ca0ea | |||
| 001d524ff7 | |||
| 5c5dce9dc3 | |||
| 293256c2ca | |||
| 4e027c1de3 | |||
| 6712a33164 | |||
| 98531143a6 | |||
| 71127ba308 | |||
| 0f6f571ccb | |||
| 9b6936a4fb | |||
| 8b78609412 | |||
| 4a31082f8c | |||
| 3643a69d8d | |||
| adbaa4aa89 | |||
| 0e025eda1b | |||
| 7c44bf8e94 | |||
| e6718f1113 | |||
| f3f6adb03a | |||
| 977827e453 | |||
| bcc9153991 | |||
| c04be14cd9 | |||
| de8f29751d | |||
| 12d58e4d08 | |||
| 3ae44185f4 | |||
| 1290f42f75 | |||
| 17b91dcad7 | |||
| 72da1642ee | |||
| 76d93b3a61 | |||
| 7be0ee9566 | |||
| 326e1f8da5 | |||
| e4e44ddd19 | |||
| 46bdde9265 | |||
| 09eba58927 | |||
| 0e884c78f5 | |||
| 2f44721086 | |||
| 6e08eebcbe | |||
| d9422d00ac | |||
| efc0501548 | |||
| bb07c60838 | |||
| 330ea05ca3 | |||
| 1f534b48a3 | |||
| aceae5eee3 | |||
| d4d66fd529 | |||
| b7686d6d37 | |||
| b97e397354 | |||
| ab8fbf171e | |||
| 2c0cfa24b0 | |||
| f28a76d470 | |||
| f3525c84a3 | |||
| abe9b54112 | |||
| b2aa59d893 | |||
| 9e9865afa7 | |||
| ecc439d4e8 | |||
| 452f0fc99e | |||
| 54f47845f6 | |||
| cb238cd6d9 | |||
| 897a9f4584 | |||
| 55f9b121de | |||
| 386f38af5e | |||
| 65cea430c2 | |||
| 4d38b508a3 | |||
| 5c774fd391 | |||
| f6d760e90f | |||
| c3ffa40bad | |||
| 5ce5d0e593 | |||
| b83f1efde8 | |||
| ca5f14c366 | |||
| 95a8b51fb4 | |||
| 2b64979730 | |||
| 508cb7db88 | |||
| e8d6d96883 | |||
| 9378a6e7bf | |||
| 1d609fc5c7 | |||
| 49d1e3dbaf | |||
| b0e9334852 | |||
| 95bd369959 | |||
| a433890097 | |||
| c9c3be517c | |||
| 89e122cd31 | |||
| 27c2a03ea8 | |||
| b56eb3a948 | |||
| fb1b0bfd88 | |||
| dd10167ec2 | |||
| 3138ad10ec | |||
| 080ca82605 | |||
| 0290de2360 | |||
| 9b330adc1d | |||
| f3ff1ae08b | |||
| 4f699af051 | |||
| 7ebc7259cb | |||
| 9d22edbdb8 | |||
| 7e95058cb5 | |||
| f33753e0cd | |||
| ac71e1d753 | |||
| 8151b6efd6 | |||
| 94de306c93 | |||
| 51f8ea26de | |||
| 29d9bb17cd | |||
| 30501a882d | |||
| f2f98e1d16 | |||
| cbf465eeea | |||
| 79c400e912 | |||
| cc4c49b5b0 | |||
| 96ea2c911d | |||
| 131b11680a | |||
| 3c6ab96283 | |||
| d2c4fa921a | |||
| dbb51a9328 | |||
| 55bfc3fa73 | |||
| d9c5182d1a | |||
| 899fcbf2da | |||
| 6e2c0d8706 | |||
| c4e599ce63 | |||
| 9cad9e4467 | |||
| 48019444ea | |||
| 76d31a7fbb | |||
| facb9d93c0 | |||
| 1d6bd46c5e | |||
| 8646dc1825 | |||
| 2992bdeed7 | |||
| c5601e6c88 | |||
| ef1d9a561c | |||
| 2d3b32c94a | |||
| 269378aa0d | |||
| 6889542801 | |||
| 8e6f1f1740 | |||
| b6ab1a992c | |||
| a5a267567c | |||
| f05f855c07 | |||
| bc5f773ba6 | |||
| 55308cc37c | |||
| 4d9db6edf0 | |||
| 38111a64e3 | |||
| bd8d3fca08 | |||
| 89ccaa3915 | |||
| 7c2cff7d99 | |||
| 788e15dbb6 | |||
| a0172d9d7e | |||
| bc1a7a1644 | |||
| c98234d3e6 | |||
| 45a94d23d5 | |||
| 351de5ee6a | |||
| 3559436d1e | |||
| 189f4c1637 | |||
| caead810e2 | |||
| ebdc5d9c9d | |||
| 4164059211 | |||
| bd27258c46 | |||
| ddf0605bc4 | |||
| 991dbe50e0 | |||
| 8050efb835 | |||
| 9355bc73a2 | |||
| acc2a6a353 | |||
| 09e83e7b8d | |||
| c0df8a3d80 | |||
| 33e890d225 | |||
| 750e5df962 | |||
| 74c65439fc | |||
| f290bd7170 | |||
| d398b749d4 | |||
| 2fd5a1574a | |||
| a7504f0629 | |||
| 3c35bb85ee | |||
| 6621edb04c | |||
| b0c722e297 | |||
| 47a6284274 | |||
| 150b8edb70 | |||
| aa5d3285eb | |||
| 993d6e3076 | |||
| 78ad9d8c2d | |||
| feff3ddce3 | |||
| 80fddad64d | |||
| 1e6dd0be02 | |||
| 22ecf4f1b6 | |||
| 5d21148657 | |||
| edb745ed92 | |||
| 61f4e0da6f | |||
| 767afa3106 | |||
| 16779359ee | |||
| 770653fafa | |||
| 0c021b6da7 | |||
| ec92b08f25 | |||
| 13e2353729 | |||
| 90d19a84ce | |||
| b4c0b95d8a | |||
| b7e8255066 | |||
| 5f8e6ad9ed | |||
| c8d253eb56 | |||
| 4814624cff | |||
| cc67f3d388 | |||
| f7c2a51c9f | |||
| 3db83e1159 | |||
| 3bb002796c | |||
| 45170bc53e | |||
| b79ff84c6f | |||
| 938c46fec9 | |||
| 8a13d94526 | |||
| 1e844d505a | |||
| a123a2c086 | |||
| 1eeeeb853e | |||
| 6e7224e306 | |||
| 9e064e2651 | |||
| 8d9dd17eac | |||
| cf1d3d3d2b | |||
| 9480ffcdc0 | |||
| 5db10add4a | |||
| fa66bf5d9d | |||
| f69da3ace1 | |||
| e01bfa05c3 | |||
| 231238c157 | |||
| 7edec099ab | |||
| 83eb8be064 | |||
| 7af30df494 | |||
| f026686fda | |||
| 35a50b9c6c | |||
| 010e2f951a | |||
| f7f86ad65a | |||
| 1e19f447d1 | |||
| 8cc378331d | |||
| 4721f68fd8 | |||
| 0d11c0a1f8 | |||
| 5076613a8f | |||
| c5a44daa65 | |||
| 520970b71f | |||
| ebfb08ee3f | |||
| 9e38b2cb90 | |||
| 71b1f00dc5 | |||
| 59d6830ab1 | |||
| 8779a3f211 | |||
| c10149baa0 | |||
| 4f87668780 | |||
| 63f33d1d8c | |||
| 734aa5a6fe | |||
| 371aef224d | |||
| 362e380ada | |||
| e995bd8c9a | |||
| 81b211dd1a | |||
| 0515fc94cb | |||
| 46ee070f0a | |||
| b17e3a6804 | |||
| 9160bee1af | |||
| 8e6018cfff | |||
| c08739b5a3 | |||
| 385a503d31 | |||
| ef286b0c15 | |||
| 53aba2d4b8 | |||
| f22da4ba3a | |||
| 23c2f12672 | |||
| 7fd53673ce | |||
| e64fd088b9 | |||
| 15672e7a09 | |||
| 2ea1d2c7b2 | |||
| 90d751f98d | |||
| 25a91c732c | |||
| 01995f5501 | |||
| 68abb3cf3d | |||
| 77cc323d0e | |||
| c7a9c03495 | |||
| f7850e3ed3 | |||
| 82125416a2 | |||
| 2c1e2155e3 | |||
| 2d30a53c3a | |||
| ccceeae29c | |||
| f22b98456f | |||
| e7f1bd692b | |||
| f82f7ab199 | |||
| 4d33b527b6 | |||
| 7bcc596308 | |||
| be2d4b080e | |||
| c96e5f5a9e | |||
| 3c23766674 | |||
| 718089fc11 | |||
| 8ab44dcb44 | |||
| 992522342c | |||
| 040898cdc3 | |||
| 343126ef22 | |||
| 05760d1eac | |||
| c10f73f1f7 | |||
| 7e0fa8e0cd | |||
| 6fe6218505 | |||
| 6059782de8 | |||
| 4844eea0ab | |||
| 8ecd62d3de | |||
| 4c610b4f58 | |||
| 1932c4ec44 | |||
| bd2e51ddca | |||
| ddcb7121c1 | |||
| f1f575fd5c | |||
| 99adb7cdc4 | |||
| 6b7ea97777 | |||
| 495b5758ec | |||
| ba56eb87b1 | |||
| 4258a3bbc9 | |||
| 4912331ddc | |||
| e87b7ecf5d | |||
| 23bce7bde6 | |||
| 926b664615 | |||
| fa8257c85b | |||
| abb9c88c23 | |||
| f89976cad5 | |||
| 0389778725 | |||
| 3c36285126 | |||
| 75054ee109 | |||
| 8447b1a42b | |||
| e5265a1c46 | |||
| 6e8ef8d0b0 | |||
| 75c081afc9 | |||
| 060fd9eaba | |||
| 63e5a60344 | |||
| 75dc1d82c1 | |||
| a5e048eb5f | |||
| 65bc1d5eb8 | |||
| bfa64705b5 | |||
| 17aea8d7d4 | |||
| a6a01fd7f2 | |||
| 41a9e91b4c | |||
| e2c1e11f95 | |||
| c2966817ce | |||
| 16958413bb | |||
| c0a0913d4b | |||
| 6d47810782 | |||
| 7a9ea2e546 | |||
| 7881b2dee4 | |||
| 5f223ef049 | |||
| e57942b374 | |||
| f18e6f078c | |||
| f667cbc2a2 | |||
| 07e65c14a9 | |||
| 0b6d04485e | |||
| 36401a063d | |||
| 849e7370d1 | |||
| 6174097a07 |
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,112 +8,69 @@ package driver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"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.Core
|
||||
*gdb.DriverMysql
|
||||
}
|
||||
|
||||
// Open creates and returns a underlying sql.DB object for mysql.
|
||||
func (d *MyDriver) Open(config *gdb.ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
|
||||
)
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
if db, err := sql.Open("mysql", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
return nil, err
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// getChars returns the security char for this type of database.
|
||||
func (d *MyDriver) GetChars() (charLeft string, charRight string) {
|
||||
return "`", "`"
|
||||
// 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
|
||||
}
|
||||
|
||||
// handleSqlBeforeExec handles the sql before posts it to database.
|
||||
func (d *MyDriver) HandleSqlBeforeExec(sql string) string {
|
||||
return sql
|
||||
}
|
||||
|
||||
// Tables retrieves and returns the tables of current schema.
|
||||
func (d *MyDriver) Tables(schema ...string) (tables []string, err error) {
|
||||
var result gdb.Result
|
||||
link, err := d.DB.GetSlave(schema...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = d.DB.DoGetAll(link, `SHOW TABLES`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, m := range result {
|
||||
for _, v := range m {
|
||||
tables = append(tables, v.String())
|
||||
}
|
||||
}
|
||||
// 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...)
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
gtime.TimestampMilli()-tsMilli,
|
||||
gtime.Now(),
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// gdb.TableFields retrieves and returns the fields information of specified table of current schema.
|
||||
//
|
||||
// Note that it returns a map containing the field name and its corresponding fields.
|
||||
// As a map is unsorted, the gdb.TableField struct has a "Index" field marks its sequence in the fields.
|
||||
//
|
||||
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
|
||||
func (d *MyDriver) TableFields(table string, schema ...string) (fields map[string]*gdb.TableField, err error) {
|
||||
table = gstr.Trim(table)
|
||||
if gstr.Contains(table, " ") {
|
||||
panic("function gdb.TableFields supports only single table operations")
|
||||
}
|
||||
checkSchema := d.DB.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v := d.DB.GetCache().GetOrSetFunc(
|
||||
fmt.Sprintf(`mysql_table_fields_%s_%s`, table, checkSchema),
|
||||
func() interface{} {
|
||||
var result gdb.Result
|
||||
var link *sql.DB
|
||||
link, err = d.DB.GetSlave(checkSchema)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result, err = d.DB.DoGetAll(
|
||||
link,
|
||||
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.DB.QuoteWord(table)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
fields = make(map[string]*gdb.TableField)
|
||||
for i, m := range result {
|
||||
fields[m["Field"].String()] = &gdb.TableField{
|
||||
Index: i,
|
||||
Name: m["Field"].String(),
|
||||
Type: m["Type"].String(),
|
||||
Null: m["Null"].Bool(),
|
||||
Key: m["Key"].String(),
|
||||
Default: m["Default"].Val(),
|
||||
Extra: m["Extra"].String(),
|
||||
Comment: m["Comment"].String(),
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}, 0)
|
||||
if err == nil {
|
||||
fields = v.(map[string]*gdb.TableField)
|
||||
}
|
||||
// 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...)
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
gtime.TimestampMilli()-tsMilli,
|
||||
gtime.Now(),
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@ -512,16 +512,6 @@ func mapToStruct() {
|
||||
}
|
||||
}
|
||||
|
||||
// getQueriedSqls
|
||||
func getQueriedSqls() {
|
||||
for k, v := range db.GetQueriedSqls() {
|
||||
fmt.Println(k, ":")
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
db.PingMaster()
|
||||
|
||||
@ -1,8 +1,18 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
# MySQL.
|
||||
[database]
|
||||
# debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
[database.logger]
|
||||
Level = "all"
|
||||
Stdout = true
|
||||
CtxKeys = ["Trace-Id"]
|
||||
[database.default]
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
debug = true
|
||||
|
||||
# Redis.
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
#[database]
|
||||
# [[database.default]]
|
||||
|
||||
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
@ -11,11 +10,19 @@ func main() {
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
r, e := db.Table("test").Order("id asc").All()
|
||||
r, e := db.GetAll("SELECT * from `user` where id in(?)", g.Slice{})
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
if r != nil {
|
||||
fmt.Println(r.List())
|
||||
fmt.Println(r)
|
||||
}
|
||||
return
|
||||
//r, e := db.Table("user").Where("id in(?)", g.Slice{}).All()
|
||||
//if e != nil {
|
||||
// fmt.Println(e)
|
||||
//}
|
||||
//if r != nil {
|
||||
// fmt.Println(r.List())
|
||||
//}
|
||||
}
|
||||
|
||||
24
.example/database/gdb/mysql/gdb_batch_insert.go
Normal file
24
.example/database/gdb/mysql/gdb_batch_insert.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
list := make(g.List, 0)
|
||||
for i := 0; i < 100; i++ {
|
||||
list = append(list, g.Map{
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
}
|
||||
r, e := db.Table("user").Data(list).Batch(2).Insert()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
if r != nil {
|
||||
fmt.Println(r.LastInsertId())
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -10,7 +11,7 @@ func main() {
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "123456",
|
||||
Pass: "12345678",
|
||||
Name: "test",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
@ -20,19 +21,20 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
//db.GetCache().SetAdapter(adapter.NewRedis(g.Redis()))
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
// 执行2次查询并将查询结果缓存3秒,并可执行缓存名称(可选)
|
||||
for i := 0; i < 2; i++ {
|
||||
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
|
||||
gutil.Dump(r.ToMap())
|
||||
for i := 0; i < 3; i++ {
|
||||
r, _ := db.Table("user").Cache(3000*time.Second).Where("id=?", 1).One()
|
||||
gutil.Dump(r.Map())
|
||||
}
|
||||
|
||||
// 执行更新操作,并清理指定名称的查询缓存
|
||||
db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("uid=?", 1).Update()
|
||||
//db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("id=?", 1).Update()
|
||||
|
||||
// 再次执行查询,启用查询缓存特性
|
||||
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
|
||||
gutil.Dump(r.ToMap())
|
||||
//r, _ := db.Table("user").Cache(300000*time.Second, "vip-user").Where("id=?", 1).One()
|
||||
//gutil.Dump(r.Map())
|
||||
}
|
||||
|
||||
14
.example/database/gdb/mysql/gdb_ctx.go
Normal file
14
.example/database/gdb/mysql/gdb_ctx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "123456789")
|
||||
_, err := g.DB().Ctx(ctx).Query("SELECT 1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
14
.example/database/gdb/mysql/gdb_ctx_model.go
Normal file
14
.example/database/gdb/mysql/gdb_ctx_model.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "123456789")
|
||||
_, err := g.DB().Model("user").Ctx(ctx).All()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.DB().Model("user").Distinct().CountColumn("uid,name")
|
||||
}
|
||||
@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"time"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -18,10 +18,9 @@ func main() {
|
||||
|
||||
db.SetDebug(true)
|
||||
|
||||
type User struct {
|
||||
CreateTime time.Time `orm:"create_time"`
|
||||
}
|
||||
r, e := db.Table("user").Data(User{CreateTime: time.Now()}).Insert()
|
||||
r, e := db.Table("user").Data(g.Map{
|
||||
"create_at": "now()",
|
||||
}).Unscoped().Insert()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
@ -8,9 +8,6 @@ import (
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetMaxIdleConnCount(10)
|
||||
db.SetMaxOpenConnCount(10)
|
||||
db.SetMaxConnLifetime(time.Minute)
|
||||
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
@ -19,7 +16,7 @@ func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
go db.Table("user").All()
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
27
.example/database/gdb/mysql/gdb_transaction.go
Normal file
27
.example/database/gdb/mysql/gdb_transaction.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.Begin(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
|
||||
if err = tx.Rollback(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
|
||||
if err = tx.Commit(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal file
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
err error
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
if err = db.Transaction(func(tx *gdb.TX) error {
|
||||
// Nested transaction 1.
|
||||
if err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
// Nested transaction 2, panic.
|
||||
if err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
|
||||
// Create a panic that can make this transaction rollback automatically.
|
||||
panic("error")
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal file
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
err error
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
_ = tx.Rollback()
|
||||
}
|
||||
}()
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.SavePoint("MyPoint"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 3, "name": "green"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.RollbackTo("MyPoint"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
|
||||
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
|
||||
one, err := g.DB().Table("carlist c").
|
||||
LeftJoin("cardetail d", "c.postid=d.carid").
|
||||
Where("c.postid", "142039140032006").
|
||||
Fields("c.*,d.*").One()
|
||||
fmt.Println(err)
|
||||
g.Dump(one)
|
||||
}
|
||||
|
||||
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal file
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal file
@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScore struct {
|
||||
gmeta.Meta `orm:"table:user_score"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScore `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
db := g.DB()
|
||||
db.Transaction(func(tx *gdb.TX) error {
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
user := User{
|
||||
Name: fmt.Sprintf(`name_%d`, i),
|
||||
}
|
||||
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Detail.
|
||||
userDetail := UserDetail{
|
||||
Uid: int(lastInsertId),
|
||||
Address: fmt.Sprintf(`address_%d`, lastInsertId),
|
||||
}
|
||||
_, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
userScore := UserScore{
|
||||
Uid: int(lastInsertId),
|
||||
Score: j,
|
||||
}
|
||||
_, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal file
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScore struct {
|
||||
gmeta.Meta `orm:"table:user_score"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScore `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
db := g.DB()
|
||||
var user *User
|
||||
err := db.Model(user).WithAll().Where("id", 3).Scan(&user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.Dump(user)
|
||||
}
|
||||
@ -513,16 +513,6 @@ func mapToStruct() {
|
||||
}
|
||||
}
|
||||
|
||||
// getQueriedSqls
|
||||
func getQueriedSqls() {
|
||||
for k, v := range db.GetQueriedSqls() {
|
||||
fmt.Println(k, ":")
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
db.PingMaster()
|
||||
|
||||
@ -9,10 +9,11 @@ import (
|
||||
|
||||
// 使用原生gredis.New操作redis,但是注意需要自己调用Close方法关闭redis链接池
|
||||
func main() {
|
||||
redis := gredis.New(gredis.Config{
|
||||
config := &gredis.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 6379,
|
||||
})
|
||||
}
|
||||
redis := gredis.New(config)
|
||||
defer redis.Close()
|
||||
redis.Do("SET", "k", "v")
|
||||
v, _ := redis.Do("GET", "k")
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -2,25 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := gi18n.New()
|
||||
t.SetPath("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/i18n/gi18n/i18n")
|
||||
t.SetLanguage("en")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
t.SetLanguage("ja")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
t.SetLanguage("ru")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
fmt.Println(t.Translate(`hello`, "zh-CN"))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`, "zh-CN"))
|
||||
var (
|
||||
orderId = 865271654
|
||||
orderAmount = 99.8
|
||||
)
|
||||
fmt.Println(g.I18n().Tfl(`en`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
fmt.Println(g.I18n().Tfl(`zh-CN`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
|
||||
hello = "Hello"
|
||||
world = "World"
|
||||
OrderPaid = "You have successfully complete order #%d payment, paid amount: ¥%0.2f."
|
||||
@ -1,2 +1 @@
|
||||
hello = "你好"
|
||||
world = "世界"
|
||||
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
|
||||
@ -9,7 +9,7 @@ import (
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -8,8 +8,8 @@ import (
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
files := r.GetUploadFiles("file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
@ -15,10 +15,10 @@ func main() {
|
||||
r.Response.Writeln("end")
|
||||
})
|
||||
s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
glog.To(r.Response.Writer).Println("BeforeServe")
|
||||
},
|
||||
ghttp.HOOK_AFTER_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookAfterServe: func(r *ghttp.Request) {
|
||||
glog.To(r.Response.Writer).Println("AfterServe")
|
||||
},
|
||||
})
|
||||
|
||||
@ -3,15 +3,12 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
glog.Println(r.Header)
|
||||
r.Response.Write("hello world")
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.SetPort(8999)
|
||||
s.Run()
|
||||
|
||||
@ -12,7 +12,7 @@ func Order(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
})
|
||||
g.GET("/order", Order)
|
||||
|
||||
@ -11,10 +11,10 @@ func main() {
|
||||
p := "/:name/info/{uid}"
|
||||
s := g.Server()
|
||||
s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_BEFORE_SERVE) },
|
||||
ghttp.HOOK_AFTER_SERVE: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_AFTER_SERVE) },
|
||||
ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_BEFORE_OUTPUT) },
|
||||
ghttp.HOOK_AFTER_OUTPUT: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_AFTER_OUTPUT) },
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeServe) },
|
||||
ghttp.HookAfterServe: func(r *ghttp.Request) { glog.Println(ghttp.HookAfterServe) },
|
||||
ghttp.HookBeforeOutput: func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeOutput) },
|
||||
ghttp.HookAfterOutput: func(r *ghttp.Request) { glog.Println(ghttp.HookAfterOutput) },
|
||||
})
|
||||
s.BindHandler(p, func(r *ghttp.Request) {
|
||||
r.Response.Write("用户:", r.Get("name"), ", uid:", r.Get("uid"))
|
||||
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
// 多事件回调示例,事件1
|
||||
pattern1 := "/:name/info"
|
||||
s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.SetParam("uid", 1000)
|
||||
},
|
||||
})
|
||||
@ -22,7 +22,7 @@ func main() {
|
||||
// 多事件回调示例,事件2
|
||||
pattern2 := "/{object}/list/{page}.java"
|
||||
s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeOutput: func(r *ghttp.Request) {
|
||||
r.Response.SetBuffer([]byte(
|
||||
fmt.Sprintf("通过事件修改输出内容, object:%s, page:%s", r.Get("object"), r.GetRouterString("page"))),
|
||||
)
|
||||
|
||||
@ -7,10 +7,10 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/*any")
|
||||
})
|
||||
s.BindHookHandler("/v1/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/v1/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/v1/*")
|
||||
r.ExitHook()
|
||||
})
|
||||
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
r.Response.Writeln(r.Get("name"))
|
||||
})
|
||||
s.BindHookHandlerByMap("/", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.SetParam("name", "john")
|
||||
},
|
||||
})
|
||||
|
||||
@ -12,17 +12,17 @@ func main() {
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/:name")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/*any")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/show")
|
||||
},
|
||||
})
|
||||
|
||||
@ -27,10 +27,10 @@ func main() {
|
||||
})
|
||||
})
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook name")
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.EnablePProf()
|
||||
s := g.Server()
|
||||
s.Domain("localhost").EnablePProf()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Writeln("哈喽世界!")
|
||||
})
|
||||
|
||||
@ -6,12 +6,8 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetConfigWithMap(g.Map{"Graceful": true})
|
||||
s.EnableAdmin()
|
||||
//s.BindHookHandler("/admin/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
// if !r.BasicAuth("admin", "123", "") {
|
||||
// r.Exit()
|
||||
// }
|
||||
//})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.SetServerRoot("root")
|
||||
s.BindHookHandler("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
fmt.Println(r.URL.Path, r.IsFileRequest())
|
||||
})
|
||||
s.BindHandler("/template", func(r *ghttp.Request) {
|
||||
|
||||
20
.example/net/ghttp/server/router/duplicated/duplicated.go
Normal file
20
.example/net/ghttp/server/router/duplicated/duplicated.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/test", func(r *ghttp.Request) {
|
||||
r.Response.Writeln(1)
|
||||
})
|
||||
group.ALL("/test", func(r *ghttp.Request) {
|
||||
r.Response.Writeln(2)
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
@ -27,7 +27,7 @@ func main() {
|
||||
s := g.Server()
|
||||
obj := new(Object)
|
||||
s.Group("/api").Bind([]ghttp.GroupItem{
|
||||
{"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE},
|
||||
{"ALL", "*", HookHandler, ghttp.HookBeforeServe},
|
||||
{"ALL", "/handler", Handler},
|
||||
{"ALL", "/obj", obj},
|
||||
{"GET", "/obj/show", obj, "Show"},
|
||||
|
||||
@ -54,10 +54,10 @@ func main() {
|
||||
})
|
||||
})
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook name")
|
||||
})
|
||||
})
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
fmt.Println(r.Router)
|
||||
fmt.Println(r.Get("customer_id"))
|
||||
})
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gfcache"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := 0
|
||||
r := ""
|
||||
path := gfile.TempDir() + gfile.Separator + "temp"
|
||||
gfile.PutContents(path, "hello")
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
|
||||
gfile.PutContentsAppend(path, " john")
|
||||
|
||||
// 等待1秒以便gfsnotify回调能处理完成
|
||||
time.Sleep(time.Second)
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
}
|
||||
14
.example/os/glog/glog_ctx.go
Normal file
14
.example/os/glog/glog_ctx.go
Normal 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)
|
||||
}
|
||||
11
.example/os/glog/glog_level_prefix.go
Normal file
11
.example/os/glog/glog_level_prefix.go
Normal 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")
|
||||
}
|
||||
@ -13,7 +13,7 @@ func main() {
|
||||
key := "key"
|
||||
|
||||
// 第一次锁带时间
|
||||
gmlock.Lock(key, 1000)
|
||||
gmlock.Lock(key)
|
||||
glog.Println("lock1")
|
||||
// 这个时候上一次的计时解锁已失效
|
||||
gmlock.Unlock(key)
|
||||
|
||||
@ -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))
|
||||
}
|
||||
20
.example/os/gres/gres_example.go
Normal file
20
.example/os/gres/gres_example.go
Normal 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()
|
||||
}
|
||||
@ -2,19 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) Test() {
|
||||
c.View.Display("layout.html")
|
||||
}
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindControllerMethod("/", new(Controller), "Test")
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"header": "This is header",
|
||||
"container": "This is container",
|
||||
"footer": "This is footer",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "container"}}
|
||||
<h1>CONTAINER</h1>
|
||||
<h1>{{.container}}</h1>
|
||||
{{end}}
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "footer"}}
|
||||
<h1>FOOTER</h1>
|
||||
<h1>{{.footer}}</h1>
|
||||
{{end}}
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "header"}}
|
||||
<h1>HEADER</h1>
|
||||
<h1>{{.header}}</h1>
|
||||
{{end}}
|
||||
@ -2,14 +2,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>GoFrame Layout</title>
|
||||
{{template "header"}}
|
||||
{{template "header" .}}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{{template "container"}}
|
||||
{{template "container" .}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
{{template "footer"}}
|
||||
{{template "footer" .}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -9,11 +9,13 @@ func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/main1", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"name": "smith",
|
||||
"mainTpl": "main/main1.html",
|
||||
})
|
||||
})
|
||||
s.BindHandler("/main2", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"name": "john",
|
||||
"mainTpl": "main/main2.html",
|
||||
})
|
||||
})
|
||||
|
||||
@ -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
|
||||
@ -1,35 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/os/gtimer"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetList() {
|
||||
START:
|
||||
for {
|
||||
res, err := g.Redis().DoVar("RPOP", "mill")
|
||||
if err != nil {
|
||||
glog.Debug("Rpop:", err)
|
||||
break
|
||||
}
|
||||
glog.Debug(res)
|
||||
if res.IsEmpty() {
|
||||
glog.Debug("nil")
|
||||
continue START
|
||||
}
|
||||
interval := 50 * time.Second
|
||||
gtimer.AddOnce(interval, func() {
|
||||
glog.Debug("end------:", res, gtime.Now().Format("Y-m-d H:i:s"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
g.Redis().SetMaxActive(2)
|
||||
//g.Redis().SetMaxIdle(100)
|
||||
GetList()
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/demo", func(r *ghttp.Request) {
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/ajax", func(r *ghttp.Request) {
|
||||
page := r.GetPage(100, 10)
|
||||
page.AjaxActionName = "DoAjax"
|
||||
|
||||
@ -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,7 +21,7 @@ func wrapContent(page *gpage.Page) string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom1/*page", func(r *ghttp.Request) {
|
||||
page := r.GetPage(100, 10)
|
||||
content := wrapContent(page)
|
||||
|
||||
@ -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"
|
||||
@ -22,7 +22,7 @@ func pageContent(page *gpage.Page) string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom2/*page", func(r *ghttp.Request) {
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
13
.example/util/guid/guid.go
Normal file
13
.example/util/guid/guid.go
Normal 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))
|
||||
}
|
||||
}
|
||||
20
.example/util/guid/guid_length.go
Normal file
20
.example/util/guid/guid_length.go
Normal 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))
|
||||
}
|
||||
23
.example/util/guid/guid_unique.go
Normal file
23
.example/util/guid/guid_unique.go
Normal 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))
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
fmt.Println(1)
|
||||
gutil.Throw("error")
|
||||
fmt.Println(2)
|
||||
}, func(err interface{}) {
|
||||
}, func(err error) {
|
||||
fmt.Println(err)
|
||||
})
|
||||
}
|
||||
|
||||
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
13
.example/util/gvalid/gvalid_custom_message.go
Normal 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())
|
||||
}
|
||||
14
.example/util/gvalid/i18n/cn.toml
Normal file
14
.example/util/gvalid/i18n/cn.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,7 +6,7 @@
|
||||
.idea/
|
||||
.settings/
|
||||
.vscode/
|
||||
vender/
|
||||
vendor/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
@ -14,4 +14,4 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@ -1,10 +1,12 @@
|
||||
os: linux
|
||||
arch: arm64-graviton2
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- "1.15.x"
|
||||
- "1.16.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -13,7 +15,7 @@ branches:
|
||||
- staging
|
||||
|
||||
env:
|
||||
- GF_DEBUG=1 GO111MODULE=on
|
||||
- TZ=Asia/Shanghai GF_DEBUG=1 GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
73
DONATOR.MD
73
DONATOR.MD
@ -1,73 +0,0 @@
|
||||
# Donators
|
||||
|
||||
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
|
||||
|---|---|--- | ---
|
||||
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00 |
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥301.00 |
|
||||
|[mg91](https://gitee.com/mg91)|gitee|¥10.00 |
|
||||
|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00 |
|
||||
|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00 |
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00 |
|
||||
|[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|¥110.00 |
|
||||
|lah|wechat|¥100.00 |
|
||||
|x*z|wechat|¥20.00 |
|
||||
|潘兄|wechat|¥100.00 |
|
||||
|Fly的狐狸|wechat|¥100.00 |
|
||||
|全|alipay|¥100.00 |
|
||||
|东东|wechat|¥100.00 |
|
||||
|严宇轩|alipay|¥99.99 |
|
||||
|土豆相公|alipay|¥66.60 |
|
||||
|Hades|alipay|¥66.66 |
|
||||
|蔡蔡|wechat|¥666.00 | gf越来越强
|
||||
|上海金保证网络科技|bank|¥2000.00 |
|
||||
|[foxhack](https://github.com/foxhack)|wechat|¥20.00 |
|
||||
|*栈|wechat|¥5.00 |
|
||||
|*络|wechat|¥10.00|
|
||||
|M*e|wechat|¥20.00|
|
||||
|*G|wechat|¥10.00|
|
||||
|E*_|wechat|¥10.00|
|
||||
|A*y|wechat|¥1.00| 感谢大佬goframe
|
||||
|K*e|wechat|¥168.00| 感谢老大
|
||||
|*雨|wechat|¥100.00|
|
||||
|*洁|wechat|¥10.00|赞助你肥宅快乐水
|
||||
|R*s|wechat|¥18.88| 谢谢GF!辛苦了!
|
||||
|粟*e|wechat|¥50.00|
|
||||
|[李超](https://github.com/effortlee)|wechat|¥124.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|
|
||||
|*包|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|
|
||||
|米司特包|wechat|¥99.99|
|
||||
|金毛|alipay|¥100.00|
|
||||
|1*1x|wechat|¥100.00|
|
||||
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
70
README.MD
70
README.MD
@ -1,7 +1,7 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -9,13 +9,10 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, loose-coupled, full-featured and production-ready application development framework of golang,
|
||||
providing a series of core components and dozens of practical modules,
|
||||
such as: memcache, configure, validator, logging, array/queue/set/map containers,
|
||||
timer/timing tasks, file/memory lock, object pool, database ORM, etc,
|
||||
supporting web server integrated with router, cookie, session, middleware, logger,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework
|
||||
of Golang.
|
||||
|
||||
> 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
|
||||
```
|
||||
@ -31,42 +28,71 @@ require github.com/gogf/gf latest
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
|
||||
# Discussion
|
||||
- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX Group:Add friend`389961817` in WeChat, commenting `GF`
|
||||
- Issues:https://github.com/gogf/gf/issues
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community Package**
|
||||
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Performance
|
||||
|
||||
The `Web` component performance of `GoFrame`, please refer to third-party project: https://github.com/the-benchmarker/web-frameworks
|
||||
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: [https://goframe.org](https://goframe.org/display/gf)
|
||||
* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Part Of Users
|
||||
|
||||
- [Tencent](https://www.tencent.com/)
|
||||
- [ZTE](https://www.zte.com.cn/china/)
|
||||
- [Ant Financial Services](https://www.antfin.com/)
|
||||
- [MedLinker](https://www.medlinker.com/)
|
||||
- [KuCoin](https://www.kucoin.io/)
|
||||
- [LeYouJia](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [XiMaLaYa](https://www.ximalaya.com)
|
||||
- [ZYBang](https://www.zybang.com/)
|
||||
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://goframe.org/pages/viewpage.action?pageId=1114415).
|
||||
|
||||
|
||||
# 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)?
|
||||
If you love `GF`, why not [buy developer a cup of coffee](https://goframe.org/pages/viewpage.action?pageId=1115633)?
|
||||
|
||||
# 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>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/download/thumbnails/1114119/jetbrains.png" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://goframe.org/download/attachments/1114119/atlassian.jpg" height="120" alt="Atlassian"/></a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
74
README_ZH.MD
74
README_ZH.MD
@ -1,6 +1,6 @@
|
||||
# GoFrame
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -8,23 +8,21 @@
|
||||
|
||||
[English](README.MD) | 简体中文
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。
|
||||
实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
|
||||
如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
|
||||
配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
`GoFrame`是一款模块化、高性能、企业级的Go基础开发框架。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便易用,易于维护;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
* 模块化、松耦合
|
||||
* 模块丰富、开箱即用
|
||||
* 简洁易用、快速接入
|
||||
* 文档详尽、易于维护
|
||||
* 自顶向下、体系化设计
|
||||
* 统一框架、统一组件、降低选择成本
|
||||
* 开发规范、设计模式、代码分层模型
|
||||
* 强大便捷的开发工具链
|
||||
* 完善的本地中文化支持
|
||||
* 设计为团队及企业使用
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
@ -44,30 +42,52 @@ require github.com/gogf/gf latest
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 性能
|
||||
大家较为感兴趣的`Web`组件性能测试,请参考第三方性能测试评估:https://github.com/the-benchmarker/web-frameworks
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:[https://goframe.org](https://goframe.org)
|
||||
官方站点:[https://goframe.org](https://goframe.org/display/gf)
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
接口文档:[https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
# 帮助
|
||||
- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`Gorame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 用户
|
||||
|
||||
- [腾讯科技](https://www.tencent.com/)
|
||||
- [中兴通讯](https://www.zte.com.cn/china/)
|
||||
- [蚂蚁金服](https://www.antfin.com/)
|
||||
- [医联科技](https://www.medlinker.com/)
|
||||
- [库币科技](https://www.kucoin.io/)
|
||||
- [乐有家](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [喜马拉雅](https://www.ximalaya.com)
|
||||
- [作业帮](https://www.zybang.com/)
|
||||
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://goframe.org/pages/viewpage.action?pageId=1114415) 留言。
|
||||
|
||||
# 贡献
|
||||
|
||||
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
|
||||
@ -76,7 +96,7 @@ golang版本 >= 1.11
|
||||
|
||||
# 捐赠
|
||||
|
||||
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](DONATOR.MD)!
|
||||
如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://goframe.org/pages/viewpage.action?pageId=1115633) 吧!
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
# 赞助
|
||||
@ -84,4 +104,6 @@ golang版本 >= 1.11
|
||||
赞助支持`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/download/thumbnails/1114119/jetbrains.png" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://goframe.org/download/attachments/1114119/atlassian.jpg" height="120" alt="Atlassian"/></a>
|
||||
|
||||
|
||||
566
RELEASE.1.MD
566
RELEASE.1.MD
@ -1,566 +0,0 @@
|
||||
# `v1.8.0` (2019-07-15)
|
||||
|
||||
## 新功能改进
|
||||
1. 框架目前 `69` 个开发模块(不包括内部模块),原生代码 `65302` 行(不包含第三包依赖包),单元测试覆盖率达到`77%`;
|
||||
1. 新增`gerror`错误处理模块:https://goframe.org/errors/gerror/index
|
||||
1. 改进`gcharset`字符编码转换模块,支持更多的字符集:https://goframe.org/encoding/gcharset/index
|
||||
1. 新增`gmutex`模块,基于`channel`实现的高级互斥锁模块,支持更丰富的互斥锁特性:https://goframe.org/os/gmutex/index
|
||||
1. 改进`glog`日志模块:
|
||||
- 新增日志异步输出特性:https://goframe.org/os/glog/async
|
||||
- 新增`Flags`额外功能特性:https://goframe.org/os/glog/flags
|
||||
- 新增`Json`数据格式输出:https://goframe.org/os/glog/json
|
||||
- 新增自定义`Writer`接口特性:https://goframe.org/os/glog/writer
|
||||
- **修改`Backtrace`名称为`Stack`,并改进调用堆栈输出格式;**
|
||||
- 新增`Expose`方法暴露内部默认`Logger`对象;
|
||||
1. 改进`gdb`数据库ORM模块:
|
||||
- **改进错误处理,当数据库操作没有查询到数据时,`error`返回`sql.ErrNoRows`**:https://goframe.org/database/gdb/error
|
||||
- 改进`Update`/`Delete`方法支持`Order BY`及`LIMIT`特性;
|
||||
- 数据库链式操作及方法操作中,预处理变量参数支持`slice`参数:https://goframe.org/database/gdb/chaining/model
|
||||
- **修改`Priority`权重配置名称为`Weight`;**
|
||||
- 新增`Debug`配置,可配置开启/关闭调试特性:https://goframe.org/database/gdb/config
|
||||
- 新增`Offset`方法,该方法为可选链式操作方法,`pgsql`数据库可直接通过`Limit`方法第二个参数自动识别为`Offset`语法;
|
||||
- 改进数据库动态切换特性,支持不同数据库类型的当前操作数据库切换;
|
||||
- 改进简化配置文件结构:https://goframe.org/database/gdb/config
|
||||
1. 改进`gconv`数据转换模块:
|
||||
- 对结构体对象转换时支持更多的标签:`gconv/c/json`;
|
||||
- 支持`*struct/[]struct/[]*struct`自动初始化创建对象/数组:https://goframe.org/util/gconv/struct
|
||||
- 新增`Strusts/StrctsDeep`方法,用于结构体数组的递归转换;
|
||||
- 新增`StructDeep`方法,用于对结构体对象的递归转换;
|
||||
- 新增`MapDeep`方法,用于对结构体属性的递归转换;
|
||||
1. 改进`ghttp`模块:
|
||||
- 改进`ghttp`模块的分组路由功能,完善逻辑处理细节,程序更加稳健;
|
||||
- 改进`ghttp.Request.Get*ToStruct`方法,支持`params/param/p`标签,支持结构体递归转换,并且支持`**struct`参数的对象自动初始化;
|
||||
- 改进`ghttp.CORSDefault`的跨域设置参数,`AllowOrigin`参数调整为`*`;
|
||||
1. 改进`gvalid`数据校验模块:
|
||||
- 增加对校验标签`gvalid/valid/v`的支持;
|
||||
- 改进`CheckStruct`支持对结构体对象的递归校验:https://goframe.org/util/gvalid/checkstruct
|
||||
1. 改进`gtcp`TCP通信模块:
|
||||
- 改进通信包协议设计,更加轻量级高效:https://goframe.org/net/gtcp/conn/pkg
|
||||
- 改进`TCP Server`增加对`TLS`的支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加`Server.Cloce`服务端关闭方法;
|
||||
1. 改进`gproc`模块的通信数据结构,并使用`gtcp`的轻量级包协议重构消息发送逻辑;
|
||||
1. 改进`gqueue`模块增加数据同步缓冲机制,解决大数据量下的内存占用及延迟问题;
|
||||
1. 改进`gmlock`模块,使用`gmutex`模块替换内部的互斥锁,增加更多的操作方法;
|
||||
1. 改进`gaes`加密模块,增加`CBC`模式的加密/解密方法:
|
||||
1. 改进`garray.Range/SubSlice`方法,改进设计,提高性能;
|
||||
1. 改进`gjson`/`gparser`模块实现`MarshalJSON`接口以实现自定义的`JSON`数据格式转换;
|
||||
1. **改进`crypto`分类下模块的方法返回值,增加`error`错误变量返回,以保证更严谨的接口设计风格;**
|
||||
1. **改进`gbase64`模块,输入输出类型发生改变,并增加多个相关方法;**
|
||||
1. 改进`gflock`修改方法名`UnLock`为`Unlock`,新增`IsRLocked`方法;
|
||||
1. 新增`gfile.CopyFile/CopyDir`方法,用于文件及目录的复制;
|
||||
1. 改进`gjson/gparser/gvar/gcfg`模块增加更多的类型转换方法;
|
||||
1. 改进`gcache`模块,过期时间参数支持`time.Duration`类型;
|
||||
1. 新增`internal/structs`包,强大且便捷的结构体解析,并改进框架中所有涉及到结构体反射处理的模块;
|
||||
1. 改进`gbinary`增加封装方法对`BigEndian`的支持;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`garray.Search`返回值问题;
|
||||
1. 修复`garray.Contains`, `garray.New*ArrayFromCopy`方法逻辑问题;
|
||||
1. 修复`gjson.Remove`删除`slice`参数问题;
|
||||
1. 修复`gtree.AVLTree.Remove`方法返回值问题;
|
||||
1. 修复`gqueue.Size`不准确的大小问题;
|
||||
1. 修复`queue.Close`问题;
|
||||
1. 修复`gcache.GetOrSetLockFunc`当回调函数返回`nil`结果时的死锁问题;
|
||||
1. 修复`gfsnotify.Add`方法默认递归监控添加失效问题;
|
||||
1. 修复`gdb.Model.Scan`在某些参数类型下的失效问题;
|
||||
|
||||
## 注意事项
|
||||
|
||||
请注意以上粗体文字部分,如有使用,在您升级时可能会出现不兼容性。
|
||||
|
||||
|
||||
|
||||
# `v1.7.0` (2019-06-10)
|
||||
## 新功能/改进
|
||||
1. 重构改进`glog`模块:
|
||||
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
|
||||
- 增加日志内容的异步输出特性:https://goframe.org/os/glog/async
|
||||
- 增加日志输出内容的`Json`格式支持:https://goframe.org/os/glog/json
|
||||
- 增加`Flags`额外特性支持,包括文件行号打印、自定义时间格式、异步输出等特性控制:https://goframe.org/os/glog/flags
|
||||
- 增加`Writer`接口支持,便于开发者进行自定义的日志功能扩展,或者与第三方服务/模块对接集成:https://goframe.org/os/glog/writer
|
||||
- 修改`SetStdPrint`方法名为`SetStdoutPrint`
|
||||
- 修改链式方法`StdPrint`方法名为`Stdout`
|
||||
- 标记淘汰`*fln`日志输出方法,`*f`方法支持自动的换行输出
|
||||
- 新增更多的链式方法支持:https://goframe.org/os/glog/chain
|
||||
1. 重构改进`gmap`模块:
|
||||
- 增加更多数据格式支持:`HashMap`/`ListMap`/`TreeMap`
|
||||
- 简化类型名称,如`gmap.StringInterfaceMap`简化为`gmap.StrAnyMap`
|
||||
- 改进`Map/Keys/Values`方法以提高性能
|
||||
- 修改`BatchSet`/`BatchRemove`方法名为`Sets`/`Removes`
|
||||
- 新增更多功能方法支持:https://goframe.org/container/gmap/index
|
||||
1. 改进`gtime`时间模块:
|
||||
- 增加并完善更多的类`PHP`时间格式支持
|
||||
- 新增更多功能方法,如`FormatTo`/`LayoutTo`等等
|
||||
- 详见开发文档:https://goframe.org/os/gtime/index
|
||||
1. 改进`gdb`数据库模块:
|
||||
- 增加对继承结构体的数据转换支持:https://goframe.org/database/gdb/senior
|
||||
- 新增`GetLastSql`方法,用以在调试模式下获取最近一条执行的SQL语句
|
||||
- 其他的细节处理改进
|
||||
1. 改进`gtcp`通信模块:
|
||||
- 完善处理细节,提高通信性能;
|
||||
- 增加`TLS`服务端/客户端通信支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加简单协议支持,便于开发者封包/解包,并解决粘包/半包问题:https://goframe.org/net/gtcp/conn/pkg
|
||||
- TCP服务端增加`Close`方法
|
||||
- 更多细节查看开发文档:https://goframe.org/net/gtcp/index
|
||||
1. 改进`gconv`类型转换模块
|
||||
- 修改`gconv.TimeDuration`转换方法名称为`gconv.Duration`
|
||||
- 新增`gconv.StructDeep`及`gconv.MapDeep`方法,支持递归转换
|
||||
- 详见开发文档:https://goframe.org/util/gconv/struct
|
||||
1. 改进`ghttp`模块:
|
||||
- 日志输出增加`http/https`字段:https://goframe.org/net/ghttp/logs
|
||||
- 新增`ghttp.Server.SetKeepAlive`设置方法,用以开启/关闭`KeepAlive`特性
|
||||
- 增加`ghttp.Request.GetUrl`方法,用以获取当前完整的URL请求地址
|
||||
- `ghttp.Client`客户端支持开发者自定义`Transport`属性,`ghttp.Client.Post`方法支持`浏览器模式`:https://goframe.org/net/ghttp/client
|
||||
1. 新增`gtree`树形数据结构容器支持:https://goframe.org/container/gtree/index
|
||||
1. 改进`gudp`通信模块,具体请参考开发文档:https://goframe.org/net/gudp/index
|
||||
1. 改进`gcfg`配置管理模块,所有`Get*`方法增加默认值支持:https://goframe.org/os/gcfg/index
|
||||
1. `gredis`模块新增`DoVar`/`ReceiveVar`方法以便于开发者对执行结果进行灵活的数据格式转换:https://goframe.org/database/gredis/index
|
||||
1. `gcache`模块`BatchSet`/`BatchRemove`方法名修改为`Sets`/`Removes`
|
||||
1. 改进`gjson`/`gparser`模块,增加更多方法:https://goframe.org/encoding/gjson/index
|
||||
1. 改进`gfile.MainPkgPath`方法,以支持不同平台的开发环境;
|
||||
1. 改进`grpool`协程池模块,提高执行性能:https://goframe.org/os/grpool/index
|
||||
1. 改进`TryCatch`方法,当开发者不传递`Catch`参数时,默认抑制并忽略错误的处理
|
||||
1. 改进`gmlock`模块,增加`TryLockFunc`/`TryRLockFunc`方法,并且为`gmlock.Mutex`高级互斥锁对象增加`TryLockFunc`/`TryRLockFunc`方法
|
||||
1. 去除`gvar.VarRead`接口类型支持
|
||||
|
||||
## Bug Fix
|
||||
1. 解决`gdb`模块与其他第三方`ORM`模块同时使用的冲突;
|
||||
1. 修复`gcron.AddOnce`方法的细节逻辑问题;
|
||||
1. 修复内部`empty`模块的`IsEmpty`方法对结构体属性的空校验错误;
|
||||
1. 修复`gview`模板引擎的并发安全问题;
|
||||
1. 修复`ghttp.Server`的SESSION初始化过期时间问题;
|
||||
|
||||
# `v1.6.0` (2019-04-09)
|
||||
|
||||
## 新功能/改进
|
||||
1. `gcron`定时任务模块增加运行日志记录功能:https://goframe.org/os/gcron/index
|
||||
1. `gredis`增加全局分组配置功能,并增加更多的配置选项`maxIdle/maxActive/idleTimeout/maxConnLifetime`:https://goframe.org/database/gredis/index
|
||||
1. `gcfg`模块增加更多的默认配置文件检索路径,并且增加全局分组配置特性,增加`Instance`单例方法:https://goframe.org/os/gcfg/index
|
||||
1. `gview`模块增加更多的默认配置文件检索路径,并且增加`Instance`单例方法:https://goframe.org/os/gview/index
|
||||
1. `ghttp`模块新功能及改进:
|
||||
- 新增`CORS`HTTP(S)跨域请求特性: https://goframe.org/net/ghttp/cors
|
||||
- 增加`TLSConfig`配置功能;
|
||||
- 去掉路由注册方法的`error`返回值,当产生注册错误时直接终端打印错误/输出到日志文件;
|
||||
- 增加在`HTTP Code 302`跳转时的`Set-Cookie`支持;
|
||||
- 增加对`SESSION ID`的安全性检查;
|
||||
- 增加对基于`HTTPS`的`WebSocket`支持(`WSS`):https://goframe.org/net/ghttp/websocket/index
|
||||
- `Request`对象增加`Error`方法,用于输出自定义错误信息到`WebServer`错误日志中;
|
||||
- 其他一些改进;
|
||||
1. `gdb`模块新功能及改进:
|
||||
- 新增`Instance`单例管理方法;
|
||||
- 新增`Structs/Scan`链式操作方法,`gdb.DB/TX`新增`GetStructs/GetScan`方法,用于结果集`struct`/`slice`映射转换:https://goframe.org/database/gdb/chaining
|
||||
- 新增`Safe`链式操作方法(默认非并发安全),用于链式安全控制:https://goframe.org/database/gdb/chaining
|
||||
- `Where`链式操作方法改进:
|
||||
- 方法支持任意的`string/map/slice/struct/*struct`类型;
|
||||
- 逻辑调整,当链式操作中存在多个`Where`方法调用时,自动转换为`And`条件;
|
||||
- 支持`slice`条件参数,常用在`SELECT IN`查询中,例如:`Where("uid IN(?)", g.Slice{1,2,3})`;
|
||||
- 支持在`map`类型条件参数的`key`中传递条件,例如:`Where(g.Map{"uid>?", uid})`;
|
||||
1. `gconv`及`gvalid`模块改进并去掉对私有`struct`方法属性的转换/校验;
|
||||
1. `gconv.Map`转换方法新增对`json tag`: `-`, `omitempty`的支持: https://goframe.org/util/gconv/map
|
||||
1. `gstr`模块新增 `ReplaceI/ReplaceIByArray/ReplaceIByMap`大小写非敏感替换方法;
|
||||
1. `gutil`模块增加`IsEmpty`方法,用于判断给定变量是否为空(整型0, 布尔false, slice/map长度为0, 其他为nil的情况,判断为空),并增加快捷方法`g.IsEmpty`;
|
||||
1. `gutil`模块增加`Export`方法,用于导出返回格式化打印的变量内容字符串,并增加快捷方法`g.Export`;
|
||||
1. `gspath`增加缓存及非缓存检索检索方法`Search`/`SearchWithCache`;
|
||||
1. `gjson`模块增加默认的`UseNumber`功能支持;
|
||||
1. `gmap`增加`SetIfNotExistFunc/SetIfNotExistFuncLock`方法;
|
||||
1. 迁移`greuseport`模块到新的仓库:https://github.com/gogf/greuseport
|
||||
1. 大量的单元测试完善;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gqueue`模块的资源竞争问题;
|
||||
1. 修复`gconv.GTime`转换失败问题;
|
||||
1. 修复`gconv.String`在转换`int`参数时字节溢出问题;
|
||||
1. 修复`ghttp.Request`的`HTTP Basic Auth`校验问题;
|
||||
1. 修复`gxml`针对于非`UTF-8`编码内容转换的并发安全问题;
|
||||
1. 修复`gtime`部分`Format`(`G`&`j`)格式失效问题;
|
||||
1. 修复`gudp.Conn`对象的`RemoteAddr`获取客户端连接地址方法问题;
|
||||
1. 修复`gmap/gcache`模块的`GetOrSetFuncLock`方法,增加对回调方法返回值的`nil`判断,只有非nil返回值才会被保存;
|
||||
|
||||
|
||||
|
||||
# `v1.5.8` (2019-02-28)
|
||||
|
||||
## 新特性
|
||||
1. 主库从`gitee`迁移到了`github`( https://github.com/gogf/gf ),`gitee`作为镜像站,用于国内的代码贡献及ISSUE提交,迁移说明详见:https://goframe.org/upgradeto150
|
||||
1. 对常用的`container`数组模块: `garray`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/container/garray
|
||||
1. 对常用的`container`集合模块: `gset`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/container/gset
|
||||
1. 对常用的`container`MAP模块: `gmap`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/container/gmap
|
||||
1. 对常用的字符串模块: `gstr`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/text/gstr
|
||||
1. 改进`gform`中对`struct`/`*struct`参数的支持,`*Insert/*Save/*Replace/*Update/Where/Data`方法的参数调整为`interface{}`类型,并支持任意类型的: `string/map/slice/struct/*struct`参数传递,具体请参考:https://goframe.org/database/orm/chaining
|
||||
1. 新增/完善若干模块的单元测试用例, 包括:`gvalid`/`gregex`/`garray`/`gset`/`gmap`/`gstr`/`gconv`/`ghttp`/`gdb`;
|
||||
1. 由于`gkafka`模块比较重,且不是框架核心模块,因此将该模块迁移到新的仓库中独立管理,并去掉相关依赖包:https://github.com/gogf/gkafka
|
||||
1. 新增`greuseport`模块,用以实现TCP的`REUSEPORT`特性:https://godoc.org/github.com/gogf/gf/net/greuseport
|
||||
|
||||
## 新功能/改进
|
||||
1. 去掉模板引擎内置变量中自动初始化`session`对象带来的内存占用问题;
|
||||
1. `ghttp.Client`改进,增加若干方法,详见:https://goframe.org/net/ghttp/client
|
||||
1. `ghttp`分组路由增加`COMMON`方法,用以注册常用的`HTTP METHOD`(`GET/PUT/POST/DELETE`)路由;
|
||||
1. 更新框架依赖的`golang.org/x/sys`模块;
|
||||
1. 改进`gform`的批量操作(`Batch*`操作)返回结果对象,可以通过该结果对象获得批量操作准确的受影响记录行数;
|
||||
1. 将`gstr`/`gregex`模块从`util`分类迁移到了`text`分类目录下;
|
||||
1. 将`gtest`模块从`util`分类迁移到了`test`分类目录下;
|
||||
1. 完善`glog`方法注释;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复带点的邮件格式,用`gvalid.Check`的"`email`"规则不能匹配成功;
|
||||
1. 修复`gvalid.Check`在`regex`规则下的检查失败问题;
|
||||
1. 修复`gcron`模块定时规则中天和周不允许`?`符号的问题;
|
||||
1. 修复`ghttp.Server`在部分异常情况下仍然返回`200`状态码的问题;
|
||||
1. 修复`gfpool`模块中由于原子操作问题造成的高并发"内存泄露"问题;
|
||||
1. 修复分组路由注册对象/控制时,方法`Index`的路由仅能通过`/xxx/index`访问的问题;
|
||||
1. 修复模板引擎使用中,当不存在`config.toml`(即使没使用)配置文件时的报错问题;
|
||||
1. 其他一些修复;
|
||||
|
||||
|
||||
|
||||
# `v1.4.6` (2019-01-24)
|
||||
|
||||
## 新特性
|
||||
1. 新增并发安全的高性能任务定时器模块`gtimer`, 类似于Java的`Timer`,但是比较于Java的`Timer`更加强大,内部实现采用灵活高效的`分层时间轮`设计,被设计为可管理维护百万级别以上数量的定时任务。`gtimer`为`GF`框架的核心模块之一,单元测试覆盖率达到`93.6%`:[https://goframe.org/os/gtimer/index](https://goframe.org/os/gtimer/index)
|
||||
1. 采用任务定时器`gtimer`重构`gcron`定时任务模块,去掉第三方`github.com/robfig/cron`包的使用。`gcron`增加单例模式的定时任务:[https://goframe.org/os/gcron/index#](https://goframe.org/os/gcron/index#);
|
||||
1. `gconv`类型转换模块支持对`struct`结构体中的**指针属性**转换:[https://goframe.org/util/gconv/struct](https://goframe.org/util/gconv/struct);
|
||||
1. `gform`增加对数据库类型的自动识别特性,这一特性在需要将查询结果`json`编码返回时非常有用: [https://goframe.org/database/orm/index](https://goframe.org/database/orm/index)
|
||||
1. `Travis CI`增加对`386`架构的自动化测试支持(目前已支持`386`和`amd64`);
|
||||
|
||||
## 新功能
|
||||
1. `ghttp`模块新增`Exit`、`ExitAll`、`ExitHook`方法,用于HTTP请求处理流程控制: [https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object);
|
||||
1. `grand`模块增加`Meet/MeetProb`方法,用于给定概率的随机满足判断,增加别名方法`N/Str/Digits/Letters`;
|
||||
1. `gvalid`数据/表单校验模块增加`16X`及`19X`手机号的校验支持;
|
||||
|
||||
## 功能改进
|
||||
1. `gform`设置默认的数据库连接池`CONN_MAX_LIFE`参数值为`30`秒;
|
||||
1. 改进`glist`模块,提高约`20%`左右性能,并增加若干链表操作方法;
|
||||
1. 改进`gqueue`模块,提高约`50`左右性能,并增加模块对`select`语法的支持(使用`Queue.C`): [https://goframe.org/container/gqueue/index](https://goframe.org/container/gqueue/index);
|
||||
1. 改进`gmlock`内存锁模块,并完善单元测试用例:[https://goframe.org/os/gmlock/index](https://goframe.org/os/gmlock/index);
|
||||
1. 改进并发安全容器所有的模块,调整并发安全控制非必需参数`safe...bool`为`unsafe...bool`;
|
||||
1. 改进`gpool`对象复用模块,支持并发安全;
|
||||
1. 更新`gkafka`模块的第三方依赖包;
|
||||
1. 完善`ghttp`模块的单元测试用例;
|
||||
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gmd5`模块操作文件时的文件指针未关闭问题;
|
||||
1. 修复`gcache`缓存项过期删除失效问题;
|
||||
1. 其他修复;
|
||||
|
||||
# `v1.3.8` (2018-12-26)
|
||||
|
||||
## 新特性
|
||||
1. 对`gform`完成重构,以提高扩展性,并修复部分细节问题、完善单元测试用例([https://goframe.org/database/orm/index](https://goframe.org/database/orm/index));
|
||||
1. `WebServer`路由注册新增分组路由特性([https://goframe.org/net/ghttp/group](https://goframe.org/net/ghttp/group));
|
||||
1. `WebServer`新增`Rewrite`路由重写特性([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
|
||||
1. 增加框架运行时对开发环境的自动识别;
|
||||
1. 增加了`Travis CI`自动化构建/测试;
|
||||
|
||||
## 新功能
|
||||
1. 改进`WebServer`静态文件服务功能,增加`SetStaticPath`/`AddStaticPath`方法([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
|
||||
1. `gform`新增`Filter`链式操作方法,用于过滤参数中的非表字段键值对([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
|
||||
1. `gcache`新增`Data`方法,用以获取所有的缓存数据项;
|
||||
1. `gredis`增加`GetConn`方法获取原生redis连接对象;
|
||||
|
||||
## 功能改进
|
||||
1. 改进`gform`的`Where`方法,支持`slice`类型的参数,并更方便地支持`in`操作查询([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
|
||||
1. 改进`gproc`进程间通信数据结构,将`pid`字段从`16bit`扩展为`24bit`;
|
||||
1. 改进`gconv`/`gmap`/`garray`,增加若干操作方法;
|
||||
1. 改进`gview`模板引擎中的`date`内置函数,当给定的时间戳为空时打印当前的系统时间;
|
||||
1. 改进`gview`模板引擎中,当打印的变量不存在时,显示为空(标准库默认显示为`<no value>`);
|
||||
1. 改进`WebServer`,去掉`HANGUP`的信号监听,避免程序通过`nohup`运行时产生异常退出问题;
|
||||
1. 改进`gcache`性能,并完善基准测试;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gcache`在非LRU特性开启时的缓存关闭资源竞争问题,并修复`doSetWithLockCheck`内部方法的返回值问题;
|
||||
1. 修复`grand.intn`内部方法在`x86`架构下的随机数位溢出问题;
|
||||
1. 修复`gbinary`中`Int`方法针对`[]byte`参数长度自动匹配造成的字节长度溢出问题;
|
||||
1. 修复`gjson`由于官方标准库`json`不支持`map[interface{}]*`类型造成的Go变量编码问题;
|
||||
1. 修复`garray`中部分方法的数据竞争问题,修复二分查找排序问题;
|
||||
1. 修复`ghttp.Request.GetVar`方法获取参数问题;
|
||||
1. 修复`gform`的数据库连接池不起作用的问题;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# `v1.2.11` (2018-11-26)
|
||||
## 新特性
|
||||
1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://goframe.org/database/orm/database](https://goframe.org/database/orm/database));
|
||||
1. 完成`gvalid`模块校验结果的顺序特性([https://goframe.org/util/gvalid/checkmap](https://goframe.org/util/gvalid/checkmap));
|
||||
1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object));
|
||||
1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://goframe.org/os/gview/funcs](https://goframe.org/os/gview/funcs));
|
||||
1. 模板引擎新增内置变量`Config` ([https://goframe.org/os/gview/vars](https://goframe.org/os/gview/vars));
|
||||
1. 改进`gconv.Struct`转换默认规则,支持不区分大小写的键名与属性名称匹配;
|
||||
1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://goframe.org/database/orm/config](https://goframe.org/database/orm/config));
|
||||
1. `gfsnotify`模块增加对特定回调的取消注册功能([https://goframe.org/os/gfsnotify/index](https://goframe.org/os/gfsnotify/index));
|
||||
|
||||
|
||||
|
||||
## 新功能
|
||||
1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://goframe.org/net/ghttp/request](https://goframe.org/net/ghttp/request));
|
||||
1. 改进`ghttp.Response`,增加`ServeFileDownload`方法,用于WebServer引导客户端下载文件([https://goframe.org/net/ghttp/response](https://goframe.org/net/ghttp/response));
|
||||
1. `gvar`模块新增`gvar.VarRead`只读接口,用于控制对外只暴露数据读取功能;
|
||||
1. 增加`g.Throw`抛异常方法,`g.TryCatch`异常捕获方法封装;
|
||||
1. 改进`gcron`模块,增加自定义的Cron管理对象,增加`New/Start/Stop`方法;
|
||||
|
||||
|
||||
## 功能改进
|
||||
1. WebServer添加`RouterCacheExpire`配置参数,用于设置路由检索缓存过期时间;
|
||||
1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://goframe.org/net/ghttp/service/hook](https://goframe.org/net/ghttp/service/hook));
|
||||
1. 当前工作目录为系统临时目录时,`gcfg`/`gview`/`ghttp`模块默认不添加工作目录到搜索路径;
|
||||
1. 改进`WebSocket`默认支持跨域请求([https://goframe.org/net/ghttp/websocket](https://goframe.org/net/ghttp/websocket));
|
||||
1. 改进`gtime.Format`支持中文;
|
||||
1. 改进`gfsnotify`,支持编辑器对文件非执行标准编辑时(RENAME+CHMOD)的热更新问题;
|
||||
1. 改进`gtype.Set`方法,增加Set原子操作返回旧的变量值;
|
||||
1. `gfile.ScanDir`增加支持`pattern`多个文件模式匹配,使用'`,`'符号分隔多个匹配模式;
|
||||
1. `gcfg`模块增加获取配置变量为`*gvar.Var`;
|
||||
1. `gstr`模块增加对中文截取方法;
|
||||
1. 改进`gtime.StrToTime`对常用时间格式匹配模式,新增`gtime.ParseTimeFromContent`方法;
|
||||
1. 修改配置管理、模板引擎、调试模式的环境变量名称为大写下划线标准格式;
|
||||
1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://goframe.org/util/grand/index](https://goframe.org/util/grand/index));
|
||||
|
||||
## 问题修复
|
||||
1. 修复`gspath`模块在`windows`下搜索失效问题;
|
||||
1. 修复`gspath`模块Search时带有indexFiles的检索问题;
|
||||
1. bug fix INZS1([https://github.com/gogf/gf/issues/INZS1](https://github.com/gogf/gf/issues/INZS1));
|
||||
1. 修复`gproc.ShellRun`在windows下的执行问题;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# `v1.0.898 stable` (2018-10-24)
|
||||
|
||||
## 新特性
|
||||
1. `gf-orm`增加`sqlite`数据库类型支持([http://gf.johng.cn/database/orm/database](http://gf.johng.cn/database/orm/database));
|
||||
1. 增加`gkafka`模块,对kafka的客户端程序封装,支持分组消费及指定起始位置等特性,并提供简便易用的API接口([http://gf.johng.cn/database/gkafka/index](http://gf.johng.cn/database/gkafka/index));
|
||||
1. 增加go语言最新版本的`go modules`特性支持;
|
||||
1. 增加`gcron`定时任务模块([http://gf.johng.cn/os/gcron/index](http://gf.johng.cn/os/gcron/index));
|
||||
1. `Web Server`增加路由注册项获取/打印特性,所有的路由注册/回调注册一览无余;
|
||||
1. 模板引擎增加全局变量管理,并增加多个常用的内置函数及内置变量([http://gf.johng.cn/os/gview/funcs](http://gf.johng.cn/os/gview/funcs));
|
||||
1. `gredis`改进为单例操作方式(基于基层连接池特性),每次操作`redis`服务器时开发者无需显示调用`Close`方法执行关闭([http://gf.johng.cn/database/gredis/index](http://gf.johng.cn/database/gredis/index));
|
||||
1. `gf-orm`增加数据库操作自动`Close`特性(基于底层链接池特性),开发者无需再`defer db.Close()`,并增加`g.DB`数据库对象单例别名([http://gf.johng.cn/database/orm/linkop](http://gf.johng.cn/database/orm/linkop));
|
||||
1. 增加`gvar`通用动态变量模块([http://gf.johng.cn/container/gvar/index](http://gf.johng.cn/container/gvar/index));
|
||||
1. 数据结构容器增加`并发安全特性开启/关闭功能`,当关闭后和普通的数据结构无异,且在非并发安全模式下性能会得到提高;
|
||||
1. 新增`gmlock`内存锁模块([http://gf.johng.cn/os/gmlock/index](http://gf.johng.cn/os/gmlock/index));
|
||||
1. 增加`gaes`算法模块([http://gf.johng.cn/crypto/gaes/index](http://gf.johng.cn/crypto/gaes/index));
|
||||
1. `gproc`模块增加执行`shell`命令方法([http://gf.johng.cn/os/gproc/index](http://gf.johng.cn/os/gproc/index));
|
||||
1. 新增`gfcache`模块,用于带自动缓存更新的文件内容操作(文档待完善);
|
||||
|
||||
## 新功能
|
||||
1. `glog`增加链式操作方法,增加日志级别管理控制、分类管理、调试管理功能;
|
||||
1. `g.View`增加分组名称设置,支持通过`g.*`对象管理器获取多个命名的单例模板引擎对象;
|
||||
1. `glog`增加对文件名称格式的自定义设置,支持`gtime日期格式`;
|
||||
1. `gconv`增加`Ints/Uints/Floats/Interfaces`转换方法;
|
||||
1. `gjson`增加`Append`方法;
|
||||
1. `gparser`增加`NewUnsafe/Append`方法;
|
||||
1. `gcache`增加`GetOrSet/GetOrSetFunc/GetOrSetFuncLock`方法;
|
||||
1. `gset`增加`LockFunc/RLockFunc`方法;
|
||||
1. `ghttp.Response`方法完善,增加`ParseTpl/ParseTplContent/TplContent`方法,`Template`修改为`Tpl`方法;
|
||||
1. `ghttp.Request`增加获取用户真实IP判断;
|
||||
1. `Session`增加`Contains`方法;
|
||||
1. 完善`ghtml`模块,增加多个方法;
|
||||
1. `gcache`新增`Contains/SetIfNotExist`方法;
|
||||
1. `gvalid`增加`Error`对象,用以管理校验错误信息;
|
||||
1. `gvalid`模块增加`struct tag`的校验规则、自定义错误提示信息绑定的支持特性([http://gf.johng.cn/util/gvalid/index](http://gf.johng.cn/util/gvalid/index));
|
||||
1. `ghttp`增加输入参数与`struct`的`绑定机制`,并增加对应`params`标签支持([http://gf.johng.cn/net/ghttp/service/handler](http://gf.johng.cn/net/ghttp/service/handler));
|
||||
1. `ghttp.Request`增加服务端`BasicAuth`功能(文档待完善);
|
||||
1. `gvalid`增加字段校验别名,用于自定义返回结果字段,并更新WebServer中相关使用的模块;
|
||||
1. `gf-orm`链式操作增加`ForPage`方法,调整`Chunks`方法;
|
||||
1. `ghttp`对象路由注册增加`Init&Shut`自动回调方法,增加重复路由注册检测功能;
|
||||
1. `gfsnotify`增加默认递归`Add/Remove`特性;
|
||||
1. `ghttp.Response`增加`ServiceFile`方法;
|
||||
1. 其他一些新功能;
|
||||
|
||||
## 功能改进
|
||||
1. 改进`ghttp.Server`配置管理;
|
||||
1. 改进`gcache`底层对象继承关系,改进部分设计细节,提高性能;
|
||||
1. 改进`gfpool`文件指针池,修复部分错误,提升性能,并增加基准测试代码;
|
||||
1. 改进`gmap`系列并发安全map数据结构,增加多个易用性的方法;
|
||||
1. 改进`gconv.Struct`对象转换功能([http://gf.johng.cn/util/gconv/index](http://gf.johng.cn/util/gconv/index));
|
||||
1. 改进`grand`随机数生成规则,提供了极高的随机数生成性能,并保证每一次调用随机方法时生成的都是不同的随机数值([http://gf.johng.cn/util/grand/index](http://gf.johng.cn/util/grand/index));
|
||||
1. 改进`gfile`文件内容操作方法,增加若干常用的文件内容读取方法;
|
||||
1. 改进`gtime`模块,并增加时区转换方法;
|
||||
1. 改进`COOKIE`,去掉锁机制;
|
||||
1. 改进`SESSION`获取方法,新增多个类型获取方法;
|
||||
1. 改进`g.DB/g.Config`单例缓存键名;
|
||||
1. 改进`gtcp/gudp`超时错误判断机制;
|
||||
1. 改进`gtype`底层统一修改为原子操作;
|
||||
1. 改进`gvalid`对`struct`的`string`属性的默认值非必需校验;
|
||||
1. 改进`gvalid`在关联规则下的非必需校验;
|
||||
1. 改进`gf-orm`在调试模式下日志自动输出功能;
|
||||
1. `ghttp.Server/gspath`模块静态文件检索改进;
|
||||
1. 优化`ghttp.ServerConfig`配置,增加`struct/method``名称到uri`的转换规则,通过`SetNameToUri`方法进行灵活配置([http://gf.johng.cn/net/ghttp/service/object](http://gf.johng.cn/net/ghttp/service/object));
|
||||
1. 改进`*any/:name`路由匹配规则,支持不带名字的`*/:`路由规则;
|
||||
1. 修改默认配置文件名称 `config.yml` -> `config.toml`([http://gf.johng.cn/os/gcfg/index](http://gf.johng.cn/os/gcfg/index));
|
||||
1. 调整服务注册的`BindControllerMethod`及`BindObjectMethod`逻辑为绑定路由到指定的方法执行;
|
||||
1. 改进`garray`二分查找方法,增加安全操作处理;
|
||||
1. 改进`gdb.Result/Recorde` `ToXml`方法,增加可选的`rootTag`参数;
|
||||
1. 其他一些改进;
|
||||
|
||||
## 问题修复
|
||||
1. 修复`ghttp.Server`在`windows`下的重启失效问题;
|
||||
1. 修复`ghttp.Server`服务注册与回调注册路由重复判断问题;
|
||||
1. 修复`garray`排序数组`Add`变参时的死锁问题;
|
||||
1. 修复`gfsnotify`默认递归监控整个`gspath.Add`添加的目录的问题;
|
||||
1. 修复`ghttp.BindParams`对`@file`文件上传标识符的转义问题;
|
||||
1. 修复`ghttp.Server`日志路径丢失问题;
|
||||
1. 修复`多WebServer`下的状态检测问题;
|
||||
1. 修复`gvalid`模块`min/max`校验问题;
|
||||
1. 修复控制器和执行对象服务注册时绑定'/'路由的问题;
|
||||
1. 修复`gvalid.CheckStruct`自定义错误提示失效问题;
|
||||
1. `ghttp.Server`修复`hook`与`serve`方法的路由影响,并新增跳转方法;
|
||||
1. 其他一些修复;
|
||||
|
||||
## 其他改动
|
||||
1. 去掉`gfile.IsExecutable`方法;
|
||||
1. 目录调整,将`加密/解密`相关的包从`encoding`目录迁移到`crypto`目录下;
|
||||
1. 增加`gfsnotify/gfcache`调试信息;
|
||||
1. `gf-orm`允许写入的键值为`nil`时往数据库中写入`null`;
|
||||
1. 统一使用`gview.Params`类型作为模板变量类型;
|
||||
1. `gconv.MapToStruct`方法名称修改为`gconv.Struct`;
|
||||
1. `ghttp.Server`完善重启及停止的终端提示信息;
|
||||
1. 完善`gring`模块,增加`约瑟夫问题`代码作为`gring`示例程序;
|
||||
1. 其他一些改动;
|
||||
|
||||
|
||||
|
||||
# `v0.99.682 beta` (2018-08-07)
|
||||
## 新特性
|
||||
1、新增gdes包,用于DES加密/加密算法处理;
|
||||
2、新增gkafka包,kafka的golang客户端;
|
||||
3、新增gpool对象复用池,比较于标准库的sync.Pool更加灵活强大,可自定义对象的缓存时间、创建方法、销毁方法(http://gf.johng.cn/686654);
|
||||
4、完成网络通信gtcp/gudp包的重构,并进行了大量的改进工作,新增了详尽的开发文档及示例代码(http://gf.johng.cn/494382);
|
||||
5、增加gring并发安全环,标准库container/ring包的并发安全版本,并做了易用性的封装(http://gf.johng.cn/686655);
|
||||
6、gtime包新增了自定义日期格式话的支持,格式化语法类似PHP的date语法(http://gf.johng.cn/494387);
|
||||
7、gdb增加调试模式特性,使用SetDebug方法实现,在调试模式下可以获取详细的SQL执行记录,增加了详细的开发文档及示例代码(http://gf.johng.cn/702801);
|
||||
8、gdb增加查询缓存特性,使用Cache方法实现,增加了详细的开发文档及示例代码(http://gf.johng.cn/702801);
|
||||
9、ghttp.Server路由功能增加字段匹配规则特性,支持如:/order/list/{page}.html 动态路由规则特性(http://gf.johng.cn/702766);
|
||||
10、gpage分页包增加分页URL规则生成模板特性,内部可使用{.page}变量指定页码位置(http://gf.johng.cn/716438);
|
||||
11、增加gmap.Map对象,这是gmap.InterfaceInterfaceMap的别名;
|
||||
|
||||
## 新功能
|
||||
1、gdb增加MaxIdleConnCount/MaxOpenConnCount/MaxConnLifetime三项配置,并增加SetMaxConnLifetime方法;
|
||||
2、ghttp.Client增加HTTP账号密码设置功能(SetBasicAuth);
|
||||
3、glog新增对系统换行符号的自适配调整(\n|\r\n);
|
||||
4、增加glog控制台调试模式打印开关(SetDebug);
|
||||
5、gcfg增加SetFileName方法设置默认读取的配置文件名称;
|
||||
6、gcfg/gjson/gparser包新增Int8/16/32/64,Uint8/16/32/64方法;
|
||||
7、增加gzip方法的封装(Zip/Unzip);
|
||||
8、gview增加模板变量分隔符设置方法SetDelimiters;
|
||||
9、ghttp.Response增加Writef、Writefln方法;
|
||||
|
||||
## 功能改进
|
||||
1、改进gfilepool文件指针池设计;改进gfile文本内容写入,增加指针池使用
|
||||
2、gdb包增加调试模式特性,并支持在调试模式下获得已执行的SQL列表结果
|
||||
3、改进gproc进程间通信机制,增加进程消息分组特性,并限定队列大小
|
||||
4、gdb结果方法处理增加ToXml/ToJson方法
|
||||
5、gregx包名修改为gregex
|
||||
6、改进gtime.StrToTime方法,新增对常见标准时间日期的自动转换,以及对时区的自动识别支持,并调整gconv,gvalid对该包的引用
|
||||
7、增加对字符集转换的封装,gxml包中使用新增的字符集转换包来做处理
|
||||
8、ghttp.Server.EnableAdmin页面Restart接口支持GET参数newExeFilePath支持
|
||||
9、ghttp.Server平滑重启机制增加可自定义重启可执行文件路径,特别是针对windows系统特别有用(因为windows下不支持可执行文件覆盖更新)
|
||||
10、改进ghttp.Server静态文件检索设计,增加开发环境时的main包源码目录查找机制;改进gcfg/gview的main包源码目录查找机制
|
||||
11、优化gcache设计,LRU特性非默认开启;优化gtype/gcache基准测试脚本;新增gregx基准测试脚本,改进设计,提升性能
|
||||
12、gfile包增加GoRootOfBuild方法,用于获取编译时的GOROOT数值;并改进glog包中backtrace的GOROOT路径过滤处理;
|
||||
13、改进grpool代码质量,并改进对池化goroutine数量的限制设计
|
||||
14、改进gdb.Map/List及g.Map/List的类型定义,改用别名特性以便支持原生类型输入(map/slice),并修复gdb.Model.Update方法参数处理问题
|
||||
15、调整ghttp包示例代码目录结构,增加ghttp.Client自定义Header方法,ghttp.Cookie增加Map方法用于获得客户端提交的所有cookie值,构造成map返回
|
||||
16、删除gcharset中的getcharset方法
|
||||
17、去掉gmap中常用的基本数据类型转换获取方法
|
||||
18、改进gconv.String方法,当无法使用基本类型进行字符串转换时,使用json.Marshal进行转换
|
||||
19、gvalid.CheckObject方法名称修改为gvalid.CheckStruct
|
||||
|
||||
|
||||
## 问题修复
|
||||
1、修正gstr.IsNumeric错误
|
||||
2、修复当xml中encoding字符集为非UTF-8字符集时报错的问题
|
||||
3、修正gconv包float32->float64精度问题
|
||||
4、修复gpage包分页计数问题
|
||||
5、修复gdb批量数据Save错误
|
||||
6、去掉gpool中math.MAXINT64常量的使用,以修复int64到int类型的转换错误,兼容32位系统
|
||||
7、修正ghttp包没有使用Server仍然初始化相关异步goroutine的问题
|
||||
|
||||
|
||||
|
||||
|
||||
# `v0.98.503 beta` (2018-05-21)
|
||||
## 新特性
|
||||
1、平滑重启特性( http://gf.johng.cn/625833 );
|
||||
2、gflock文件锁模块( http://gf.johng.cn/626062 );
|
||||
3、gproc进程管理及通信模块( http://gf.johng.cn/626063 );
|
||||
4、gpage分页管理模块,强大的动态分页及静态分页功能,并为开发者自定义分页样式提供了极高的灵活度( http://gf.johng.cn/597431 );
|
||||
5、ghttp.Server增加多端口监听特性,并支持HTTP/HTTPS( http://gf.johng.cn/494366 , http://gf.johng.cn/598802 );
|
||||
6、增加gspath目录检索包管理工具,支持对多目录下的文件检索特性;
|
||||
7、ghttp包控制器及执行对象注册增加更灵活的动态路由特性,路由规则增加{method}变量支持;
|
||||
|
||||
## 新功能
|
||||
1、gutil包增加MapToStruct方法,支持将map数据类型映射为struct对象;
|
||||
2、gconv
|
||||
1)、gconv包增加按照类型名称字符串进行类型转换;
|
||||
2)、gconv包新增Time/TimeDuration类型转换方法;
|
||||
3、ghttp
|
||||
1)、增加Web Server目录安全访问控制机制;
|
||||
2)、ghttp.Server增加自定义状态码回调函数注册处理;
|
||||
4、gdb
|
||||
1)、gdb包增加gdb.GetStruct/gdb.Model.Struct方法,获取查询结果记录自动转换为指定对象;
|
||||
2)、gdb增加Value/Record/Result类型,增加对Value类型的系列类型转换方法;
|
||||
3)、gdb包增加db.GetCount,tx.GetCount,model.Count数量查询方法;
|
||||
|
||||
## 功能改进
|
||||
1、改进gredis客户端功能封装;
|
||||
2、改进grand包随机数生成性能;
|
||||
3、grand/gdb/gredis包增加benchmark性能测试脚本;
|
||||
4、改进gjson/gparser包的ToStruct方法实现;
|
||||
5、gdb :改进gdb.New获取ORM操作对象性能;
|
||||
6、gcfg :改进配置文件检索功能;
|
||||
7、gview:模板引擎增加多目录检索功能;
|
||||
8、gfile:增加源码main包目录获取方法MainPkgPath;
|
||||
9、ghttp
|
||||
1)、ghttp.Request增加请求进入和完成时间记录,并增加到默认日志内容中;
|
||||
2)、ghttp.Server事件回调之间支持通过ghttp.Request.Param自定义参数进行流程传参;
|
||||
10、gdb
|
||||
1)、改进gdb.Result与gdb.List, gdb.Record与gdb.Map之间的类型转换,便于业务层数据编码处理(如json/xml);
|
||||
2)、改进gdb.Tx.GetValue返回值类型;
|
||||
3)、gdb.Model.Data参数支持更加灵活的map参数;
|
||||
|
||||
## 问题修复
|
||||
1、ghttp
|
||||
1)、修复ghttp包路由缓存问题;
|
||||
2)、修复服务注册时的控制器及执行对象方法丢失问题;
|
||||
2、gconv
|
||||
1)、修正gconv.Float64方法位大小设置问题;
|
||||
2)、修复gconv.Int64(float64(xxx))问题;
|
||||
2、gdb
|
||||
1)、修复gdb.GetAll针对返回数据列表的for..range...的返回结果slice相同指针问题;
|
||||
2)、修复gdb.Delete方法错误;
|
||||
3)、修复gdb.Model.And/Or方法;
|
||||
4)、修复gdb.Model.Where方法参数处理问题;
|
||||
3、garray:修复garray包Remove方法锁机制问题;
|
||||
4、gtype :修复gtype.Float32/gtype.Float64对象类型的方法逻辑错误;
|
||||
5、gfsnotify:修复在windows下文件参数中不同文件分隔符引起的热更新机制失效问题;
|
||||
6、修复gvalid包验证问题:如果值为nil,并且不需要require*验证时,其他验证失效。并增加单元测试项,测试通过。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# `v0.97.399 beta` (2018-04-23)
|
||||
1、 增加gfsnotify文件监控模块;
|
||||
2、 配置管理模块增加配置文件自动检测更新机制;
|
||||
3、 模板引擎增加对模板文件的自动检测更新机制;
|
||||
4、 改进gconv包基本类型转换功能,提高转换性能;
|
||||
5、 增加gpage分页管理包,支持动态分页、静态分页以及自定义分页样式特性;
|
||||
6、 ghttp.Request增加Exit方法,用以标记服务退出,当在服务执行前调用后,服务将不再执行;
|
||||
7、 ghttp.Response去掉WriteString方法,统一使用Write方法返回数据流,是使用灵活的参数形式;
|
||||
8、 模板引擎增加模板变量暴露接口LockFunc/RLockFunc,以便支持开发者灵活处理模板变量;
|
||||
9、 ghttp.Server增加access & error log功能,并支持开发者自定义日志处理回调函数注册;
|
||||
10、增加gredis包,支持对redis的客户端操作封装,并将gredis.Redis对象加入到gins单例管理器中进行统一配置管理维护;
|
||||
11、gins单例管理器增加对单例对象配置文件的自动检测更新机制,当配置文件在外部发生变更时,自动刷新单例管理器中的单例对象;
|
||||
12、gdb数据库ORM包增加And/Or条件链式方法,并改进Where/Data方法参数灵活性;
|
||||
13、对于新增加的模块,同时也增加了对应的开发文档,并梳理完善了现有的其他模块开发文档;
|
||||
14、修复ISSUE:
|
||||
#IISWI github.com/gogf/gf/issues/IISWI,
|
||||
#IISMY github.com/gogf/gf/issues/IISMY,
|
||||
反馈并跟踪完成第三方依赖mxj包的ISSUE修复(github.com/clbanning/mxj/issues/48);
|
||||
|
||||
|
||||
|
||||
266
RELEASE.2.MD
266
RELEASE.2.MD
@ -1,266 +0,0 @@
|
||||
# `v1.10.0` (2019-12-05)
|
||||
|
||||
各位`gfer`久等了,较上一次发布时间过去已有两个多月了,这段时间`GF`也在不断地迭代改进,细节比较多,拟了个大概,以下是`release log`。
|
||||
|
||||
另外,`GoFrame`也参加了2019最受欢迎中国开源软件评选投票,明天就结束了,欢迎为`GF`投票啊:https://www.oschina.net/project/top_cn_2019 网页可以投一票,微信也可以投一票。
|
||||
|
||||
## 新特性
|
||||
|
||||
1. `Web Server`新特性:
|
||||
- 改进中间件及分组路由实现:https://goframe.org/net/ghttp/router/middleware
|
||||
- 增加文件配置管理特性:https://goframe.org/net/ghttp/config
|
||||
- 改进参数获取:https://goframe.org/net/ghttp/request
|
||||
- 改进文件上传:https://goframe.org/net/ghttp/client/demo/upload
|
||||
1. `Session`增加内置的多种`Storage`实现:
|
||||
- 基本介绍:https://goframe.org/os/gsession/index
|
||||
- 文件存储:https://goframe.org/os/gsession/file
|
||||
- 内存存储:https://goframe.org/os/gsession/memory
|
||||
- `Redis`存储:https://goframe.org/os/gsession/redis
|
||||
1. 增加日志组件单例对象,并优化配置管理:
|
||||
- https://goframe.org/frame/g/index
|
||||
- https://goframe.org/os/glog/config
|
||||
1. 常用的`container`容器增加`JSON`数据格式的`Marshal`/`UnMarshal`接口实现:
|
||||
- https://goframe.org/container/gmap/index
|
||||
- https://goframe.org/container/garray/index
|
||||
- https://goframe.org/container/gset/index
|
||||
- https://goframe.org/container/gvar/index
|
||||
- https://goframe.org/container/gtype/index
|
||||
- https://goframe.org/container/glist/index
|
||||
- https://goframe.org/container/gvar/index
|
||||
1. 新增`guuid`模块,用于通用的`UUID`生成:https://goframe.org/util/guuid/index
|
||||
|
||||
## 功能改进
|
||||
|
||||
### `net`
|
||||
1. `ghttp`
|
||||
- 改进请求流程处理性能;
|
||||
- `Server`增加对`Logger`日志对象的配置;
|
||||
- `Server`开放了`GetRouterMap`方法,用于获得当前服务的路由列表信息,使得开发者可以更方便地实现自定义权限管理;
|
||||
- `Server`配置管理优化;
|
||||
- `Client`客户端对象进行了大量的改进工作;
|
||||
- `Client`客户端对象增加多文件上传功能;
|
||||
- `Request`对象增加`GetError`方法,用于获取当前处理错误;
|
||||
- `Request`对象增加独立的视图对象及视图变量绑定功能,使得每个请求可以独立视图管理,也可以通过中间件切换请求对象的视图对象。默认情况下该功能关闭,视图解析时使用的是`Server`对象的视图对象;
|
||||
- 改建`Response`对象的`CORS`功能;
|
||||
- 增加`Response.WriteTplDefault`方法,用于解析并返回默认的模板内容;
|
||||
- 增加更多的单元测试用例;
|
||||
- 其他改进;
|
||||
1. `gipv4`/`gipv6`
|
||||
- 一些改进工作;
|
||||
1. `gtcp`/`gudp`
|
||||
- 一些改进工作;
|
||||
|
||||
### `database`
|
||||
1. `gdb`
|
||||
- 大量细节改进工作;
|
||||
- 去掉查询数据为空时的`sql.ErrNoRows`错误返回,保留`Struct`/`Structs`/`Scan`方法在操作数据为空的该错误返回;
|
||||
- 调试模式开启时,输出的SQL语句改进为完整的带参数的SQL,仅作参考;
|
||||
- `Where`方法增加对`gmap`数据类型支持,包括顺序性的`ListMap`/`TreeMap`等等;
|
||||
- 查询缓存方法`Cache`的缓存时间参数类型修改为`time.Duration`;
|
||||
- 修改`Record`/`Result`的数据类型转换方法名称,原有的转换方法标记为`deprecated`;
|
||||
- `Record`/`Result`查询结果类型增加`IsEmpty`方法,用于判断结果集是否为空;
|
||||
- `Record`类型增加`GMap`方法,用于将查询记录转换为`gmap`类型;
|
||||
- 增加`Option`/`OptionOmitEmpty`方法,用于输入参数过滤,包括`Data`参数及`Where`参数:https://goframe.org/database/gdb/empty
|
||||
- 增加字段排除方法`FieldsEx`:https://goframe.org/database/gdb/senior
|
||||
- 增加日志功能特性:https://goframe.org/database/gdb/senior
|
||||
- 改进数据库配置管理:https://goframe.org/database/gdb/config
|
||||
- 增加大量单元测试;
|
||||
1. `gredis`
|
||||
- 返回数据类型转换改进:https://github.com/gogf/gf/issues/415
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
|
||||
### `os`
|
||||
1. `gcache`
|
||||
- 需要注意了:缓存的有效时间参数从`interface{}`类型调整为了`time.Duration`类型,因此不再兼容之前的`int`参数类型,以保证更好的性能;
|
||||
1. `gfcache`
|
||||
- 由于`gcache`组件的缓存时间参数类型的变更,因此该组件的时间参数也变更为了`time.Duration`类型;
|
||||
1. `gcfg`
|
||||
- 增加`Available`方法,用以判断配置是否有效;
|
||||
1. `gfile`
|
||||
- 增加`Chdir`方法,用于工作目录切换;
|
||||
1. `gtime`
|
||||
- 增加`JSON`数据格式的`Marshal`/`UnMarshal`接口实现;
|
||||
|
||||
### `container`
|
||||
1. `gmap`
|
||||
- 增加`MapStrAny`方法,用于常见`map`类型的转换;
|
||||
- 增加`MapCopy`方法,用于底层`map`数据复制;
|
||||
- 增加`FilterEmpty`方法,用于`map`空值过滤;
|
||||
- 增加`Pop`/`Pops`方法,用于随机返回`map`中的数据项(并删除);
|
||||
- 增加`Replace`方法,用于给定的`map`数据覆盖底层`map`数据项;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
1. `garray`
|
||||
- 增加`Interfaces`转换方法,返回`[]interface{}`类型;
|
||||
- 对排序数组增加`SetComparator`方法用户自定义修改比较器;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
1. `glist`
|
||||
- 增加`NewFrom`方法,基于给定的`[]interface{}`变量创建链表;
|
||||
- 增加`Join`方法,用于将链表项使用给定字符串连接为字符串返回;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
1. `gset`
|
||||
- 增加`AddIfNotExistFunc`/`AddIfNotExistFuncLock`方法;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
1. `gtree`
|
||||
- 增加`Replace`方法,用于更新现有树的数据项;
|
||||
- 其他改进;
|
||||
1. `gtype`
|
||||
- 一些细节改进工作,不一一列出;
|
||||
- 完善基准测试、单元测试;
|
||||
1. `gvar`
|
||||
- 增加`Ints`/`Uints`类型转换方法;
|
||||
- 其他改进;
|
||||
|
||||
### `crypto`
|
||||
1. `gmd5`
|
||||
- 小细节改进;
|
||||
1. `gsha1`
|
||||
- 小细节改进;
|
||||
|
||||
### `text`
|
||||
1. `gstr`
|
||||
- 改进`SplitAndTrim`方法,将`SplitAndTrimSpace`标记为`deprecated`;
|
||||
- 增加`TrimStr`方法;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
|
||||
### `debug`
|
||||
|
||||
1. `gdebug`
|
||||
- 增加`CallerFileLineShort`/`FuncPath`/`FuncName`方法;
|
||||
- 其他改进;
|
||||
|
||||
### `encoding`
|
||||
|
||||
1. `gbase64`
|
||||
- 增加`EncodeToString`/`EncodeFile`/`EncodeFileToString`/`DecodeToString`方法;
|
||||
- 完善单元测试;
|
||||
1. `gjson`
|
||||
- 完善单元测试;
|
||||
|
||||
### `frame`
|
||||
|
||||
1. `g`/`gins`
|
||||
- https://goframe.org/frame/g/index
|
||||
- 增加`CreateVar`方法;
|
||||
- 完善单元测试;
|
||||
- 其他改进;
|
||||
|
||||
### `util`
|
||||
|
||||
1. `gconv`
|
||||
- 改进优化部分类型转换方法性能;
|
||||
- 增加`Uints`/`SliceUint`类型转换方法;
|
||||
- 增加`UnsafeStrToBytes`/`UnsafeBytesToStr`高性能的类型转换方法;
|
||||
- 增加对`MapStrAny`接口方法的支持,用于常见`map`类型的转换;
|
||||
- 其他改进;
|
||||
1. `gvalid`
|
||||
- 改进对中国身份证号的识别校验功能;
|
||||
- 增加`luhn`银行卡号的校验功能;
|
||||
1. `grand`
|
||||
- 一些性能改进工作;
|
||||
|
||||
|
||||
## Bug Fix
|
||||
1. 解决`WebSocket`关闭时的`hijacked`报错问题:https://github.com/gogf/gf/issues/381
|
||||
1. 解决静态文件服务时大文件的内存占用问题;
|
||||
1. 修复前置`Nginx`后默认情况下的`Cookie`域名设置问题;
|
||||
1. 修复`gconv.Struct`在属性为`[]struct`并且输入属性参数为空时的转换失败问题:https://github.com/gogf/gf/issues/405
|
||||
1. 其他一些修复;
|
||||
|
||||
|
||||
|
||||
# `v1.9.3` (2019-09-24)
|
||||
|
||||
该版本实际为`v2.0`的大版本发布,为避免`go module`机制严格要求`v2`版本以上需要修改`import`并加上`v2`后缀,因此使用了`v1.9`版本进行发布。
|
||||
|
||||
## 新特性
|
||||
|
||||
1. 新增`gf`命令行开发辅助工具:https://goframe.org/toolchain/cli
|
||||
1. 新增`gres`资源管理器模块:https://goframe.org/os/gres/index
|
||||
1. 重构`Session`功能,新增`gsession`模块,`WebServer`默认使用文件存储`Session`:https://goframe.org/net/ghttp/session
|
||||
1. `WebServer`新增中间件特性,并保留原有的HOOK设计,两者都可实现请求拦截、预处理等等特性:https://goframe.org/net/ghttp/router/middleware
|
||||
1. 新增`gi18n`国际化管理模块:https://goframe.org/i18n/gi18n/index
|
||||
1. 新增`gini`模块:https://goframe.org/encoding/gini/index
|
||||
1. `WebServer`新增更便捷的层级路由注册方式:https://goframe.org/net/ghttp/group/level
|
||||
1. `gcmd`命令行参数解析模块重构,增加`Parser`解析对象:https://goframe.org/os/gcmd/index
|
||||
1. 新增`gdebug`模块,用于堆栈信息获取/打印:https://goframe.org/debug/gdebug/index
|
||||
|
||||
|
||||
## 重大调整
|
||||
1. 去掉`1.x`版本中已经被标记为`deprecated`的方法;
|
||||
1. 调整`container`分类的容器模块,将默认并发安全参数调整为默认非并发安全;
|
||||
1. 目录调整:
|
||||
- 去掉`third`目录,统一使用`go module`管理包依赖;
|
||||
- 将原有`g`目录中的模块移出到框架主目录,原有的`g`模块移动到`frame/g`目录;
|
||||
- 将原有`geg`示例代码目录名称修改为`.example`;
|
||||
|
||||
|
||||
|
||||
## 功能改进
|
||||
|
||||
1. `ghttp`
|
||||
- 改进`Request`参数解析方式:https://goframe.org/net/ghttp/request
|
||||
- 改进跨域请求功能,新增`Origin`设置及校验功能:https://goframe.org/net/ghttp/cors
|
||||
- `Cookie`及`Session`的`TTL`配置数据类型修改为`time.Duration`;
|
||||
- 新增允许同时通过`Header/Cookie`传递`SessionId`;
|
||||
- 新增`ConfigFromMap/SetConfigWithMap`方法,支持通过`map`参数设置WebServer;
|
||||
- 改进默认的`CORS`配置,增加对常见`Header`参数的默认支持;
|
||||
- 新增`IsExitError`方法,用于开发者自定义处理`recover`错误处理时,过滤框架本身自定义的非异常错误;
|
||||
- 新增`SetSessionStorage`配置方法,用于开发者自定义`Session`存储;
|
||||
- `ghttp.Request`新增更多的参数获取方法;
|
||||
1. `gdb`
|
||||
- 增加对SQL中部分字段的自动转义(`Quote`)功能;
|
||||
- 增加对方法操作以及链式操作中的`slice`参数的支持;
|
||||
- 增加`SetLogger`方法用于开发者自定义数据库的日志打印;
|
||||
- 增加`Master/Slave`方法,开发者可自主选择数据库操作执行的主从节点;
|
||||
- 增加对`mssql/pgsql/oracle`的单元测试;
|
||||
- 在`debug`模式支持完整带参数整合的SQL语句调试打印;
|
||||
- 增加了更多的功能方法;
|
||||
1. `glog`
|
||||
- 新增`Default`方法用于获取默认的`Logger`对象;
|
||||
- 新增`StackWithFilter`方法用于自定义堆栈打印过滤;
|
||||
- 增加了更多的功能方法;
|
||||
1. `gfile`
|
||||
- 部分方法名称调整:`Get/PutBinContents`修改为`Get/PutBytes`;
|
||||
- 增加`ScanDirFile`方法,用于仅检索文件目录,支持递归检索;
|
||||
- 增加了更多的功能方法;
|
||||
1. `gview`
|
||||
- 新增`SetI18n`方法用于设置视图对象自定义的`gi18n`国际化对象;
|
||||
- 新增对`gres`资源管理器的内置支持;
|
||||
1. `gcompress`
|
||||
- 增加`zip`算法的文件/目录的压缩/解压方法;
|
||||
- 文件/目录压缩参数支持多路径;
|
||||
1. `gconv`
|
||||
- 改进对`[]byte`数据类型参数的支持;
|
||||
- 新增`Unsafe`转换方法,开发者可在特定场景下使用,提高转换效率;
|
||||
- 新增`MapDeep/StructDeep/StructsDeep`方法,支持递归`struct`转换;
|
||||
1. `gjson/gparser`
|
||||
- 改进类型自动识别功能;
|
||||
- 新增`LoadJson/LoadXml/LoadToml/LoadYaml/LoadIni`方法用于自定义的数据类型内容加载;
|
||||
- 增加了更多的功能方法;
|
||||
1. `gerror`
|
||||
- 改进错误堆栈获取逻辑;
|
||||
- 增加了更多的功能方法;
|
||||
1. `gmap/garray/gset/glist/gvar`
|
||||
- 改进并发安全基准测试脚本;
|
||||
- 修改`garray.StringArray`为`garray.StrArray`;
|
||||
- 增加了更多的功能方法;
|
||||
1. `gdes`
|
||||
- 规范化修改方法名称;
|
||||
1. `gstr`
|
||||
- 增加`Camel/Snake`相关命名转换方法;
|
||||
- 增加了更多的功能方法;
|
||||
1. `genv`
|
||||
- 增加了更多的功能方法;
|
||||
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gvalid`校验`struct`时的`tag`自定义错误失效的问题;
|
||||
1. 修复`gcfg`配置管理模块在特定情况下的内容类型自动识别失败问题;
|
||||
1. 修复`gqueue`在用户主动关闭队列时的并发安全问题;
|
||||
1. 修复`session`在开发者设置的`TTL`过大时的整型变量溢出问题;
|
||||
132
TODO.MD
132
TODO.MD
@ -1,132 +0,0 @@
|
||||
# ON THE WAY
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. Cookie&Session数据池化处理;
|
||||
1. ghttp.Client增加proxy特性;
|
||||
1. gtime增加对时区转换的封装,并简化失去转换时对类似+80500时区的支持;
|
||||
1. orm增加sqlite对Save方法的支持(去掉触发器语句);
|
||||
1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps);
|
||||
1. ghttp增加返回数据压缩机制;
|
||||
1. ghttp.Server增加proxy功能特性,本地proxy和远程proxy,本地即将路由规则映射;远程即反向代理;
|
||||
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替换;
|
||||
1. 文档完善:
|
||||
- gconv struct tag、
|
||||
- 控制器及执行对象注册的Init&Shut方法、
|
||||
- ghttp.Response&ServeFile、gfcache、gproc shell执行、
|
||||
- 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
|
||||
1. gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
|
||||
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题,并完善文档(参考:https://www.cnblogs.com/kex1n/p/6502002.html);
|
||||
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. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作;
|
||||
3. ghttp.Response对象完善并改进数据返回方法(Write/WriteString);
|
||||
4. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作;
|
||||
5. 增加fsnotify包支持;
|
||||
6. 改进gcfg和gview的文件自动更新机制;
|
||||
7. 将模板变量进行暴露,以便应用端可以进行灵活控制;
|
||||
8. 跟踪第三方mxj包的issue问题:https://github.com/clbanning/mxj/issues/48;
|
||||
9. gdb Where方法参数的改进,研究是否可以将string参数类型修改为interface{};
|
||||
10. gpage分页控制功能;
|
||||
11. https支持;
|
||||
12. ghttp.Server日志中增加请求时间和返回时间,以便计算执行时间差;
|
||||
13. 由于去掉了gdb的单例模式,并且将gins的部分对象封装迁移到了g包中,需要同时梳理文档,完善修改;
|
||||
14. 在代码中增加https与http同时开启使用的示例代码,这块大家问得比较多;
|
||||
15. ghttp.Server多个事件之间通过ghttp.Request.Param自定义参数传参;
|
||||
16. 研究是否增加配置文件目录检索功能,特别是如何友好改进开发环境的配置文件默认目录问题;
|
||||
17. 增加ghttp.Server不同状态码的自定义处理方法;
|
||||
18. ghttp.Server平滑重启方案;
|
||||
19. 完善gconv类型转换功能,增加time.Time/time.Duration类型转换,并增加benchmark测试脚本
|
||||
20. 当二进制参数为nil时,gjson.LoadContent并将gjson.Json对象ToMap时会报错;
|
||||
21. 改进控制器及执行对象注册,更友好地支持动态路由注册,例如:注册规则为 /channel/:name,现有的控制器及执行对象注册很难友好支持这种动态形式;
|
||||
22. 当前gpage分页包的输出标签不支持li,大多数CSS框架都是li+a标签模式,需要提供可更加灵活的定制化功能实现;
|
||||
23. 平滑重启机制改进,以便于开发阶段调试;
|
||||
24. 对grpool进行优化改进,包括属性原子操作封装采用gtype实现,修正设计BUG:https://github.com/gogf/gf/issues/6;
|
||||
25. gredis增加redis密码支持;
|
||||
26. 改进ghttp.Server平滑重启机制,当新进程接管服务后,再使用进程间通信方式通知父进程销毁;
|
||||
27. gproc进程间通信增加分组特性,不同的进程间可以通过进程ID以及分组名称发送/获取进程消息;
|
||||
28. ORM增加获取被执行的sql语句的方法;
|
||||
29. gdb增加查询缓存特性;
|
||||
30. gpage分页增加对自定义后缀的支持,如:2.html, 2.php等等;
|
||||
31. gvalid包增加struct tag的校验规则、自定义错误提示信息绑定的支持特性;
|
||||
32. 增加文件缓存包,可根据fsnotify机制进行缓存更新;
|
||||
33. *any/:name路由匹配路由改进支持不带名字的*/:路由规则;
|
||||
34. ghttp静态文件服务改进(特别是403返回状态的修改);
|
||||
35. map转struct增加对tag的支持;
|
||||
36. gcache检查在i386下的int64->int转换问题;
|
||||
37. ghttp获取参数支持直接转struct功能;
|
||||
38. gfsnotify增加对于目录的监控;
|
||||
39. 检查windows下的平滑重启失效问题;
|
||||
40. ghttp.Server的Cookie及Session锁机制优化(去掉map锁机制);
|
||||
41. 解决glog串日志情况;
|
||||
42. glog增加对日志文件名称的生成规则设定,支持时间格式规则;
|
||||
43. ghttp日志增加客户端IP信息;
|
||||
44. 完善gform配置管理说明,g.DB/Database和gdb.New的区别;
|
||||
1. 完善配置管理章节,说明默认的配置文件更改方式;
|
||||
1. 服务注册时判断方法定义满足规范时才执行绑定,否则提示WARN信息;
|
||||
1. `gfsnotify`增加添加监听文件时的监听ID返回,以便调用端删除监听时只删除自己添加的监听,而不影响其他对该同一文件的监听回调;
|
||||
1. `gfsnotify`针对添加目录监听时无法使用多个`Watcher`,考虑改进,并考虑动态扩容全局`Watcher`方案;
|
||||
1. 由于系统对inotify实例数量(`fs.inotify.max_user_instances`)以及队列大小(`fs.inotify.max_user_watches`)有限制,需要改进`gfsnotify`;
|
||||
1. WebServer事件回调允许对同一个路由规则绑定多个事件回调;
|
||||
1. gcfg/gview/ghttp等模块加上对临时文件目录的自动添加监听判断(基本是开发环境下,特别是windows环境),去掉临时文件的监听,避免临时文件过大引起的运行缓慢占用内存问题;
|
||||
1. 改进gfpool在文件指针变化时的更新;
|
||||
1. ghttp hook回调使用方式在注册路由比较多的时候,优先级可能使得开发者混乱,考虑方式便于管理;
|
||||
1. gform对于MySQL字段类型为datetime类型的时区问题分析;
|
||||
1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行;
|
||||
1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性;
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp.Response增加输出内容后自动退出当前请求机制,不需要用户手动return,参考beego如何实现;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
1. gdb执行数据写入时,如果参数为struct/[]struct,自动映射与表字段对应关系,不再使用gconv标签标识;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
|
||||
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
|
||||
1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性;
|
||||
1. ghttp.Client自动Close机制;
|
||||
1. ghttp路由功能增加分组路由特性;
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
1. gview中的template标签失效问题;
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,9 +8,10 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -20,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{}
|
||||
}
|
||||
|
||||
@ -42,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),
|
||||
}
|
||||
}
|
||||
@ -77,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,
|
||||
}
|
||||
}
|
||||
@ -89,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>.
|
||||
@ -154,48 +163,60 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *Array) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
@ -226,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:]
|
||||
@ -282,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]
|
||||
@ -411,7 +453,7 @@ func (a *Array) Clone() (newArray *Array) {
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -433,18 +475,7 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
func (a *Array) doSearch(value interface{}) int {
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
@ -459,12 +490,15 @@ func (a *Array) doSearch(value interface{}) int {
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -493,32 +527,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 {
|
||||
@ -527,7 +545,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,
|
||||
@ -581,27 +599,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.
|
||||
@ -628,6 +646,9 @@ func (a *Array) Reverse() *Array {
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -654,7 +675,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -666,7 +687,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -701,7 +722,8 @@ func (a *Array) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -709,8 +731,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -723,9 +744,6 @@ func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -765,3 +783,18 @@ func (a *Array) FilterEmpty() *Array {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,8 +8,9 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"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,56 +164,68 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *IntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *IntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -228,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:]
|
||||
@ -281,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]
|
||||
@ -422,7 +469,7 @@ func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewIntArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -444,6 +491,10 @@ func (a *IntArray) Contains(value int) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -451,17 +502,19 @@ func (a *IntArray) Search(value int) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -490,32 +543,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 {
|
||||
@ -524,7 +561,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,
|
||||
@ -578,27 +615,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.
|
||||
@ -625,6 +662,9 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -651,7 +691,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -663,7 +703,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -681,7 +721,8 @@ func (a *IntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -689,8 +730,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -703,9 +743,6 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -730,3 +767,18 @@ func (a *IntArray) FilterEmpty() *IntArray {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,7 +8,9 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"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,56 +152,68 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *StrArray) InsertBefore(index int, value string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *StrArray) InsertAfter(index int, value string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *StrArray) Remove(index int) string {
|
||||
func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return ""
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
rear := append([]string{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *StrArray) InsertAfter(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]string{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *StrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *StrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -214,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:]
|
||||
@ -267,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]
|
||||
@ -408,7 +457,7 @@ func (a *StrArray) Clone() (newArray *StrArray) {
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewStrArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -426,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 {
|
||||
@ -440,17 +506,19 @@ func (a *StrArray) Search(value string) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *StrArray) Unique() *StrArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -479,32 +547,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 {
|
||||
@ -513,7 +565,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,
|
||||
@ -567,27 +619,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.
|
||||
@ -614,6 +666,9 @@ func (a *StrArray) Reverse() *StrArray {
|
||||
func (a *StrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
@ -640,7 +695,7 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -652,7 +707,7 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -681,7 +736,8 @@ func (a *StrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a StrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -689,8 +745,7 @@ func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -703,9 +758,6 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *StrArray) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -730,3 +782,18 @@ func (a *StrArray) FilterEmpty() *StrArray {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *StrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,25 +8,28 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@ -45,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,
|
||||
}
|
||||
@ -74,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
|
||||
}
|
||||
@ -94,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
|
||||
})
|
||||
}
|
||||
|
||||
@ -117,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
|
||||
}
|
||||
@ -131,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 {
|
||||
@ -148,38 +156,46 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedArray) Get(index int) interface{} {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedArray) Remove(index int) interface{} {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
@ -193,41 +209,53 @@ func (a *SortedArray) RemoveValue(value interface{}) bool {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedArray) PopLeft() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
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
|
||||
}
|
||||
@ -236,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:]
|
||||
@ -249,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]
|
||||
@ -360,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()
|
||||
@ -397,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 + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -427,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()
|
||||
}
|
||||
@ -447,7 +484,7 @@ func (a *SortedArray) Unique() *SortedArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -462,7 +499,7 @@ func (a *SortedArray) Clone() (newArray *SortedArray) {
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
|
||||
return NewSortedArrayFrom(array, a.comparator, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -479,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
|
||||
}
|
||||
@ -496,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,
|
||||
@ -539,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))
|
||||
@ -592,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()
|
||||
@ -604,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()
|
||||
@ -639,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()
|
||||
@ -668,11 +697,9 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -733,3 +760,31 @@ func (a *SortedArray) FilterEmpty() *SortedArray {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it panics.
|
||||
func (a *SortedArray) getComparator() func(a, b interface{}) int {
|
||||
if a.comparator == nil {
|
||||
panic("comparator is missing for sorted array")
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,22 +8,25 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// 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,97 +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
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedIntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
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:]
|
||||
@ -231,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]
|
||||
@ -388,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 + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -418,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()
|
||||
}
|
||||
@ -438,7 +481,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -453,7 +496,7 @@ func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewSortedIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -487,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,
|
||||
@ -530,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))
|
||||
@ -583,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()
|
||||
@ -595,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()
|
||||
@ -613,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)
|
||||
@ -621,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()
|
||||
@ -640,10 +669,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -680,3 +706,31 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortInt(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedIntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedIntArray) getComparator() func(a, b int) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorInt
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,22 +8,26 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// 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,38 +127,46 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedStrArray) Get(index int) string {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedStrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedStrArray) Remove(index int) string {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return ""
|
||||
return "", false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
@ -163,52 +180,72 @@ func (a *SortedStrArray) RemoveValue(value string) bool {
|
||||
}
|
||||
|
||||
// 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:]
|
||||
@ -216,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]
|
||||
@ -358,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) {
|
||||
@ -373,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 + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -403,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()
|
||||
}
|
||||
@ -423,7 +483,7 @@ func (a *SortedStrArray) Unique() *SortedStrArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -438,7 +498,7 @@ func (a *SortedStrArray) Clone() (newArray *SortedStrArray) {
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedStrArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewSortedStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
@ -472,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,
|
||||
@ -515,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)
|
||||
@ -568,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()
|
||||
@ -580,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()
|
||||
@ -609,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)
|
||||
@ -617,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()
|
||||
@ -636,10 +682,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -676,3 +719,31 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortStr(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedStrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedStrArray) getComparator() func(a, b string) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorStr
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
281
container/garray/garray_z_example_any_test.go
Normal file
281
container/garray/garray_z_example_any_test.go
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
// A normal array.
|
||||
a := garray.New()
|
||||
|
||||
// Adding items.
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// Print the array length.
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// Print the array items.
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Retrieve item by index.
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// Check item existence.
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// Insert item before specified index.
|
||||
a.InsertAfter(9, 11)
|
||||
// Insert item after specified index.
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Modify item by index.
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Search item and return its index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// Remove item by index.
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Empty the array, removes all items of it.
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6 true
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleArray_Iterator() {
|
||||
array := garray.NewArrayFrom(g.Slice{"a", "b", "c"})
|
||||
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
|
||||
// with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func ExampleArray_Reverse() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
fmt.Println(array.Reverse().Slice())
|
||||
|
||||
// Output:
|
||||
// [9 8 7 6 5 4 3 2 1]
|
||||
}
|
||||
|
||||
func ExampleArray_Shuffle() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
fmt.Println(array.Shuffle().Slice())
|
||||
}
|
||||
|
||||
func ExampleArray_Rands() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func ExampleArray_PopRand() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func ExampleArray_Join() {
|
||||
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
|
||||
fmt.Println(array.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func ExampleArray_Chunk() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
fmt.Println(array.Chunk(2))
|
||||
|
||||
// Output:
|
||||
// [[1 2] [3 4] [5 6] [7 8] [9]]
|
||||
}
|
||||
|
||||
func ExampleArray_PopLeft() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopLefts() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopRight() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopRights() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_Contains() {
|
||||
var array garray.StrArray
|
||||
array.Append("a")
|
||||
fmt.Println(array.Contains("a"))
|
||||
fmt.Println(array.Contains("A"))
|
||||
fmt.Println(array.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleArray_Merge() {
|
||||
array1 := garray.NewFrom(g.Slice{1, 2})
|
||||
array2 := garray.NewFrom(g.Slice{3, 4})
|
||||
slice1 := g.Slice{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
|
||||
func ExampleArray_FilterEmpty() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
|
||||
func ExampleArray_FilterNil() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
28
container/garray/garray_z_example_str_test.go
Normal file
28
container/garray/garray_z_example_str_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func ExampleStrArray_Walk() {
|
||||
var array garray.StrArray
|
||||
tables := g.SliceStr{"user", "user_detail"}
|
||||
prefix := "gf_"
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value string) string {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// 创建普通的数组
|
||||
a := garray.New()
|
||||
|
||||
// 添加数据项
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// 获取当前数组长度
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// 获取当前数据项列表
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 获取指定索引项
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// 查找指定数据项是否存在
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// 在指定索引前插入数据项
|
||||
a.InsertAfter(9, 11)
|
||||
// 在指定索引后插入数据项
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 修改指定索引的数据项
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 搜索数据项,返回搜索到的索引位置
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// 删除指定索引的数据项
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 清空数组
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
// 随机返回两个数据项(不删除)
|
||||
fmt.Println(array.Rands(2))
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 9
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_merge() {
|
||||
array1 := garray.NewFrom([]interface{}{1, 2})
|
||||
array2 := garray.NewFrom([]interface{}{3, 4})
|
||||
slice1 := []interface{}{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -20,41 +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(array.Remove(-1), nil)
|
||||
gtest.Assert(array.Remove(100000), nil)
|
||||
|
||||
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()
|
||||
@ -64,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() {
|
||||
expect := []interface{}{1, 1, 2, 3}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
@ -147,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"}
|
||||
@ -159,185 +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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []interface{}{4, 5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
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, 6})
|
||||
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, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []interface{}{4, 5, 6})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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"}
|
||||
@ -346,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)
|
||||
|
||||
@ -404,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)
|
||||
|
||||
@ -436,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
|
||||
@ -471,123 +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.Case(t, func() {
|
||||
gtest.Assert(array.RemoveValue("e"), false)
|
||||
gtest.Assert(array.RemoveValue("b"), true)
|
||||
gtest.Assert(array.RemoveValue("a"), true)
|
||||
gtest.Assert(array.RemoveValue("c"), true)
|
||||
gtest.Assert(array.RemoveValue("f"), false)
|
||||
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 T struct {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.Array
|
||||
}
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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})
|
||||
})
|
||||
// Map
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
|
||||
array := garray.NewArrayFromCopy(values)
|
||||
gtest.Assert(array.FilterNil().Slice(), values)
|
||||
t.Assert(array.FilterNil().Slice(), values)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
|
||||
gtest.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_FilterEmpty(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
|
||||
gtest.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
|
||||
gtest.Assert(array.FilterEmpty(), 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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -21,37 +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.Remove(-1), 0)
|
||||
gtest.Assert(array.Remove(100000), 0)
|
||||
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()
|
||||
@ -61,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() {
|
||||
expect := []int{1, 1, 2, 3}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
@ -147,260 +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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []int{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []int{4, 5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
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, 6})
|
||||
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, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []int{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []int{4, 5, 6})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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)
|
||||
|
||||
@ -426,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)
|
||||
|
||||
@ -471,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
|
||||
@ -505,112 +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.Case(t, func() {
|
||||
gtest.Assert(array.RemoveValue(99), false)
|
||||
gtest.Assert(array.RemoveValue(20), true)
|
||||
gtest.Assert(array.RemoveValue(10), true)
|
||||
gtest.Assert(array.RemoveValue(20), false)
|
||||
gtest.Assert(array.RemoveValue(88), false)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
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 T struct {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.IntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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})
|
||||
})
|
||||
// Map
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
//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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
gtest.Assert(array.FilterEmpty(), 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})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -21,38 +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.Remove(-1), "")
|
||||
gtest.Assert(array.Remove(100000), "")
|
||||
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()
|
||||
@ -60,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() {
|
||||
expect := []string{"1", "1", "2", "3"}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
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) {
|
||||
@ -145,267 +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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
gtest.Assert(chunks[1], []string{"4", "5"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
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", "6"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
gtest.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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)
|
||||
|
||||
@ -431,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)
|
||||
|
||||
@ -475,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
|
||||
@ -509,111 +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.Case(t, func() {
|
||||
gtest.Assert(array.RemoveValue("e"), false)
|
||||
gtest.Assert(array.RemoveValue("b"), true)
|
||||
gtest.Assert(array.RemoveValue("a"), true)
|
||||
gtest.Assert(array.RemoveValue("c"), true)
|
||||
gtest.Assert(array.RemoveValue("f"), false)
|
||||
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 T struct {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.StrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","2","3"]`),
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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"})
|
||||
})
|
||||
// Map
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "2", "3"},
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
gtest.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
gtest.Assert(array.FilterEmpty(), 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"})
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -21,355 +21,441 @@ 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)
|
||||
|
||||
gtest.Assert(array1.Remove(-1), 0)
|
||||
gtest.Assert(array1.Remove(100000), 0)
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
i1 := array1.Remove(2)
|
||||
gtest.Assert(i1, 3)
|
||||
gtest.Assert(array1.Search(5), 2)
|
||||
v, ok = array1.Remove(-100000)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
// 再次删除剩下的数组中的第一个
|
||||
i2 := array1.Remove(0)
|
||||
gtest.Assert(i2, 0)
|
||||
gtest.Assert(array1.Search(5), 1)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []int{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []int{4, 5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(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, 6})
|
||||
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, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []int{1, 2, 3})
|
||||
gtest.Assert(chunks[1], []int{4, 5, 6})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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() {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.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_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_LockFunc(t *testing.T) {
|
||||
gtest.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)
|
||||
@ -394,13 +480,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)
|
||||
|
||||
@ -426,13 +512,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
|
||||
@ -449,37 +535,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
|
||||
@ -489,112 +595,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.Case(t, func() {
|
||||
gtest.Assert(array.RemoveValue(99), false)
|
||||
gtest.Assert(array.RemoveValue(20), true)
|
||||
gtest.Assert(array.RemoveValue(10), true)
|
||||
gtest.Assert(array.RemoveValue(20), false)
|
||||
gtest.Assert(array.RemoveValue(88), false)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
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 T struct {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedIntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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})
|
||||
})
|
||||
// Map
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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 TestSortedIntArray_FilterEmpty(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
gtest.Assert(array.FilterEmpty(), 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})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -20,362 +20,460 @@ 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(-1), "")
|
||||
gtest.Assert(array1.Remove(100000), "")
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
gtest.Assert(array1.Remove(2), "c")
|
||||
gtest.Assert(array1.Get(2), "d")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Contains("c"), false)
|
||||
v, ok = array1.Remove(100000)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
gtest.Assert(array1.Remove(0), "a")
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(array1.Contains("a"), false)
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
|
||||
// 此时array1里的元素只剩下2个
|
||||
gtest.Assert(array1.Remove(1), "d")
|
||||
gtest.Assert(array1.Len(), 1)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
gtest.Assert(chunks[1], []string{"4", "5"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(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", "6"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
gtest.Assert(len(chunks), 2)
|
||||
gtest.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
gtest.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
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() {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
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(), 3)
|
||||
t.Assert(array2, []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@ -401,13 +499,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)
|
||||
|
||||
@ -433,13 +531,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
|
||||
@ -457,39 +555,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
|
||||
@ -499,111 +619,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.Case(t, func() {
|
||||
gtest.Assert(array.RemoveValue("e"), false)
|
||||
gtest.Assert(array.RemoveValue("b"), true)
|
||||
gtest.Assert(array.RemoveValue("a"), true)
|
||||
gtest.Assert(array.RemoveValue("c"), true)
|
||||
gtest.Assert(array.RemoveValue("f"), false)
|
||||
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 T struct {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedStrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","3","2"]`),
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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"})
|
||||
})
|
||||
// Map
|
||||
gtest.Case(t, func() {
|
||||
var t *T
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "3", "2"},
|
||||
}, &t)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t.Name, "john")
|
||||
gtest.Assert(t.Array.Slice(), 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 TestSortedStrArray_FilterEmpty(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
gtest.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
gtest.Assert(array.FilterEmpty(), 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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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()
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user