mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
3084 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f326dcac5 | |||
| aa294ea5df | |||
| 6afc725b61 | |||
| ec01693773 | |||
| b0cf501782 | |||
| c2fb7ada0a | |||
| d0a8e60ace | |||
| ab36bb8842 | |||
| 0b3cd7b7ae | |||
| 10ed04cdb8 | |||
| e09704a408 | |||
| ade9ae3c0b | |||
| 9cf6124c4c | |||
| 6d323cc529 | |||
| aea9f6fe18 | |||
| 8a27463e44 | |||
| 47ee2cba51 | |||
| 531cc7b864 | |||
| 54bdabd94d | |||
| bb6e8fe7a8 | |||
| d5d199ebef | |||
| 158a4589d2 | |||
| 84c0f456c0 | |||
| 3fcd6ef877 | |||
| 4e2d378145 | |||
| d64898c59a | |||
| 3bff71b3fc | |||
| 8343d1cd0e | |||
| 5c23c0cecd | |||
| f580713478 | |||
| 3c58b8d7fa | |||
| 072d5f9760 | |||
| f8067f5dd5 | |||
| ea354d10cc | |||
| 1724a26957 | |||
| 46dc68dfd5 | |||
| 12fdfbf8b2 | |||
| 68bdf7deb4 | |||
| 2362c453ec | |||
| 50f6b6e0f0 | |||
| 88a9eef8a6 | |||
| 308e13a546 | |||
| a0b1fefdbb | |||
| 3edbcb7bf9 | |||
| cb78953b38 | |||
| a1ddac4e6b | |||
| 456697ea99 | |||
| 8acffd1186 | |||
| 814450fd17 | |||
| 1365c1d277 | |||
| 30be5c5e49 | |||
| 932cd9d5bb | |||
| 7b5f17c16b | |||
| b5e8e68713 | |||
| 3a803ac39f | |||
| d27db119a0 | |||
| f54d0a339c | |||
| d83b676c60 | |||
| def3dc364f | |||
| 298aa5f040 | |||
| e4d56e7ad9 | |||
| 8eb9fdfcd0 | |||
| 3db5358dcc | |||
| d1fe0670fc | |||
| ee614f9c6b | |||
| 0fce4edcd3 | |||
| a34f52ae5e | |||
| da465bb030 | |||
| d23b24dfba | |||
| 22941276a5 | |||
| 20a50cb198 | |||
| f0cd3c084e | |||
| d9c1e1f576 | |||
| aa6705f493 | |||
| 3f526c3819 | |||
| 6f5058c9b6 | |||
| 4935416b94 | |||
| afa58ed45b | |||
| 42bccb4c23 | |||
| b2b0a1828e | |||
| 5eec9ce7b1 | |||
| 7812f41b43 | |||
| 0bd3537a78 | |||
| fdf09b5978 | |||
| 17d7c92b9d | |||
| b6a7788240 | |||
| 00ccce7bb8 | |||
| 54b564b871 | |||
| 5882a9dc21 | |||
| 144249fcff | |||
| 31ce55eb4d | |||
| 255b502460 | |||
| 073869b354 | |||
| 9fdf20b3e2 | |||
| 2461ef9f29 | |||
| f8f13bd905 | |||
| 948cb9ff7c | |||
| fa39b9ee54 | |||
| 6d09328d6d | |||
| 77fc8866bf | |||
| a5cc03ff25 | |||
| 5ab959ba58 | |||
| 86b90ed4b6 | |||
| 6ffdff7095 | |||
| 37c6320dd7 | |||
| 33367fd3ee | |||
| 3a1cd3d588 | |||
| 4fc27f6509 | |||
| f0b78253b2 | |||
| 2428b27168 | |||
| 01ba56c38b | |||
| 887cad3b96 | |||
| 14268aa1c0 | |||
| 23c00eb83f | |||
| 5f2047d61b | |||
| 1f9d86f015 | |||
| 8cb6086f73 | |||
| a887cedb99 | |||
| 1fb9be0628 | |||
| caf03b223b | |||
| f77d388d4f | |||
| d0cfeb2a8c | |||
| 0a6e4ebf18 | |||
| dd34892857 | |||
| af39eb4c9f | |||
| bd4948c084 | |||
| cea6cbc7db | |||
| 54ccbfef98 | |||
| 88b124b29a | |||
| 7eff3bc697 | |||
| c3161d58fd | |||
| e2a0cb97ae | |||
| 4fae47db63 | |||
| 48b5f37894 | |||
| ff1755d2db | |||
| f13ccd8527 | |||
| a97d74b15c | |||
| 8b48f16508 | |||
| 04eb654133 | |||
| 1a000396e2 | |||
| 3a658cdb85 | |||
| 3cb578488c | |||
| c845d1d93d | |||
| d322e67117 | |||
| 9ff17810a4 | |||
| 5f87591407 | |||
| 65c385c013 | |||
| 8094968605 | |||
| 7ac9c46f12 | |||
| 01b9fa8ac9 | |||
| adb4a1e6c0 | |||
| c371e5221a | |||
| c9529199a1 | |||
| f2e7f18d61 | |||
| 96044822a0 | |||
| c469d0277f | |||
| 221082b967 | |||
| 2c2acfb5b8 | |||
| 76b819ae3b | |||
| 43f1354e79 | |||
| 6e1c76efe6 | |||
| e517bf7b0e | |||
| 3b811c3434 | |||
| 9e3a49dd1b | |||
| 65d1648c30 | |||
| a4d80bf743 | |||
| a15ec0d677 | |||
| 7e5301c845 | |||
| 795ac4773e | |||
| c75cce0378 | |||
| 5760289d2a | |||
| 619e256138 | |||
| 7401fb09c9 | |||
| bb37e5ac88 | |||
| 69935f3d1c | |||
| 3dcd9b535b | |||
| e90f72b538 | |||
| 9efc2894d4 | |||
| 07c5ce0bbc | |||
| 3bbbe1db9c | |||
| d7b94428ae | |||
| 033e2c1d78 | |||
| 9345eb5e94 | |||
| 220ed74ad1 | |||
| 56f88f759a | |||
| ee1585fb24 | |||
| c72a9f2e1e | |||
| d4b502f14e | |||
| d32acabcef | |||
| 4943c3a9e0 | |||
| 58290ec9ea | |||
| 6468d55a81 | |||
| fe93d7b332 | |||
| 067514b74f | |||
| aff2fbdc54 | |||
| 805f60efa1 | |||
| b0b1b61280 | |||
| c09f22242f | |||
| 9892cc46e4 | |||
| 6e12aa4bf7 | |||
| 8731123030 | |||
| eaeb8e03ab | |||
| 6ded700f92 | |||
| e5613e8690 | |||
| 45fbb5326c | |||
| b09f4e9240 | |||
| c2f2ad7a28 | |||
| 0534994fa8 | |||
| 9486f6e7e9 | |||
| 343985ad12 | |||
| 0f67559995 | |||
| 280c3b4a86 | |||
| d4cb1666e5 | |||
| bc29e86e58 | |||
| 2e76268005 | |||
| 9abaf744f2 | |||
| 6232c873b8 | |||
| a3bab8db55 | |||
| 3bed3e4f5b | |||
| d045b4d2f5 | |||
| 572e71d76a | |||
| 09f83bdd58 | |||
| c91b83969c | |||
| 4cdddbd6e0 | |||
| 99455e328b | |||
| ee25d696eb | |||
| c17530a6db | |||
| 0a7e4be4ef | |||
| c1586a3b81 | |||
| 09f9d1cb7a | |||
| 00692c50da | |||
| 4663978fde | |||
| 130a67c72b | |||
| fd5b0fdf49 | |||
| f04715bccb | |||
| 3932e0b15f | |||
| b00de2c617 | |||
| 14e96069f2 | |||
| 079cf9d7b8 | |||
| ca5c0cc84d | |||
| 984e5f98f7 | |||
| b79e4a3ab7 | |||
| fab17f89c4 | |||
| 48c5c1e5a8 | |||
| b9bb1ecd46 | |||
| 2a8e2f9f2c | |||
| e5f1b74bd9 | |||
| cafd9098d7 | |||
| 739490c62b | |||
| 8de8a0c97a | |||
| 44089d70d4 | |||
| aabc1f8322 | |||
| 09493bbc36 | |||
| e389d73c65 | |||
| a9647a7bf3 | |||
| ce93b625d4 | |||
| bdf8244f7d | |||
| 7e81600772 | |||
| de17302ad0 | |||
| 25bdf10cc8 | |||
| 02c6deaec8 | |||
| 63d384b676 | |||
| 29a996e70e | |||
| 4978955404 | |||
| a7c4597c97 | |||
| 57a1a7607a | |||
| 31772d5eac | |||
| 77422f71f7 | |||
| 40225c5352 | |||
| ad48dc025b | |||
| 1215f67aac | |||
| b9907ae3ba | |||
| 656d1bbbd6 | |||
| 20c0e6d68f | |||
| b27259aded | |||
| 3658faafae | |||
| 0b05c9e3e7 | |||
| f3c25ecba9 | |||
| b9d6ac26d1 | |||
| f583f28953 | |||
| b6d60fc209 | |||
| f72ba0b64d | |||
| 8f24dc3549 | |||
| 4de74464ac | |||
| fa45715416 | |||
| df68ce23d2 | |||
| aee52fd56e | |||
| 000483cc6f | |||
| f4e8eff813 | |||
| 4143722f6f | |||
| b0675400e3 | |||
| eb5332ea11 | |||
| ff2e9b568b | |||
| 19c9f0a488 | |||
| 90e116fb9e | |||
| e28be8d114 | |||
| 36e05561cc | |||
| bc2ce19876 | |||
| 4c7e409e61 | |||
| a4bb80b6fd | |||
| bb1a95fff8 | |||
| 937845e67d | |||
| 7d84ab761b | |||
| 80daed6bdf | |||
| 1015fb5338 | |||
| e401ba0f43 | |||
| b4e6abc460 | |||
| a1fb5f18dc | |||
| 780d06b19e | |||
| 3a20883a81 | |||
| f0c25d8476 | |||
| 34e7913268 | |||
| e10ba4cf67 | |||
| 39bbca2a50 | |||
| e02bdd875d | |||
| 3f24159636 | |||
| 4769fbf383 | |||
| f25f8e8033 | |||
| 6a7722995e | |||
| 892619678e | |||
| 8a2f49a4d2 | |||
| 57a939e10b | |||
| 01930c9899 | |||
| 4f0b45aeac | |||
| b7b71795a4 | |||
| 9c41f2e3f8 | |||
| a8d6363e79 | |||
| 5dcdb66794 | |||
| 0c1de766bc | |||
| ad64a29145 | |||
| 885ddd36fc | |||
| 6a82fdb888 | |||
| 02f01323f9 | |||
| 5af426d265 | |||
| 10fc81f716 | |||
| 4b168f5579 | |||
| a0a9ecfa20 | |||
| ff36667dbc | |||
| 667b6aa4ca | |||
| e11698dc2a | |||
| dacc19ac98 | |||
| d330e9ac8f | |||
| 85d01f3d9e | |||
| 15fd815144 | |||
| 68a8a456c9 | |||
| 2bb80593a7 | |||
| 58992fa0ba | |||
| 0e3a8576b4 | |||
| 8586f31e03 | |||
| bca467cd15 | |||
| 21215f6b2f | |||
| cb6095dc17 | |||
| 8121b9c794 | |||
| 66450b0ecd | |||
| a4d7cf14a7 | |||
| 65fde53083 | |||
| acb2bc7d99 | |||
| 7b9a03d13c | |||
| b6df7b94d0 | |||
| 71399d1f65 | |||
| 0da5535bda | |||
| c74a33f04a | |||
| ecfedff32a | |||
| 34bf315623 | |||
| 3f030b1f6e | |||
| 8de176d73f | |||
| afc3367f31 | |||
| f56c2ed356 | |||
| e2fbce0420 | |||
| 80e8a58f45 | |||
| 26ef8c5872 | |||
| b840405af6 | |||
| 6aa39b3451 | |||
| c1b75b9071 | |||
| 65e1048715 | |||
| 9ed6ebbcff | |||
| e615528f17 | |||
| 261fa7e843 | |||
| 5ed5adc6df | |||
| 632b7f34ae | |||
| 9659f45fbb | |||
| 30a597cadc | |||
| e4e98caf8f | |||
| 6b5f111b6a | |||
| 8f0c0337c5 | |||
| d87413fb23 | |||
| 0340fb7124 | |||
| 6c19844a2f | |||
| dd0045e36f | |||
| 6acb2f46e6 | |||
| 578d8218bd | |||
| f754bdbb0e | |||
| 3ff77a87d4 | |||
| 2ead7606b2 | |||
| 1fdf85e3b8 | |||
| 18c4082bd2 | |||
| 45e9e108bd | |||
| ec55463856 | |||
| 8944635bf9 | |||
| c4b45d2400 | |||
| b444a661c2 | |||
| 085ac39231 | |||
| f5bd5a8748 | |||
| 478f901595 | |||
| 5073413ffc | |||
| 7bdb4fe660 | |||
| bf8afc96e4 | |||
| 817c3ce698 | |||
| c33378540a | |||
| c160500c7d | |||
| 35cf228d83 | |||
| b0eb76359e | |||
| 16a05f1450 | |||
| 1749af1416 | |||
| ae7685278d | |||
| b63786cd75 | |||
| e5c3444ea6 | |||
| 0ded168715 | |||
| a7b78b00b1 | |||
| a0c139f6a7 | |||
| b649c5aca8 | |||
| 56c13c2abf | |||
| e21cd301c2 | |||
| 656fb99b6a | |||
| 1725d247ae | |||
| 85aa850539 | |||
| 2cae1412f9 | |||
| f78d65833b | |||
| 2c542cfd46 | |||
| ab1f64a83e | |||
| 9105590c69 | |||
| fe1ca3cc22 | |||
| c906990b63 | |||
| 23f13c0f6a | |||
| be0df90d05 | |||
| 65b6fbba84 | |||
| 58a2e5a3b3 | |||
| 2866ee75a1 | |||
| f91b5b558a | |||
| 0db3a9f0f6 | |||
| 8442bab164 | |||
| 332de30fba | |||
| c5541b484f | |||
| 7b22355abb | |||
| 09be476fd9 | |||
| e2abee7ba4 | |||
| 6d62e071e4 | |||
| af289bcc7e | |||
| c12404b378 | |||
| 8a22b129dd | |||
| 28fe100761 | |||
| 0622b517c5 | |||
| d7da2b5657 | |||
| 5415d6dc52 | |||
| 1978a262a8 | |||
| 9e21052a3c | |||
| 9748b9f70b | |||
| a237495b66 | |||
| 00ac9069ee | |||
| 7e3d7b3575 | |||
| a96acf6eed | |||
| bbc43520fb | |||
| cea6e4c3d9 | |||
| 7e56bf700a | |||
| 2728ab9e42 | |||
| 3be19e58e1 | |||
| 82ea10a59b | |||
| 86a1721326 | |||
| eaef2d865e | |||
| dc1ad3dda4 | |||
| c94513f4aa | |||
| bae18a92b7 | |||
| a3dfebffb4 | |||
| f5920fff68 | |||
| e15a862cd2 | |||
| 22b7b2b953 | |||
| dde4b75022 | |||
| e936b376fb | |||
| b58c5311d0 | |||
| dbee5810dc | |||
| 8ea4b71e7a | |||
| 3e5b3a6dad | |||
| 61bc38fff6 | |||
| c047214a33 | |||
| 7d75f6e7c5 | |||
| a99cc34243 | |||
| 8db890c510 | |||
| 1db2c31ecc | |||
| 49edad0216 | |||
| 51fd87bc01 | |||
| 42673c383a | |||
| 9aeac45198 | |||
| d9a152b772 | |||
| cd9ea5697b | |||
| 551ceef0e1 | |||
| b81a1e7c9c | |||
| 0355ec6b21 | |||
| 076b15d71f | |||
| d35daa0f72 | |||
| d138c5bff2 | |||
| 66d56396ca | |||
| 0c62c62a7a | |||
| 7d6c9244cf | |||
| 7f33c101c8 | |||
| 07ac8aca41 | |||
| de4db409ee | |||
| 712416f490 | |||
| 0d8c9d5cfb | |||
| 9547277931 | |||
| 02849956e8 | |||
| dcf5975334 | |||
| 747ed6c64d | |||
| e586e4d109 | |||
| be2a0f4a18 | |||
| a184d16f06 | |||
| f6facc7507 | |||
| 7d7ceec1d6 | |||
| 3f4dff462d | |||
| 36927b4371 | |||
| 290200a490 | |||
| 57ccc13e0c | |||
| ef29ee4804 | |||
| 732664a1ff | |||
| f2ef7454c2 | |||
| e4023cadce | |||
| 92b9af5d9e | |||
| 51cc9c541f | |||
| 6ad7baeb2c | |||
| 266a4214c5 | |||
| 67a479a2cd | |||
| 459aaea6e1 | |||
| 9b93542e99 | |||
| 0b9931a3a4 | |||
| bb4f72d1ec | |||
| e86ee052cb | |||
| c6efb5ee3b | |||
| efcba5ecae | |||
| 416a5173ae | |||
| cc9b3a08d0 | |||
| 857ca4d978 | |||
| 0135d23b16 | |||
| babb84a798 | |||
| e92aaa0891 | |||
| 939a9267c6 | |||
| ff8633231f | |||
| 4e1bbd76f9 | |||
| 570814cf6e | |||
| 47a4c6b58c | |||
| 4fa2a879d7 | |||
| cd60a4aca7 | |||
| b664982be3 | |||
| 485785e023 | |||
| 5389a93ec8 | |||
| 634cacd435 | |||
| 61bf7a28d2 | |||
| e172c486c3 | |||
| da7ddd43f5 | |||
| 98e5fb1542 | |||
| f99b037c60 | |||
| 5a1209c82d | |||
| 8ddab7f0cc | |||
| 6cd8d008eb | |||
| f4f1145424 | |||
| d7c121430e | |||
| e23704d694 | |||
| e4ddeb6033 | |||
| 4cdd98dbf5 | |||
| ce16dad88f | |||
| b59ed1b943 | |||
| ea31af5364 | |||
| ac2a95534e | |||
| 1a7450b9e9 | |||
| 5ef8280c31 | |||
| 15deeb19b0 | |||
| d13bbb7a04 | |||
| f2dc26a7b3 | |||
| 299e16bed1 | |||
| d864120353 | |||
| be03b3cdda | |||
| cfa3d817af | |||
| 2c716a88c4 | |||
| 89d2a896f1 | |||
| 56648638aa | |||
| 741a790660 | |||
| 684fa9b9c9 | |||
| 4e91039254 | |||
| 195e167502 | |||
| 01a50b06cb | |||
| 9b663b4c2b | |||
| 655426c322 | |||
| ae819ad44b | |||
| 50ad3f0f26 | |||
| 413a8be037 | |||
| da659817c5 | |||
| 6d37515e14 | |||
| ae27afaeb4 | |||
| 30f462288e | |||
| 4862505c5e | |||
| 4b0a8f81d5 | |||
| f889df8d50 | |||
| 836be74d13 | |||
| 7f24f0637b | |||
| 31d793fafb | |||
| be274478d0 | |||
| 5b8c91a877 | |||
| 95a4690e69 | |||
| 33694c8b47 | |||
| 14b7c591d0 | |||
| 72043d0657 | |||
| e0a0fcbde2 | |||
| 58dd8a29db | |||
| 1f83c00ebf | |||
| 02e1d01f29 | |||
| e0db3c87cf | |||
| 903299728f | |||
| c617ad6a04 | |||
| 814649e7a3 | |||
| d1b69142c4 | |||
| 2d1593f78e | |||
| d61f7af60c | |||
| 65140d1293 | |||
| 85299cd447 | |||
| 091df972f1 | |||
| 748040fb0b | |||
| df02ca764f | |||
| 6f64c26349 | |||
| 072355dc2c | |||
| 3ca2cad449 | |||
| 05bb81cd71 | |||
| fd9246358a | |||
| 42c4b32720 | |||
| 2757647211 | |||
| 45787dd8e4 | |||
| 38b797b42f | |||
| e8f6ebc154 | |||
| e715ce0625 | |||
| aceb586bef | |||
| 15f9b69b36 | |||
| 0aabb7d990 | |||
| b036767fc0 | |||
| b967cf6224 | |||
| 1a596fe84d | |||
| 64fa8d5282 | |||
| 99d2186c8c | |||
| 24f759e00e | |||
| 66731c9c66 | |||
| e5ec9cd676 | |||
| e7b63839c8 | |||
| 2511c378f2 | |||
| 3de37873dc | |||
| 12514a0311 | |||
| 8a9dd169e2 | |||
| 84046a060a | |||
| a27eec7752 | |||
| 4539a9a4ab | |||
| cffa098373 | |||
| c663a0d9c5 | |||
| 5b83b2375e | |||
| 98d72fae25 | |||
| 041ebacb8c | |||
| 4f2a22dd0e | |||
| 4acc0e9876 | |||
| 7be24776d5 | |||
| 485706a676 | |||
| ea7d963f20 | |||
| 613958a4b6 | |||
| 44023ea91d | |||
| ef7f7e35f8 | |||
| 613a50428b | |||
| 3091f61a26 | |||
| 5776b2596d | |||
| 430102c995 | |||
| 2a2bb4f1e1 | |||
| 205243e8b9 | |||
| d16ce643fd | |||
| e94eb8f668 | |||
| 357084e788 | |||
| 9a757acc88 | |||
| 3fb1c07eac | |||
| 6050b14087 | |||
| 58c1c1bb16 | |||
| b6f6ab17f9 | |||
| 7bcba437a0 | |||
| abf199bf61 | |||
| 9ea2db5c81 | |||
| 7a6fb4a807 | |||
| c3a75f5568 | |||
| c850c420fd | |||
| e045aaaf07 | |||
| 0cd40caf0c | |||
| f2bb9d65c3 | |||
| 4e05795642 | |||
| 1df52637e8 | |||
| b83362cc2e | |||
| 274052511c | |||
| 32bc1ec064 | |||
| 5fa62e02c6 | |||
| bf3c3367a6 | |||
| 9c05682605 | |||
| 8f090739d9 | |||
| 7971177c58 | |||
| 5707763758 | |||
| 35a786d765 | |||
| 12fe41e34d | |||
| 5ffd362b6e | |||
| a423fba2a8 | |||
| 41d0832fa5 | |||
| 5e7b0c9303 | |||
| 0c43e7986f | |||
| bc1d76a796 | |||
| 6b9f72d973 | |||
| d980cff663 | |||
| 7de69db707 | |||
| 5e34aee2d7 | |||
| 797719d8d5 | |||
| 6c3aa6ede5 | |||
| 18459ec1bc | |||
| e727788f42 | |||
| 3df7711e74 | |||
| c1f856fa8e | |||
| 8ffc7699d6 | |||
| a3eff53c69 | |||
| 56c12ad7c3 | |||
| 77a0f59cd3 | |||
| 94768530cc | |||
| 79a233eb78 | |||
| 183d800f4c | |||
| 191ad20436 | |||
| c0c68d1e46 | |||
| ac6968edf1 | |||
| e22e1d0e4a | |||
| 42e27dd14c | |||
| d817047c98 | |||
| e15cd6ae89 | |||
| 17e6063c5c | |||
| a8b2a2ff33 | |||
| 4b3eb09492 | |||
| d1c09cb21d | |||
| 1188793f8f | |||
| 09b8df1818 | |||
| 6192d32501 | |||
| a6a8d787e4 | |||
| e5c6d3f777 | |||
| 1bfa792ea9 | |||
| 8ef4f68215 | |||
| 493f5dcff2 | |||
| 2cf84e020f | |||
| e21bd9b4cb | |||
| ded94276a8 | |||
| a19ba3d530 | |||
| e0674ee7fe | |||
| 2481435829 | |||
| e48b565e18 | |||
| f6c2206b88 | |||
| 4717e01708 | |||
| 17861fc45d | |||
| a92c31168a | |||
| 940dcfb8c4 | |||
| 8dd9483572 | |||
| 4e7b33bde3 | |||
| 977780736f | |||
| aacfa1bd96 | |||
| 862ef57e0f | |||
| fa5499373a | |||
| f901f19714 | |||
| 6bde7c61b4 | |||
| 085c887b33 | |||
| 920c97af79 | |||
| f887c9f44b | |||
| 18ceebeffd | |||
| 854747574d | |||
| 7eb3b32b26 | |||
| 54dd11f511 | |||
| 1bc0635f8b | |||
| bea54b445e | |||
| bc735fee51 | |||
| 82175b5de9 | |||
| cefa3638cf | |||
| cbfd92a21d | |||
| 310e178206 | |||
| e0c4f7ccbf | |||
| 17904bccd0 | |||
| 5b2f40a454 | |||
| b1113c328a | |||
| 26d823bd81 | |||
| 5a5809bd95 | |||
| c225a15746 | |||
| edd62f3b4f | |||
| 73e4a69383 | |||
| 5e32e8868e | |||
| d430f2f52e | |||
| 51dbd133a4 | |||
| 4eb48660df | |||
| d66d8585a2 | |||
| 89661301b0 | |||
| 5d804987b9 | |||
| 892d3abf38 | |||
| 4a1a8c9fc5 | |||
| 18ede3ae1f | |||
| 9ed79e9e86 | |||
| 5a7555a6ec | |||
| 82d04d612b | |||
| 9f2eeb23ba | |||
| c84e79a46d | |||
| 337c2d0953 | |||
| 71189c8fb0 | |||
| 322f5933ad | |||
| 1e80dc71eb | |||
| 198a6c8e33 | |||
| 0fdb935c3d | |||
| dcafba8a2d | |||
| 131214151c | |||
| dd4e08e88d | |||
| b7b4067973 | |||
| 02478371f2 | |||
| 750972e7c6 | |||
| a9d7dc68a3 | |||
| 0a828aaed3 | |||
| f2bab9777d | |||
| 993bf897cf | |||
| df09d8c604 | |||
| 5904a1e7c0 | |||
| b826ed6b03 | |||
| 6eee72b336 | |||
| a2c5c577db | |||
| 6fa0ad5793 | |||
| 2891c8e29d | |||
| c3b3258194 | |||
| ef0aeb363b | |||
| c843661dfd | |||
| b2629b9bfa | |||
| ad2b3e6cc6 | |||
| cbe704333a | |||
| 45b45517c8 | |||
| 9d67cb4f34 | |||
| fe2eccfda4 | |||
| 81e54be0c1 | |||
| 2e49c33cc7 | |||
| e01e8dd4a2 | |||
| a1297b0402 | |||
| 53db88c630 | |||
| c99c13acd1 | |||
| 01a3dd1eb0 | |||
| 5383672d78 | |||
| 28f70d52ee | |||
| cfc1f8fbe4 | |||
| 1455e30350 | |||
| b46e9bb68a | |||
| 55a9c0738b | |||
| a72493f320 | |||
| 0d9d7b8e04 | |||
| 101db282c2 | |||
| ac78b2fa50 | |||
| e877eab033 | |||
| 68e760d13a | |||
| c323256812 | |||
| 26584cdbb4 | |||
| 64a5cedcdf | |||
| 40fedea408 | |||
| ffedfb7c0c | |||
| cc08ceb797 | |||
| 053e4f2fc8 | |||
| 727f58a24b | |||
| 99b6085235 | |||
| 006fb79967 | |||
| 5820e6ebe1 | |||
| 0776c35ddb | |||
| 471a37ef00 | |||
| 1242733735 | |||
| a6c9a7a944 | |||
| e782facdf3 | |||
| 077a41911b | |||
| cc60bc9dab | |||
| 425ca8aa3e | |||
| 73d2b7ed06 | |||
| 0048e2c8c4 | |||
| adee4ac8dd | |||
| 549adf6487 | |||
| b5502c5580 | |||
| 46bf669cdd | |||
| fa7a3e987d | |||
| b316b9c073 | |||
| 5b57c35522 | |||
| b9a07b104a | |||
| c7b379e53f | |||
| e0a3233ea3 | |||
| 1b588b14d8 | |||
| 2c839db941 | |||
| f9eaa8f930 | |||
| 59397fd8a5 | |||
| 6be582355c | |||
| ac73851f78 | |||
| 257897763d | |||
| 29d50994ae | |||
| b129ee3f04 | |||
| a47699bf6e | |||
| 66b8b591df | |||
| 7b47fe96dd | |||
| abc8e62d58 | |||
| 22e3fddf8d | |||
| ef36cf8446 | |||
| c5345239fc | |||
| a229960218 | |||
| 43180ef239 | |||
| e10240bd7c | |||
| a0ef6fce81 | |||
| 314bee2b4e | |||
| 050c93ee8d | |||
| feefcc98ef | |||
| 8f4ce91361 | |||
| f03f56ba4e | |||
| 457d552fa0 | |||
| be2fcf7f57 | |||
| ad6c4f5245 | |||
| db621e38d9 | |||
| 170af1ab00 | |||
| 31ef4e7b5a | |||
| f56f689970 | |||
| 17b73e7cb3 | |||
| 903f29a824 | |||
| 12f2193963 | |||
| 13ecbc263e | |||
| 7b9888c004 | |||
| 91cd4f96f0 | |||
| 95a44122dd | |||
| 2bd76dfdde | |||
| 26895294f9 | |||
| 4cd4559784 | |||
| 3fc96f2bd0 | |||
| 91dd9e2bf9 | |||
| 9c5642f141 | |||
| 00f0a743fc | |||
| 232751e3db | |||
| 1c600d5b20 | |||
| 9bc3b44a61 | |||
| 83729f18ad | |||
| fdb6e70322 | |||
| efaf3d591c | |||
| ef50eb6d6b | |||
| fb5a1f2306 | |||
| 9d4382d12e | |||
| 9ee76ecc93 | |||
| 0d2ca48d16 | |||
| f1857df5e2 | |||
| fc1dfb7ff9 | |||
| dc407bf293 | |||
| 4eb057227c | |||
| 9cd944fd77 | |||
| cd3593182a | |||
| 0f6820df9e | |||
| 0e158903c2 | |||
| 214d0513e5 | |||
| 75ca866991 | |||
| f22aa1b5d6 | |||
| 00d7ee93b2 | |||
| 537cbf983e | |||
| 4c54b1cfbc | |||
| e9ea58df64 | |||
| 65fff6feae | |||
| dbded5e753 | |||
| 3043645605 | |||
| 685bf56a30 | |||
| a4497ed547 | |||
| 28cb0bef25 | |||
| 114cdb2351 | |||
| a2bb8ad2f2 | |||
| 5a4de52900 | |||
| ff70e54e3e | |||
| fddc21670a | |||
| f02372cf58 | |||
| ab5f809074 | |||
| 121c1a0125 | |||
| 7678540270 | |||
| 248e6ff134 | |||
| 839ebd5b51 | |||
| fa64df6f91 | |||
| 0acd118c03 | |||
| 2b5244a54b | |||
| 2472dd5fac | |||
| 5899f676f7 | |||
| bb57dc1ae7 | |||
| 5a6c2c27df | |||
| 9f096fc63d | |||
| 4267aadd78 | |||
| ef77a54c7e | |||
| afb0af4afd | |||
| 9be92cc3d4 | |||
| 15aabfb4e7 | |||
| c83e899f1f | |||
| ebe90dcaa8 | |||
| 1c3ae11eba | |||
| b718aa88a2 | |||
| 6240c3d90b | |||
| c78f9d19f5 | |||
| 0ddacdd7e2 | |||
| f8486474aa | |||
| 906c54ce61 | |||
| f72d991c36 | |||
| 528f0e5434 | |||
| 88009ee278 | |||
| b192b7dd60 | |||
| 03d51bd18c | |||
| 5c6c932a75 | |||
| 92c3c136f9 | |||
| 5069436fd2 | |||
| 141ba2e951 | |||
| fae4dea37a | |||
| 860b22aba4 | |||
| 30dbccf99e | |||
| 9b2497bc57 | |||
| 0140808460 | |||
| 84aa30d9c2 | |||
| bc724deb5e | |||
| fbfc23211c | |||
| 3d4d3a763a | |||
| 046749566d | |||
| 3b1b8a8306 | |||
| 5e92747737 | |||
| 82ad7e2acc | |||
| 2d319d0856 | |||
| c060904f3f | |||
| a63c4b6441 | |||
| 9df860a202 | |||
| 2a1634fd6f | |||
| 2970864158 | |||
| 8e76d7a8ed | |||
| 50e5dd5bd0 | |||
| 7e2605188d | |||
| e5ae1cb85c | |||
| 1e78734f2c | |||
| d5fad88c56 | |||
| 35a81b868f | |||
| 84355c1ddd | |||
| 012121ea77 | |||
| 1e628b9edb | |||
| bfdeb6c4f5 | |||
| c42a9d6e50 | |||
| 083e32fd9e | |||
| 2a350fd3ab | |||
| 03928f1977 | |||
| a2771c7558 | |||
| 968e1db94d | |||
| bb0a3e09d6 | |||
| d109706ad3 | |||
| b958689264 | |||
| 859ea150ed | |||
| 91ca79b300 | |||
| 8210f40469 | |||
| 50ffaef33f | |||
| c25f88293b | |||
| 025cdd66c5 | |||
| 237f172ae5 | |||
| 4cd7e4e5a0 | |||
| e6688b9e86 | |||
| 65131c6f22 | |||
| 7667aca4c2 | |||
| 816791b9c1 | |||
| d6ea2220f7 | |||
| 69dd5db774 | |||
| adca9222ab | |||
| 7144aa6999 | |||
| fbad5f60eb | |||
| 266f592739 | |||
| 7c8bbcb3af | |||
| ba18e2bf6b | |||
| 5fefe97b87 | |||
| 5fba250a14 | |||
| 2606ad83ac | |||
| d450de8e0d | |||
| e4b0de0d4f | |||
| 2af4fd86cc | |||
| ddd171bc18 | |||
| 5ef4ef61f0 | |||
| 2679f92aa8 | |||
| cca438d77f | |||
| f2bc29e5c1 | |||
| fe7209e76d | |||
| 7c4a0453b7 | |||
| 97879834bc | |||
| 332535901f | |||
| e68e7a3224 | |||
| b935a8c652 | |||
| 65befd5ac4 | |||
| 78bdb5ef71 | |||
| 6eb7261dfd | |||
| 4f82be5bc0 | |||
| 3ac5772059 | |||
| eb723e47c2 | |||
| 8aa7f08350 | |||
| a54559d016 | |||
| 8e1f6abac5 | |||
| 7f3a2207a3 | |||
| 742c7913ea | |||
| 2d8ab726e2 | |||
| 702a296258 | |||
| c3c5414ce2 | |||
| ee3d375532 | |||
| e3f5c9175c | |||
| 7c24449a24 | |||
| 1c09846d3e | |||
| db94346863 | |||
| 3e6b9864d5 | |||
| 392c81ad69 | |||
| fe142c93fd | |||
| 578e7d634b | |||
| 6659a49869 | |||
| d9bc8b05e1 | |||
| 571405597b | |||
| 1441ce7f5f | |||
| 071a587755 | |||
| c1db01425e | |||
| 7bfd48e2ab | |||
| e80309af32 | |||
| fa1814ff54 | |||
| d7b8a2684a | |||
| 8559cd299d | |||
| b2d3c7d1fd | |||
| 5100e0e8b7 | |||
| 8acb921ee3 | |||
| 9ee0f04e58 | |||
| fc88001a8c | |||
| fab6c4048d | |||
| 7b32791006 | |||
| aa04948319 | |||
| 5903eb8ceb | |||
| 211e62a8e7 | |||
| 64a9b06e64 | |||
| 6d81aa4462 | |||
| 406b6bf410 | |||
| 4e41d8aff8 | |||
| 017c6e4e1f | |||
| 7d3233c7ad | |||
| 3e2662582c | |||
| c94dee8191 | |||
| ab2ef13d99 | |||
| fac9ab5c01 | |||
| 420e0b9ca4 | |||
| ea0340db8e | |||
| 0bd1ea07a7 | |||
| c8c28770fb | |||
| 0dfd968824 | |||
| b84ca9cc13 | |||
| e5734425ba | |||
| 2274a10cfd | |||
| 302e234bfe | |||
| 2b942bf06b | |||
| 522f6cb455 | |||
| a757fbd37d | |||
| 33567ef338 | |||
| b2a15c259e | |||
| d12409b118 | |||
| 8aed1eca13 | |||
| f389688caa | |||
| 7003c284d0 | |||
| facb2949c3 | |||
| 1b1355a595 | |||
| d76e4c8aed | |||
| cc1224e032 | |||
| 09de115670 | |||
| 23110b5d19 | |||
| a326f4a989 | |||
| d21b9d58e1 | |||
| 9d362c3738 | |||
| b06580d343 | |||
| a4240bdfb7 | |||
| 1eab1cb367 | |||
| 2e38416e12 | |||
| 034a3f1808 | |||
| 531519e8f2 | |||
| 6a80091fef | |||
| 742653ce75 | |||
| a8c3d07d9f | |||
| df1ef5db78 | |||
| 4b15ab5e99 | |||
| cdc97e9b2b | |||
| bd84b97614 | |||
| d7eb1cca07 | |||
| 5856f74d83 | |||
| 563509c4a6 | |||
| f3983cd6b7 | |||
| 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 | |||
| 799e8214f8 | |||
| 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 | |||
| e3b8f374e5 | |||
| 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 | |||
| f01dca0895 | |||
| 55137b2aa3 | |||
| 22540921b3 | |||
| 6174097a07 | |||
| 52de11b1fe | |||
| 5a646179ad | |||
| 33ae93e050 | |||
| f3d859159d | |||
| 4fb01e68f7 | |||
| 2812a247aa | |||
| 2b46e765c4 | |||
| c7f911cae2 | |||
| 9f0548c03d | |||
| c578df06a3 | |||
| 8ed3cf9c97 | |||
| 2438f565e9 | |||
| 8230c72ec6 | |||
| d716037caa | |||
| 855a4ddb2c | |||
| 74be9fac18 | |||
| e9fba5a166 | |||
| 0a92df691b | |||
| e513cd10ed | |||
| b702d98700 | |||
| 99f1d9d0ed | |||
| 707b08c585 | |||
| f44c19868c | |||
| 13ab139afc | |||
| cacb2f142b | |||
| 11f0317e92 | |||
| 9a667c8803 | |||
| c1cce17934 | |||
| 5a92d7de0d | |||
| 53bf378868 | |||
| cd7c45c00c | |||
| f13a5ad82e | |||
| 2e9be609c8 | |||
| 2f2f6e1ffe | |||
| f3bd2b67f7 | |||
| 645cecdffb | |||
| 22e9965629 | |||
| 24ea9f9245 | |||
| 9dbde6e8f1 | |||
| fe0b34544d | |||
| 5e489d59b4 | |||
| 61f49574a9 | |||
| 2fabcb62a8 | |||
| 3bc3b652c1 | |||
| 042a6f12f5 | |||
| 5acce82e63 | |||
| 4732bf46ad | |||
| 5bed5a1532 | |||
| 5b7576430f | |||
| 356f4cd701 | |||
| c444630d1e | |||
| 8e40cded42 | |||
| 0e52d467d3 | |||
| 6665d62e7e | |||
| 5bdf1a71b8 | |||
| a34ca0ff4b | |||
| 7f0163d958 | |||
| 31f19b0eee | |||
| 93d0760898 | |||
| 4863e7a6ae | |||
| a161b44cc7 | |||
| 7072244420 | |||
| f68b66e606 | |||
| 4e7c6c1fb4 | |||
| d8a7e36478 | |||
| 8971ad8445 | |||
| 270af8accb | |||
| b0ef63fc9d | |||
| 6e1f8c3cfc | |||
| e58d7e8dda | |||
| 3a3384cf06 | |||
| ef2a9f6fd1 | |||
| 63f756f731 | |||
| bb1c27c36a | |||
| 87cd0703c0 | |||
| 6317d9de53 | |||
| 7acf16fdba | |||
| a52b454d3e | |||
| 816e075c52 | |||
| 9882b361a8 | |||
| 4415dcf1c1 | |||
| 42fd583bfd | |||
| c70bc7c96a | |||
| 02bd780a33 | |||
| 24a2192ce2 | |||
| d8ef8a1f5d | |||
| 745a913cfb | |||
| 13dba407a2 | |||
| 34e7c5f809 | |||
| d570624caa | |||
| f18312419b | |||
| 89f869dd44 | |||
| 20b64507b1 | |||
| 7443246e05 | |||
| f9e7823c14 | |||
| 52943b283c | |||
| 36403fdc08 | |||
| 0317f6812e | |||
| 5169137069 | |||
| 7d9bccf912 | |||
| 14f56ea18f | |||
| 1736d271d2 | |||
| 19755ad233 | |||
| cfdd043e4e | |||
| 78917ed5cb | |||
| 88684ca00a | |||
| 784983806a | |||
| cdb3b94e22 | |||
| 83dcc4a5e0 | |||
| a6c0b281a3 | |||
| 3120f24553 | |||
| ac9be6134b | |||
| 4c1b4f7858 | |||
| 1e45bf93d8 | |||
| e8dd3979b6 | |||
| 374ee4c0ea | |||
| 95411aff77 | |||
| 1999ef95c1 | |||
| b15075fdfe | |||
| 4d2b244319 | |||
| 4c3af63076 | |||
| 91bbff6ced | |||
| 26aab44ec8 | |||
| 2e10ce421b | |||
| 8eda69b11e | |||
| 7d7b242968 | |||
| 202419202f | |||
| 55078beed1 | |||
| d9f4e6eaa6 | |||
| 2ba0913bea | |||
| 8f2dcf21ff | |||
| 01b06e0745 | |||
| 665b5960c8 | |||
| eb6a7a4728 | |||
| 7df53ff55e | |||
| 8021f39710 | |||
| f2af08270b | |||
| c2f028848c | |||
| d9c7224861 | |||
| f59a1ada88 | |||
| 705ab1d33f | |||
| c07c4d7217 | |||
| b867b2a0bc | |||
| 872d674182 | |||
| 4682abafdf | |||
| b7d194cf52 | |||
| edf2366296 | |||
| 22af5be71f | |||
| f662ff8051 | |||
| 8c51121b3b | |||
| e9a0805801 | |||
| afadbc6621 | |||
| ca546fc30b | |||
| 7c7c168c3d | |||
| 16f0bb96db | |||
| 33a899d32e | |||
| f3a208f02f | |||
| 81fd3d06bb | |||
| 9227139cf8 | |||
| f2190e50b2 | |||
| c4537b4753 | |||
| 167d58490b | |||
| d36aceb9f1 | |||
| eb31922124 | |||
| bec9f5a847 | |||
| 506552c3a9 | |||
| 2bacc77224 | |||
| bc53f265af | |||
| 344f232c36 | |||
| 27b677b0c0 | |||
| a5a0e381bd | |||
| d528d7f5ab | |||
| 821c71bd8d | |||
| 604a10400d | |||
| 9219471f67 | |||
| fe5d2e5685 | |||
| 0a89daa513 | |||
| d6e6ddf996 | |||
| 5dbda8aedc | |||
| 134e4cf28f | |||
| 56a85abef7 | |||
| 80c6ceaf26 | |||
| a10f428715 | |||
| 597f7468e9 | |||
| 5db8851213 | |||
| 1d53d760d8 | |||
| 922e720d63 | |||
| 50018773b7 | |||
| df99036d41 | |||
| ae0fa888f0 | |||
| 18892fb66d | |||
| 5f2be10563 | |||
| a5a88222a6 | |||
| 4facdd5c9e | |||
| 76bc9bd385 | |||
| 364452f3bb | |||
| 4996755f11 | |||
| e33230a88f | |||
| 951ce46932 | |||
| 795c7395e6 | |||
| 27cf47bcd3 | |||
| 58a25c6f61 | |||
| 2d754f80b1 | |||
| f4e8fbe767 | |||
| 458318d374 | |||
| e3f54e1353 | |||
| 4374996073 | |||
| 87295ef1fe | |||
| 81d4082b6a | |||
| d7e19bc3f3 | |||
| 34ef0ea792 | |||
| 2804834540 | |||
| add7dd5a45 | |||
| e40894ca45 | |||
| 28825f5395 | |||
| 6ca5141020 | |||
| fe4f8e1810 | |||
| 671157cb70 | |||
| f775479c3f | |||
| 64bb72842e | |||
| e1d4ba9d23 | |||
| a1edd83add | |||
| f7d6883405 | |||
| 8448a70646 | |||
| 4018bfa899 | |||
| 3cc9ce74e1 | |||
| eac60d845f | |||
| 835c045c92 | |||
| be15f85eae | |||
| 3e27ea0259 | |||
| 6abe660287 | |||
| a06ca31530 | |||
| 890865251b | |||
| 6af90cafee | |||
| 2e2363bb41 | |||
| 108ced2b0b | |||
| fcba650348 | |||
| 1b8a082942 | |||
| 40570cdb03 | |||
| 10451864e6 | |||
| 55e2646367 | |||
| c88839edb1 | |||
| 2a2cfc289c | |||
| 8bbeb186c2 | |||
| be4bf39719 | |||
| 459c69839a | |||
| a5407e57d9 | |||
| 5c749e7762 | |||
| b3fafc64f8 | |||
| c7b0763ab0 | |||
| 0cfdf60de5 | |||
| 096bff791d | |||
| 0cce858641 | |||
| bb45d8d578 | |||
| 7e43aa6b9d | |||
| 4ee7c82bf1 | |||
| 85caa40a3d | |||
| f4654bf446 | |||
| 94ed0bf9c9 | |||
| 22e3705d3e | |||
| 9b1cc6e9c7 | |||
| 9429c8ff83 | |||
| 056c6d4688 | |||
| 382356bc8d | |||
| 1deb3510f0 | |||
| dd7ae1b07a | |||
| 04a8755162 | |||
| 682f99a763 | |||
| 61282d6dab | |||
| ad540f7c25 | |||
| a4f191c1c6 | |||
| 43531c2680 | |||
| 635f5d36fd | |||
| 1becc4932c | |||
| e36dd06f22 | |||
| 8f2c62d444 | |||
| 21efde1a38 | |||
| d8b6466ed0 | |||
| d7b0228e9e | |||
| 9da1277b47 | |||
| a3fd0c9a4a | |||
| ad7375b44b | |||
| 05120b585d | |||
| 9e32d74c8c | |||
| 0e62510c6f | |||
| c492de4fa8 | |||
| 6308380541 | |||
| 2609db1aec | |||
| 6c54e73dbd | |||
| 21abe62633 | |||
| e30b2b0732 | |||
| dcb74ee9df | |||
| 82bf21e831 | |||
| 66355354fc | |||
| 4204125dce | |||
| e6aa9d3a46 | |||
| 9416cd1274 | |||
| 8b5ab846b2 | |||
| a15b93be90 | |||
| 38ee5f7d53 | |||
| 6f9bbbf416 | |||
| 143bc3d8e3 | |||
| ea75b1a936 | |||
| 0dde8c735e | |||
| 1ab7f00b91 | |||
| 3ef42bfbf0 | |||
| 05fec23457 | |||
| 7225e49aa0 | |||
| 14e9deb254 | |||
| f9569b387f | |||
| a0722ed51f | |||
| e9ace9b17a | |||
| 4791d10761 | |||
| c686b1c080 | |||
| 056d6ebbd9 | |||
| baa2cb68de | |||
| e4909b318b | |||
| f1b7cb37c6 | |||
| 734728fa9c | |||
| b373ace065 | |||
| bc7b5c8626 | |||
| f2b45622d6 | |||
| 77cb219057 | |||
| cf34d7bd56 | |||
| 5adc9be0d9 | |||
| 58b2efc900 | |||
| 28326606f5 | |||
| 91c98bbb60 | |||
| 63cd1128a7 | |||
| c0236d7dfa | |||
| d4051df5b6 | |||
| 88045417ff | |||
| 500efb5601 | |||
| 97fe8235da | |||
| e1164e935b | |||
| b26330aee1 | |||
| 2b083709b5 | |||
| 0ac45dc379 | |||
| 650916c22a | |||
| 2804183325 | |||
| 2dc2610621 | |||
| 1736e71e6b | |||
| 69ee5375b9 | |||
| 3ac0a66887 | |||
| 20e873a1fc | |||
| 142484d89c | |||
| 76a9f4ca14 | |||
| c4e5679d5c | |||
| b08d7c3c38 | |||
| 5092d8e6c5 | |||
| 74d625ff97 | |||
| f1119e28e8 | |||
| 3082c7f761 | |||
| 5f36614dd7 | |||
| 1dcc7a4887 | |||
| 41e9d35487 | |||
| 977c8b7ee3 | |||
| 939e6244ee | |||
| e764b2393d | |||
| 6384e75ed9 | |||
| 195cae6577 | |||
| c8cf46a5a7 | |||
| eba97277b2 | |||
| 4e19fbc5fb | |||
| 494b5bbae2 | |||
| 987ce709e5 | |||
| 35ad4d869f | |||
| 39d654e3f2 | |||
| 7fe9c641f4 | |||
| ee1414c010 | |||
| 8eb1b685a5 | |||
| 46768d6f91 | |||
| bb6fed3dc2 | |||
| 47e74d27bf | |||
| b830f9b96d | |||
| c85162a8a0 | |||
| ef4e128af7 | |||
| cce8ac5118 | |||
| cb7c3a9fa4 | |||
| b0f859cc91 | |||
| 6d4da529ee | |||
| 6c7e536eeb | |||
| 36c2648be8 | |||
| 419b58452f | |||
| b1835ea4e8 | |||
| a876b6133d | |||
| 03ff358da8 | |||
| 037f74c549 | |||
| a8caf4ad21 | |||
| 51d9fe5253 | |||
| 3218c89f17 | |||
| 02e467fb57 | |||
| ec994f3080 | |||
| 1181ab499c | |||
| 5424935fd9 | |||
| b9fbfb91bd | |||
| 62f66b4fec | |||
| eef9da9a41 | |||
| 7d32be3b6c | |||
| c2ad9f5fb9 | |||
| 4847fecdaa | |||
| f75383e0c5 | |||
| f683dccb6f | |||
| 6fb106b618 | |||
| 97956ad903 | |||
| 5d72a5b5ae | |||
| 1d7ded562c | |||
| 0d87b601cc | |||
| 33fbedffb8 | |||
| 2cfcbc82e1 | |||
| e1721cb1b6 | |||
| 1d264a6545 | |||
| 565e48f6f1 | |||
| 2b20c77bc0 | |||
| 1665d92136 | |||
| f128cb9f61 | |||
| 6d55b30b39 | |||
| 953ea55899 | |||
| 966c93af00 | |||
| 41a0b52939 | |||
| 8281d9cdd1 | |||
| b0ece3bd8f | |||
| 19a856549a | |||
| 65bd31bb65 | |||
| a3830f0820 | |||
| 5f8656eb41 | |||
| 141bee9c49 | |||
| aa44c0fb11 | |||
| 79e2d30849 | |||
| e8a9c7ac72 | |||
| 006054d13a | |||
| b39d653fe9 | |||
| d9e7d4249f | |||
| 34ad4ae166 | |||
| 69684f5023 | |||
| 2ed1854f0c | |||
| 09da0f7388 | |||
| 7f943c6b5f | |||
| ed3fc529ed | |||
| 2eb0511ef5 | |||
| 8bc1f457c7 | |||
| 77a727e1d7 | |||
| 16cc3c0fca | |||
| 96106ace92 | |||
| 13e528abb6 | |||
| 0d5dfe59b9 | |||
| 8fd88307f7 | |||
| 57244a9553 | |||
| fb8aac38dd | |||
| 8f953dabe5 | |||
| d63c2882fd | |||
| 952eb7df15 | |||
| dfc76ddb76 | |||
| b0cddc29e4 | |||
| dfc61cb273 | |||
| 05922e4f21 | |||
| 6cf4bf81e5 | |||
| ad43989944 | |||
| 31e7037e3e | |||
| 425d45e502 | |||
| 9ddb148fcb | |||
| e4d34abd7e | |||
| 16103c43eb | |||
| 7fad4b686c | |||
| cd00ac446b | |||
| e2906fba0b | |||
| acd5e72f6c | |||
| 8f1fd259d4 | |||
| d9041da426 | |||
| 269ddb04af | |||
| b1c2b9f4e0 | |||
| 7e9fe8ea98 | |||
| af81bf2bde | |||
| 55addaa8ea | |||
| 6ccc4d119f | |||
| c05a91cdc9 | |||
| 077d8b9f48 | |||
| 15a6680833 | |||
| 6c61704e60 | |||
| cfa0b99cd8 | |||
| c0bb8bef06 | |||
| 86f9d5c39d | |||
| 33f2ae5fc4 | |||
| ea1277d76c | |||
| ee89a06b3e | |||
| a31108e753 | |||
| 2bf9bc98a7 | |||
| d67975249c | |||
| d3a2bef9ba | |||
| b7f67e3162 | |||
| 1a20edc0b0 | |||
| f80ba1473e | |||
| 89a8203949 | |||
| fd6321e71b | |||
| 7dffd9d1ff | |||
| 51a156420d | |||
| c92323c681 | |||
| 10e03eef3b | |||
| e151055521 | |||
| 1f8408b622 | |||
| 98ddadb131 | |||
| a5ac12545a | |||
| e58384d815 | |||
| ab381dacfe | |||
| 9162fde14a | |||
| 6cf1714a07 | |||
| c402e17605 | |||
| 25746e9844 | |||
| a8a8dbeab8 | |||
| 6113df2b3e | |||
| 8f7363df41 | |||
| dafc79bf80 | |||
| 9ae537cafc | |||
| 162df6b250 | |||
| 12f28dfc95 | |||
| 5d7d8abbde | |||
| b960faec45 | |||
| 9a057b757d | |||
| 296c4b750b | |||
| 7ecc47e127 | |||
| d03180ff4d | |||
| c1c52b072e | |||
| 72be3803ec | |||
| 5cb4a8557d | |||
| 7787135549 | |||
| 56368500a3 | |||
| 846c6a579e | |||
| 573179060e | |||
| fda345577a | |||
| 8387710473 | |||
| 3af3fd1428 | |||
| c7c6e2866e | |||
| 7172ae0232 | |||
| 4eac97a7d8 | |||
| 352e4c088a | |||
| 4d5814fc43 | |||
| 6d13b91d37 | |||
| 0376c99b5a | |||
| 77d9e472b7 | |||
| 2911ebb5d7 | |||
| f4d01f56c6 | |||
| 878305fee3 | |||
| 0460a53f53 | |||
| e622f6aff2 | |||
| 9e96a30ea2 | |||
| d188fbdf5a | |||
| 06c16bf560 | |||
| 29ea9e3237 | |||
| b01add2925 | |||
| a98ad9577b | |||
| ef8351151d | |||
| f6bb3c848e | |||
| 3fd4cbbbfd | |||
| b102e0dbc5 | |||
| 1c0e8f77c8 | |||
| bebcb70b14 | |||
| 165602ae0a | |||
| a36e00efeb | |||
| bd4d273e1c | |||
| 216a928861 | |||
| a157c3f940 | |||
| 5a23459b23 | |||
| 538b282f43 | |||
| 86f51f1e4a | |||
| 622f76d5fb | |||
| 65e06b12ae | |||
| b714f7db69 | |||
| 1063922682 | |||
| 2c6168129a | |||
| 45465c1bd1 | |||
| 8acecc88f9 | |||
| 575349929b | |||
| 3913a89434 | |||
| 1bc8c9e3e2 | |||
| b4a0cca9c4 | |||
| 78b13ef4e1 | |||
| a49df101cd | |||
| 7ff4a00063 | |||
| 2636f8acf1 | |||
| 9042a885fa | |||
| ef837bd9c6 | |||
| 6a76725d64 | |||
| 697dbdc604 | |||
| 86f98f3710 | |||
| 470c696976 | |||
| e61bd174c8 | |||
| c71b9bc122 | |||
| c5339cbbe7 | |||
| 29020b0ac0 | |||
| 0a0530af0a | |||
| a5783ab860 | |||
| ae2de91b4c | |||
| ceecbef586 | |||
| 2fa558b25f | |||
| f465b478d6 | |||
| a4abac4916 | |||
| 9f9cb097d9 | |||
| 5ab64e31fd | |||
| ac6a8b9b74 | |||
| f43d252d08 | |||
| 185f9efb9c | |||
| 47d423036f | |||
| 7fae21f255 | |||
| 3fce835da0 | |||
| 0b62ccf941 | |||
| f7c5d7fc7f | |||
| e484685282 | |||
| 9c857705ff | |||
| 76f680de33 | |||
| 1181b6ae3a | |||
| 8a7f4ab156 | |||
| 99d43a7ddc | |||
| bd97d7d94e | |||
| 73235f1967 | |||
| 1ebc8092a6 | |||
| edd93c39a3 | |||
| 88c43d1772 | |||
| a0a8eb4700 | |||
| 28e5c33e81 | |||
| f24847576d | |||
| dfaf27e9e5 | |||
| 32d5b28423 | |||
| 0b8ca3313e | |||
| e6bb5e82a3 | |||
| a24b97b004 | |||
| 08eeff7f1c | |||
| 769f31e69a | |||
| cdf92b64d6 | |||
| 6d1ca028e7 | |||
| a228356399 | |||
| 3521f1b641 | |||
| 1dedf3d83b | |||
| da4c01c011 | |||
| 9cd445ad40 | |||
| ff4ef7e240 | |||
| 4de574ee86 | |||
| 0481b4025b | |||
| 9495b858fd | |||
| 5576adbd0b | |||
| b8da86bce8 | |||
| a926d3dadd | |||
| a95624ab19 | |||
| cf745422f3 | |||
| 10e042454c | |||
| 759be06ebc | |||
| aea37272ea | |||
| 2f17d37f7b | |||
| df8623c4e2 | |||
| 5c9766fb1b | |||
| a3d2e03425 | |||
| 717dae8f5d | |||
| 373dbde42c | |||
| 125af33941 | |||
| b43e36a79d | |||
| 145fccdf6e | |||
| f9c478f250 | |||
| 5da822fdc4 | |||
| 5abf1b5742 | |||
| 9ac3841342 | |||
| 1f315c5b8d | |||
| b9440587d0 | |||
| 835a971f89 | |||
| 0bba2092af | |||
| c3240218f8 | |||
| 9e392985b8 | |||
| a7d30dd1d5 | |||
| fa96d881e1 | |||
| 9ca9d6c4bd | |||
| 7ad66db491 | |||
| 1dbda3872b | |||
| 3619a46f52 | |||
| 6a93d166c5 | |||
| c84e62febe | |||
| 8ff21d6936 | |||
| 3b0012ec30 | |||
| 62a3f1693d | |||
| ed6796dde0 | |||
| 46721d7552 | |||
| a9a5a78e02 | |||
| 7c4431ceeb | |||
| 8cfdc8f27f | |||
| 691baf5c16 | |||
| 8669057681 | |||
| b439aedce5 | |||
| 2504e405a7 | |||
| 6426409bf9 | |||
| 9cf4ea5c91 | |||
| 86343abcf8 | |||
| d0fe2d2f75 | |||
| 49ef4fd266 | |||
| 929a57ceb8 | |||
| c6f94ed95a | |||
| 949ac459fc | |||
| 32fc6868aa | |||
| 82394cd70e | |||
| 4670a5d2e2 | |||
| 273d992493 | |||
| 1c71340719 | |||
| ed61c2ee22 | |||
| 363dede57c | |||
| b29c6add47 | |||
| 8d01e565c5 | |||
| 47e0fb95d5 | |||
| 23d346b291 | |||
| 105cdf6fe6 | |||
| 5135264d56 | |||
| c3ff1a3db3 | |||
| d62c1dedf3 | |||
| bd105a188f | |||
| 09affd3981 | |||
| 8e7e18e22d | |||
| e03fd80fd4 | |||
| adc3201dcf | |||
| 3a681e5b1a | |||
| c90ed0d424 | |||
| 418cbb420b | |||
| c948f0c287 | |||
| 285b45d19d | |||
| 783f1c8457 | |||
| daa7f12994 | |||
| e63e989d41 | |||
| 31921905a9 | |||
| 5572ab858e | |||
| 961ca0879d | |||
| 5d464494b6 | |||
| 4ca2513d00 | |||
| 15d69ed950 | |||
| 39bd2616c4 | |||
| 6302789c41 | |||
| 0ff31012c8 | |||
| 5c7b25c960 | |||
| 10102f2db9 | |||
| 6f5b5e4dc2 | |||
| 007c8a7b64 | |||
| 009ce9063e | |||
| 58a405bfa8 | |||
| 42214f17fc | |||
| b5f117e932 | |||
| b0f158047c | |||
| d5f7ca634d | |||
| aa6110c619 | |||
| a9660fe9fa | |||
| 0801245871 | |||
| 278fd3515f | |||
| 56588f3f7f | |||
| a5d01cb547 | |||
| fabf5d1ad5 | |||
| ebae3a4929 | |||
| 8e6640ed79 | |||
| e4c42bde89 | |||
| f7515edde9 | |||
| 2b0da75412 | |||
| 04c422c3af | |||
| 339ca74ff4 | |||
| 741a13379a | |||
| da907f35bd | |||
| 133531b6a0 | |||
| 5fa0f8dd7e | |||
| 7247413a48 | |||
| 591158adc5 | |||
| 5d83383105 | |||
| 5ad809f49e | |||
| 56a62bb4c2 | |||
| afb4a233be | |||
| c0aebe023c | |||
| 055c6a668e | |||
| 735c5fc7ed | |||
| c066fff971 | |||
| 89373ebcd6 | |||
| 51cf232691 | |||
| 3bfff2347f | |||
| 7d83604540 | |||
| f25571a0a9 | |||
| 05c7ba5f29 | |||
| dcf7772589 | |||
| 3806f9db07 | |||
| 0f14002b05 | |||
| 1bf53d8b89 | |||
| 8eb10a58ad | |||
| 9985378062 | |||
| 38ad5d457b | |||
| ebed433dde | |||
| 2b02f7e210 | |||
| 1434800982 | |||
| 327e33b827 | |||
| d2a053c1d7 | |||
| abe14e049a | |||
| 4b9db0c794 | |||
| b118cd8f27 | |||
| 83669964f5 | |||
| 9248fd6b28 | |||
| bb935967ac | |||
| b7e0f1f983 | |||
| 6ee6c007c5 | |||
| 9a507b54d7 | |||
| c88f516759 | |||
| 912b743a1e | |||
| d546424f18 | |||
| fe179fff42 | |||
| adf2ddb510 | |||
| f30c9020fa | |||
| 1b3073f3f9 | |||
| 615161ac9d | |||
| 02e06e7c6e | |||
| d15268eb22 | |||
| e48415d932 | |||
| e5fa341f39 | |||
| 234d734981 | |||
| d771ed9209 | |||
| 37d72cb8b3 | |||
| 1154f9601b | |||
| 322513f99b | |||
| 6e7ac60d4d | |||
| 405840607f | |||
| 8417e7ef65 | |||
| ac2fe44d8e | |||
| 3030d7f032 | |||
| dcf7138694 | |||
| 97df154b78 | |||
| d74034e08e | |||
| 674590e247 | |||
| d41cc7c3b6 | |||
| ab48800401 | |||
| ea456c5faa | |||
| 83d1442781 | |||
| 5888b0e06a | |||
| 3120d0bd7a | |||
| a7dcc2c9c6 | |||
| 17898cc747 | |||
| 7bebf062be | |||
| 048c3d5025 | |||
| 0adcdc6b4a | |||
| 19d7ad734b | |||
| 6c657dff37 | |||
| 78ccbeac3b | |||
| 2a0eae8975 | |||
| 28d25e7812 | |||
| fe795d49f3 | |||
| 9fad898ee3 | |||
| 9a42142ce7 | |||
| 9db8ed2dfc | |||
| 619a539f2e | |||
| ae8eb4a1f1 | |||
| 9a469e64da | |||
| f7d1613d62 | |||
| 80655bce50 | |||
| b3b0ba775c | |||
| 4713739d1a | |||
| 53b5de330e | |||
| fd4843c3a1 | |||
| 97d2b9e1f9 | |||
| 5896dadaad | |||
| 0ab60cb770 | |||
| 5b71e92776 | |||
| 211678c7d3 | |||
| d25793ed03 | |||
| 98cae011a5 | |||
| 59ae6f9c10 | |||
| 474d1669d9 | |||
| 563a1408ea | |||
| 168c08a6f6 | |||
| 82783072ef | |||
| 11972ef96c | |||
| 468ba21283 | |||
| 78b0cae892 | |||
| 866482a8e8 | |||
| c342310389 | |||
| 0eb028f0b5 | |||
| 4ffe4f2262 | |||
| 677549ec15 | |||
| 68d8e25bc4 | |||
| 4f007fdd44 | |||
| 54392941f3 | |||
| ebdad47f2d | |||
| cb4e36f591 | |||
| cd30efaaa1 | |||
| 7c234e0437 | |||
| d973c5d5c7 | |||
| a7f15a4e00 | |||
| 6b5484bf55 | |||
| ab38b709b2 | |||
| 4118038198 | |||
| 142154c0df | |||
| 07ae90d64d | |||
| 8a69fd09fc | |||
| 5c592afebe | |||
| 8402a2b710 | |||
| e695983d4d | |||
| a5ab2ba332 | |||
| c6e5a52104 | |||
| 3e2d5e0bdb | |||
| 1f19ed71aa | |||
| 5baa82da8f | |||
| 48f610a216 | |||
| e4e58791a6 |
@ -1,4 +1,7 @@
|
||||
<!-- 为更高效率地交流并解决问题,请按照以下模板提交issue,感谢! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 重要的事情说三遍! -->
|
||||
|
||||
### 1. 您当前使用的`Go`版本,及系统版本、系统架构?
|
||||
|
||||
|
||||
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://github.com/gogf/gf#donators
|
||||
7
.github/ISSUE_TEMPLATE.MD
vendored
7
.github/ISSUE_TEMPLATE.MD
vendored
@ -1,5 +1,10 @@
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 重要的事情说三遍! -->
|
||||
|
||||
### 1. What version of `Go` and system type/arch are you using?
|
||||
|
||||
<!--
|
||||
@ -13,7 +18,7 @@ What expect to see is like: `go 1.12, linux/amd64`
|
||||
<!-- You can find the GF version from your `go.mod`, or from the `version.go` in `GF` -->
|
||||
|
||||
|
||||
### 3. Can this issue be reproduced with the latest release?
|
||||
### 3. Can this issue be re-produced with the latest release?
|
||||
|
||||
|
||||
|
||||
|
||||
63
.github/workflows/cli.yml
vendored
Normal file
63
.github/workflows/cli.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: GoFrame CLI Build Release
|
||||
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build And Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Github Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Golang Environment
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: Build CLI Binary
|
||||
run: |
|
||||
echo "Building linux amd64 binary..."
|
||||
cd cmd/gf
|
||||
GOOS=linux GOARCH=amd64 go build main.go
|
||||
chmod +x main
|
||||
./main install -y
|
||||
|
||||
- name: Build CLI Binary For All Platform
|
||||
run: |
|
||||
cd cmd/gf
|
||||
gf build main.go -n gf -a all -s all
|
||||
|
||||
- name: Move Files Before Release
|
||||
run: |
|
||||
cd cmd/gf/temp
|
||||
for OS in *;do for FILE in $OS/*;\
|
||||
do mv $FILE gf_$OS && rm -rf $OS;\
|
||||
done;done
|
||||
|
||||
- name: Create Github Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: GoFrame CLI Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: alexellis/upload-assets@0.2.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["cmd/gf/temp/gf_*"]'
|
||||
149
.github/workflows/gf.yml
vendored
Normal file
149
.github/workflows/gf.yml
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
name: GoFrame CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
|
||||
env:
|
||||
GF_DEBUG: 0
|
||||
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
env:
|
||||
POSTGRES_PASSWORD: 12345678
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_DB: test
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 5432:5432
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
mssql:
|
||||
image: mcmoe/mssqldocker:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: LoremIpsum86
|
||||
MSSQL_DB: test
|
||||
MSSQL_USER: root
|
||||
MSSQL_PASSWORD: LoremIpsum86
|
||||
ports:
|
||||
- 1433:1433
|
||||
options: >-
|
||||
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
|
||||
--health-start-period 10s
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.15", "1.16", "1.17"]
|
||||
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repositary
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi
|
||||
echo "gofmt check pass."
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: CLI Build & Test
|
||||
run: |
|
||||
cd cmd/gf
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Example Build & Test
|
||||
run: |
|
||||
cd example
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Contrib Build & Test
|
||||
run: |
|
||||
cd contrib
|
||||
for file in `find . -name go.mod`; do
|
||||
path=$(dirname $file)
|
||||
# Ignore oracle database driver build&test.
|
||||
if [ "oracle" = $(basename $path) ]; then
|
||||
continue 1
|
||||
fi
|
||||
cd $path
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
cd -
|
||||
done
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: |
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: |
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: go-${{ matrix.go }}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -6,8 +6,7 @@
|
||||
.idea/
|
||||
.settings/
|
||||
.vscode/
|
||||
vender/
|
||||
log/
|
||||
vendor/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
@ -15,5 +14,6 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
|
||||
.test/
|
||||
main
|
||||
gf
|
||||
949
.golangci.yml
Normal file
949
.golangci.yml
Normal file
@ -0,0 +1,949 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 5m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: false
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
# - mytag
|
||||
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-dirs:
|
||||
- .example
|
||||
- .test
|
||||
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-files:
|
||||
- ".*_test\\.go$"
|
||||
- ".*_packed\\.go$"
|
||||
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
#modules-download-mode: release|readonly|vendor
|
||||
|
||||
# Allow multiple parallel golangci-lint instances running.
|
||||
# If false (default) - golangci-lint acquires file lock on start.
|
||||
allow-parallel-runners: true
|
||||
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions
|
||||
# default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
|
||||
# make issues output unique by line, default is true
|
||||
uniq-by-line: true
|
||||
|
||||
# add a prefix to the output file references; default is no prefix
|
||||
path-prefix: ""
|
||||
|
||||
# sorts results by: filepath, line and column
|
||||
sort-results: true
|
||||
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
bidichk:
|
||||
# The following configurations check for all mentioned invisible unicode
|
||||
# runes. It can be omitted because all runes are enabled by default.
|
||||
left-to-right-embedding: true
|
||||
right-to-left-embedding: true
|
||||
pop-directional-formatting: true
|
||||
left-to-right-override: true
|
||||
right-to-left-override: true
|
||||
left-to-right-isolate: true
|
||||
right-to-left-isolate: true
|
||||
first-strong-isolate: true
|
||||
pop-directional-isolate: true
|
||||
|
||||
cyclop:
|
||||
# the maximal code complexity to report
|
||||
max-complexity: 50
|
||||
# the maximal average package complexity. If it's higher than 0.0 (float) the check is enabled (default 0.0)
|
||||
package-average: 0.0
|
||||
# should ignore tests (default false)
|
||||
skip-tests: false
|
||||
|
||||
dogsled:
|
||||
# checks assignments with too many blank identifiers; default is 2
|
||||
max-blank-identifiers: 2
|
||||
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
|
||||
errcheck:
|
||||
# report about not checking of errors in type assertions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
#ignore: fmt:.*,io/ioutil:^Read.*
|
||||
|
||||
# [deprecated] use exclude-functions instead.
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
# exclude: /path/to/file.txt
|
||||
|
||||
# list of functions to exclude from checking, where each entry is a single function to exclude.
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
exclude-functions:
|
||||
# - io/ioutil.ReadFile
|
||||
# - io.Copy(*bytes.Buffer)
|
||||
# - io.Copy(os.Stdout)
|
||||
|
||||
errorlint:
|
||||
# Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats
|
||||
errorf: true
|
||||
# Check for plain type assertions and type switches
|
||||
asserts: true
|
||||
# Check for plain error comparisons
|
||||
comparison: true
|
||||
|
||||
exhaustive:
|
||||
# check switch statements in generated files also
|
||||
check-generated: false
|
||||
# presence of "default" case in switch statements satisfies exhaustiveness,
|
||||
# even if all enum members are not listed
|
||||
default-signifies-exhaustive: false
|
||||
# enum members matching the supplied regex do not have to be listed in
|
||||
# switch statements to satisfy exhaustiveness
|
||||
ignore-enum-members: ""
|
||||
# consider enums only in package scopes, not in inner scopes
|
||||
package-scope-only: false
|
||||
|
||||
exhaustivestruct:
|
||||
# Struct Patterns is list of expressions to match struct packages and names
|
||||
# The struct packages have the form example.com/package.ExampleStruct
|
||||
# The matching patterns can use matching syntax from https://pkg.go.dev/path#Match
|
||||
# If this list is empty, all structs are tested.
|
||||
struct-patterns:
|
||||
# - '*.Test'
|
||||
# - 'example.com/package.ExampleStruct'
|
||||
# - '*.Test2'
|
||||
# - '*.Embedded'
|
||||
# - '*.External'
|
||||
|
||||
forbidigo:
|
||||
# Forbid the following identifiers (identifiers are written using regexp):
|
||||
forbid:
|
||||
# - ^print.*$
|
||||
# - 'fmt\.Print.*'
|
||||
# - fmt.Println.* # too much log noise
|
||||
# - ginkgo\\.F.* # these are used just for local development
|
||||
# Exclude godoc examples from forbidigo checks. Default is true.
|
||||
exclude_godoc_examples: false
|
||||
|
||||
funlen:
|
||||
lines: 150
|
||||
statements: 50
|
||||
|
||||
gci:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# only support one prefix
|
||||
# if not set, use goimports.local-prefixes
|
||||
local-prefixes: github.com/gogf/gf
|
||||
|
||||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 50
|
||||
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimum occurrences of constant string count to trigger issue, 3 by default
|
||||
min-occurrences: 3
|
||||
# ignore test files, false by default
|
||||
ignore-tests: true
|
||||
# look for existing constants matching the values, true by default
|
||||
match-constant: true
|
||||
# search also for duplicated numbers, false by default
|
||||
numbers: false
|
||||
# minimum value, only works with goconst.numbers, 3 by default
|
||||
min: 3
|
||||
# maximum value, only works with goconst.numbers, 3 by default
|
||||
max: 3
|
||||
# ignore when constant is not used as function argument, true by default
|
||||
ignore-calls: true
|
||||
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
enabled-checks:
|
||||
- nestingReduce
|
||||
- unnamedresult
|
||||
- ruleguard
|
||||
- truncateCmp
|
||||
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
- regexpMust
|
||||
- ifElseChain
|
||||
- exitAfterDefer
|
||||
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
disabled-tags:
|
||||
- experimental
|
||||
|
||||
# Settings passed to gocritic.
|
||||
# The settings key is the name of a supported gocritic checker.
|
||||
# The list of supported checkers can be find in https://go-critic.github.io/overview.
|
||||
settings:
|
||||
captLocal: # must be valid enabled check name
|
||||
# whether to restrict checker to params only (default true)
|
||||
paramsOnly: true
|
||||
elseif:
|
||||
# whether to skip balanced if-else pairs (default true)
|
||||
skipBalanced: true
|
||||
hugeParam:
|
||||
# size in bytes that makes the warning trigger (default 80)
|
||||
sizeThreshold: 80
|
||||
nestingReduce:
|
||||
# min number of statements inside a branch to trigger a warning (default 5)
|
||||
bodyWidth: 5
|
||||
rangeExprCopy:
|
||||
# size in bytes that makes the warning trigger (default 512)
|
||||
sizeThreshold: 512
|
||||
# whether to check test functions (default true)
|
||||
skipTestFuncs: true
|
||||
rangeValCopy:
|
||||
# size in bytes that makes the warning trigger (default 128)
|
||||
sizeThreshold: 32
|
||||
# whether to check test functions (default true)
|
||||
skipTestFuncs: true
|
||||
ruleguard:
|
||||
# Enable debug to identify which 'Where' condition was rejected.
|
||||
# The value of the parameter is the name of a function in a ruleguard file.
|
||||
#
|
||||
# When a rule is evaluated:
|
||||
# If:
|
||||
# The Match() clause is accepted; and
|
||||
# One of the conditions in the Where() clause is rejected,
|
||||
# Then:
|
||||
# ruleguard prints the specific Where() condition that was rejected.
|
||||
#
|
||||
# The flag is passed to the ruleguard 'debug-group' argument.
|
||||
debug: 'emptyDecl'
|
||||
# Deprecated, use 'failOn' param.
|
||||
# If set to true, identical to failOn='all', otherwise failOn=''
|
||||
failOnError: false
|
||||
# Determines the behavior when an error occurs while parsing ruleguard files.
|
||||
# If flag is not set, log error and skip rule files that contain an error.
|
||||
# If flag is set, the value must be a comma-separated list of error conditions.
|
||||
# - 'all': fail on all errors.
|
||||
# - 'import': ruleguard rule imports a package that cannot be found.
|
||||
# - 'dsl': gorule file does not comply with the ruleguard DSL.
|
||||
failOn: dsl
|
||||
# Comma-separated list of file paths containing ruleguard rules.
|
||||
# If a path is relative, it is relative to the directory where the golangci-lint command is executed.
|
||||
# The special '${configDir}' variable is substituted with the absolute directory containing the golangci config file.
|
||||
# Glob patterns such as 'rules-*.go' may be specified.
|
||||
rules: '' #${configDir}/ruleguard/rules-*.go,${configDir}/myrule1.go'
|
||||
#tooManyResultsChecker:
|
||||
# maximum number of results (default 5)
|
||||
#maxResults: 10
|
||||
truncateCmp:
|
||||
# whether to skip int/uint/uintptr types (default true)
|
||||
skipArchDependent: true
|
||||
underef:
|
||||
# whether to skip (*x).method() calls where x is a pointer receiver (default true)
|
||||
skipRecvDeref: true
|
||||
unnamedResult:
|
||||
# whether to check exported functions
|
||||
checkExported: true
|
||||
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 30
|
||||
|
||||
godot:
|
||||
# comments to be checked: `declarations`, `toplevel`, or `all`
|
||||
scope: declarations
|
||||
# list of regexps for excluding particular comment lines from check
|
||||
exclude:
|
||||
# example: exclude comments which contain numbers
|
||||
# - '[0-9]+'
|
||||
# check that each sentence starts with a capital letter
|
||||
capital: false
|
||||
|
||||
godox:
|
||||
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
|
||||
# might be left in the code accidentally and should be resolved before merging
|
||||
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
|
||||
#- NOTE
|
||||
- BUG
|
||||
- FIXME
|
||||
- OPTIMIZE # marks code that should be optimized before merging
|
||||
- HACK # marks hack-arounds that should be removed before merging
|
||||
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
|
||||
gofumpt:
|
||||
# Select the Go version to target. The default is `1.15`.
|
||||
lang-version: "1.16"
|
||||
|
||||
# Choose whether or not to use the extra rules that are disabled
|
||||
# by default
|
||||
extra-rules: false
|
||||
|
||||
goheader:
|
||||
values:
|
||||
const:
|
||||
# define here const type values in format k:v, for example:
|
||||
# COMPANY: MY COMPANY
|
||||
regexp:
|
||||
# define here regexp type values, for example
|
||||
# AUTHOR: .*@mycompany\.com
|
||||
template: # |-
|
||||
# put here copyright header template for source code files, for example:
|
||||
# Note: {{ YEAR }} is a builtin value that returns the year relative to the current machine time.
|
||||
#
|
||||
# {{ AUTHOR }} {{ COMPANY }} {{ YEAR }}
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at:
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
template-path:
|
||||
# also as alternative of directive 'template' you may put the path to file with the template source
|
||||
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/gogf/gf
|
||||
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.9
|
||||
|
||||
gomnd:
|
||||
settings:
|
||||
mnd:
|
||||
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
|
||||
checks: argument,case,condition,operation,return,assign
|
||||
# ignored-numbers: 1000
|
||||
# ignored-files: magic_.*.go
|
||||
# ignored-functions: math.*
|
||||
|
||||
gomoddirectives:
|
||||
# Allow local `replace` directives. Default is false.
|
||||
replace-local: false
|
||||
# List of allowed `replace` directives. Default is empty.
|
||||
replace-allow-list:
|
||||
- launchpad.net/gocheck
|
||||
- github.com/coreos/etcd
|
||||
- google.golang.org/grpc
|
||||
- gitlab.jntmedia.cn/lanren/core
|
||||
# Allow to not explain why the version has been retracted in the `retract` directives. Default is false.
|
||||
retract-allow-no-explanation: false
|
||||
# Forbid the use of the `exclude` directives. Default is false.
|
||||
exclude-forbidden: false
|
||||
|
||||
gomodguard:
|
||||
allowed:
|
||||
modules: # List of allowed modules
|
||||
# - gopkg.in/yaml.v2
|
||||
- gorm.io/gorm
|
||||
- gorm.io/driver/mysql
|
||||
- k8s.io/klog
|
||||
domains: # List of allowed module domains
|
||||
# - golang.org
|
||||
- google.golang.org
|
||||
- gopkg.in
|
||||
- golang.org
|
||||
- github.com
|
||||
- go.uber.org
|
||||
blocked:
|
||||
modules: # List of blocked modules
|
||||
# - github.com/uudashr/go-module: # Blocked module
|
||||
# recommendations: # Recommended modules that should be used instead (Optional)
|
||||
# - golang.org/x/mod
|
||||
# reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional)
|
||||
versions: # List of blocked module version constraints
|
||||
# - github.com/mitchellh/go-homedir: # Blocked module with version constraint
|
||||
# version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons
|
||||
# reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional)
|
||||
local_replace_directives: false # Set to true to raise lint issues for packages that are loaded from a local path via replace directive
|
||||
|
||||
gosec:
|
||||
# To select a subset of rules to run.
|
||||
# Available rules: https://github.com/securego/gosec#available-rules
|
||||
includes:
|
||||
- G401
|
||||
- G306
|
||||
- G101
|
||||
# To specify a set of rules to explicitly exclude.
|
||||
# Available rules: https://github.com/securego/gosec#available-rules
|
||||
excludes:
|
||||
- G204
|
||||
# Exclude generated files
|
||||
exclude-generated: true
|
||||
# Filter out the issues with a lower severity than the given value. Valid options are: low, medium, high.
|
||||
severity: "low"
|
||||
# Filter out the issues with a lower confidence than the given value. Valid options are: low, medium, high.
|
||||
confidence: "low"
|
||||
# To specify the configuration of rules.
|
||||
# The configuration of rules is not fully documented by gosec:
|
||||
# https://github.com/securego/gosec#configuration
|
||||
# https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/rules/rulelist.go#L60-L102
|
||||
config:
|
||||
G306: "0600"
|
||||
G101:
|
||||
pattern: "(?i)example"
|
||||
ignore_entropy: false
|
||||
entropy_threshold: "80.0"
|
||||
per_char_threshold: "3.0"
|
||||
truncate: "32"
|
||||
|
||||
gosimple:
|
||||
# Select the Go version to target. The default is '1.13'.
|
||||
go: "1.16"
|
||||
# https://staticcheck.io/docs/options#checks
|
||||
checks: [ "all" ]
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||
|
||||
# enable or disable analyzers by name
|
||||
# run `go tool vet help` to see all analyzers
|
||||
enable:
|
||||
- atomicalign
|
||||
enable-all: false
|
||||
disable:
|
||||
- shadow
|
||||
disable-all: false
|
||||
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/sirupsen/logrus
|
||||
packages-with-error-message:
|
||||
# specify an error message to output when a blacklisted package is used
|
||||
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
|
||||
|
||||
ifshort:
|
||||
# Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax.
|
||||
# Has higher priority than max-decl-chars.
|
||||
max-decl-lines: 1
|
||||
# Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax.
|
||||
max-decl-chars: 30
|
||||
|
||||
importas:
|
||||
# if set to `true`, force to use alias.
|
||||
no-unaliased: true
|
||||
# List of aliases
|
||||
alias:
|
||||
# using `servingv1` alias for `knative.dev/serving/pkg/apis/serving/v1` package
|
||||
- pkg: knative.dev/serving/pkg/apis/serving/v1
|
||||
alias: servingv1
|
||||
# using `autoscalingv1alpha1` alias for `knative.dev/serving/pkg/apis/autoscaling/v1alpha1` package
|
||||
- pkg: knative.dev/serving/pkg/apis/autoscaling/v1alpha1
|
||||
alias: autoscalingv1alpha1
|
||||
# You can specify the package path by regular expression,
|
||||
# and alias by regular expression expansion syntax like below.
|
||||
# see https://github.com/julz/importas#use-regular-expression for details
|
||||
- pkg: knative.dev/serving/pkg/apis/(\w+)/(v[\w\d]+)
|
||||
alias: $1$2
|
||||
|
||||
ireturn:
|
||||
# ireturn allows using `allow` and `reject` settings at the same time.
|
||||
# Both settings are lists of the keywords and regular expressions matched to interface or package names.
|
||||
# keywords:
|
||||
# - `empty` for `interface{}`
|
||||
# - `error` for errors
|
||||
# - `stdlib` for standard library
|
||||
# - `anon` for anonymous interfaces
|
||||
|
||||
# By default, it allows using errors, empty interfaces, anonymous interfaces,
|
||||
# and interfaces provided by the standard library.
|
||||
allow:
|
||||
- anon
|
||||
- error
|
||||
- empty
|
||||
- stdlib
|
||||
# You can specify idiomatic endings for interface
|
||||
- (or|er)$
|
||||
|
||||
# Reject patterns
|
||||
# reject:
|
||||
# - github.com\/user\/package\/v4\.Type
|
||||
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 240
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 4
|
||||
|
||||
makezero:
|
||||
# Allow only slices initialized with a length of zero. Default is false.
|
||||
always: false
|
||||
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
ignore-words:
|
||||
- someword
|
||||
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
|
||||
nestif:
|
||||
# minimal complexity of if statements to report, 5 by default
|
||||
min-complexity: 4
|
||||
|
||||
nilnil:
|
||||
# By default, nilnil checks all returned types below.
|
||||
checked-types:
|
||||
- ptr
|
||||
- func
|
||||
- iface
|
||||
- map
|
||||
- chan
|
||||
|
||||
nlreturn:
|
||||
# size of the block (including return statement that is still "OK")
|
||||
# so no return split required.
|
||||
block-size: 1
|
||||
|
||||
nolintlint:
|
||||
# Disable to ensure that all nolint directives actually have an effect. Default is true.
|
||||
allow-unused: true
|
||||
# Disable to ensure that nolint directives don't have a leading space. Default is true.
|
||||
allow-leading-space: true
|
||||
# Exclude following linters from requiring an explanation. Default is [].
|
||||
allow-no-explanation: [ ]
|
||||
# Enable to require an explanation of nonzero length after each nolint directive. Default is false.
|
||||
require-explanation: false
|
||||
# Enable to require nolint directives to mention the specific linter being suppressed. Default is false.
|
||||
require-specific: true
|
||||
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
simple: true
|
||||
range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
|
||||
promlinter:
|
||||
# Promlinter cannot infer all metrics name in static analysis.
|
||||
# Enable strict mode will also include the errors caused by failing to parse the args.
|
||||
strict: false
|
||||
# Please refer to https://github.com/yeya24/promlinter#usage for detailed usage.
|
||||
disabled-linters:
|
||||
# - "Help"
|
||||
# - "MetricUnits"
|
||||
# - "Counter"
|
||||
# - "HistogramSummaryReserved"
|
||||
# - "MetricTypeInName"
|
||||
# - "ReservedChars"
|
||||
# - "CamelCase"
|
||||
# - "lintUnitAbbreviations"
|
||||
|
||||
predeclared:
|
||||
# comma-separated list of predeclared identifiers to not report on
|
||||
ignore: ""
|
||||
# include method names and field names (i.e., qualified names) in checks
|
||||
q: false
|
||||
|
||||
rowserrcheck:
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
|
||||
revive:
|
||||
# see https://github.com/mgechev/revive#available-rules for details.
|
||||
ignore-generated-header: true
|
||||
severity: warning
|
||||
rules:
|
||||
- name: indent-error-flow
|
||||
severity: warning
|
||||
- name: add-constant
|
||||
severity: warning
|
||||
arguments:
|
||||
- maxLitCount: "3"
|
||||
allowStrs: '""'
|
||||
allowInts: "0,1,2"
|
||||
allowFloats: "0.0,0.,1.0,1.,2.0,2."
|
||||
|
||||
staticcheck:
|
||||
# Select the Go version to target. The default is '1.13'.
|
||||
go: "1.16"
|
||||
# https://staticcheck.io/docs/options#checks
|
||||
checks: [ "all" ]
|
||||
|
||||
stylecheck:
|
||||
# Select the Go version to target. The default is '1.13'.
|
||||
go: "1.16"
|
||||
# https://staticcheck.io/docs/options#checks
|
||||
checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]
|
||||
# https://staticcheck.io/docs/options#dot_import_whitelist
|
||||
dot-import-whitelist:
|
||||
- fmt
|
||||
# https://staticcheck.io/docs/options#initialisms
|
||||
initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ]
|
||||
# https://staticcheck.io/docs/options#http_status_code_whitelist
|
||||
http-status-code-whitelist: [ "200", "400", "404", "500" ]
|
||||
|
||||
tagliatelle:
|
||||
# check the struck tag name case
|
||||
case:
|
||||
# use the struct field name to check the name of the struct tag
|
||||
use-field-name: true
|
||||
rules:
|
||||
# any struct tag type can be used.
|
||||
# support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`
|
||||
json: camel
|
||||
yaml: camel
|
||||
xml: camel
|
||||
bson: camel
|
||||
avro: snake
|
||||
mapstructure: kebab
|
||||
|
||||
testpackage:
|
||||
# regexp pattern to skip files
|
||||
skip-regexp: (export|internal)_test\.go
|
||||
|
||||
thelper:
|
||||
# The following configurations enable all checks. It can be omitted because all checks are enabled by default.
|
||||
# You can enable only required checks deleting unnecessary checks.
|
||||
test:
|
||||
first: true
|
||||
name: true
|
||||
begin: true
|
||||
benchmark:
|
||||
first: true
|
||||
name: true
|
||||
begin: true
|
||||
tb:
|
||||
first: true
|
||||
name: true
|
||||
begin: true
|
||||
|
||||
tenv:
|
||||
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
|
||||
# By default, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
|
||||
all: false
|
||||
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
|
||||
unused:
|
||||
# Select the Go version to target. The default is '1.13'.
|
||||
go: "1.16"
|
||||
|
||||
varnamelen:
|
||||
# The longest distance, in source lines, that is being considered a "small scope." (defaults to 5)
|
||||
# Variables used in at most this many lines will be ignored.
|
||||
max-distance: 5
|
||||
# The minimum length of a variable's name that is considered "long." (defaults to 3)
|
||||
# Variable names that are at least this long will be ignored.
|
||||
min-name-length: 1
|
||||
# Check method receiver names. (defaults to false)
|
||||
check-receiver: false
|
||||
# Check named return values. (defaults to false)
|
||||
check-return: false
|
||||
# Ignore "ok" variables that hold the bool return value of a type assertion. (defaults to false)
|
||||
ignore-type-assert-ok: false
|
||||
# Ignore "ok" variables that hold the bool return value of a map index. (defaults to false)
|
||||
ignore-map-index-ok: false
|
||||
# Ignore "ok" variables that hold the bool return value of a channel receive. (defaults to false)
|
||||
ignore-chan-recv-ok: false
|
||||
# Optional list of variable names that should be ignored completely. (defaults to empty list)
|
||||
ignore-names:
|
||||
- err
|
||||
# Optional list of variable declarations that should be ignored completely. (defaults to empty list)
|
||||
# Entries must be in the form of "<variable name> <type>" or "<variable name> *<type>".
|
||||
ignore-decls:
|
||||
- c echo.Context
|
||||
- t testing.T
|
||||
- f *foo.Bar
|
||||
- e error
|
||||
- i int
|
||||
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
|
||||
wrapcheck:
|
||||
# An array of strings that specify substrings of signatures to ignore.
|
||||
# If this set, it will override the default set of ignored signatures.
|
||||
# See https://github.com/tomarrell/wrapcheck#configuration for more information.
|
||||
ignoreSigs:
|
||||
- .Errorf(
|
||||
- errors.New(
|
||||
- errors.Unwrap(
|
||||
- .Wrap(
|
||||
- .Wrapf(
|
||||
- .WithMessage(
|
||||
- .WithMessagef(
|
||||
- .WithStack(
|
||||
ignorePackageGlobs:
|
||||
- encoding/*
|
||||
- github.com/pkg/*
|
||||
|
||||
wsl:
|
||||
# See https://github.com/bombsimon/wsl/blob/master/doc/configuration.md for
|
||||
# documentation of available settings. These are the defaults for
|
||||
# `golangci-lint`.
|
||||
allow-assign-and-anything: false
|
||||
allow-assign-and-call: true
|
||||
allow-cuddle-declarations: false
|
||||
allow-multiline-assign: true
|
||||
allow-separated-leading-comment: false
|
||||
allow-trailing-comment: false
|
||||
force-case-trailing-whitespace: 0
|
||||
force-err-cuddling: false
|
||||
force-short-decl-cuddling: false
|
||||
strict-append: true
|
||||
|
||||
# The custom section can be used to define linter plugins to be loaded at runtime.
|
||||
# See README doc for more info.
|
||||
# custom:
|
||||
# Each custom linter should have a unique name.
|
||||
# example:
|
||||
# The path to the plugin *.so. Can be absolute or local. Required for each custom linter
|
||||
# path: /path/to/example.so
|
||||
# The description of the linter. Optional, just for documentation purposes.
|
||||
# description: This is an example usage of a plugin linter.
|
||||
# Intended to point to the repo location of the linter. Optional, just for documentation purposes.
|
||||
# original-url: github.com/golangci/example-linter
|
||||
|
||||
linters:
|
||||
#disable-all: true
|
||||
#enable:
|
||||
# - megacheck
|
||||
# - govet
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- prealloc
|
||||
#- tagliatelle
|
||||
#- wrapcheck
|
||||
#- forcetypeassert
|
||||
- goerr113
|
||||
- gomnd
|
||||
- wsl
|
||||
- testpackage
|
||||
- gochecknoglobals
|
||||
- interfacer
|
||||
- maligned
|
||||
- scopelint
|
||||
- gocritic
|
||||
- typecheck
|
||||
# presets:
|
||||
# - bugs
|
||||
# - unused
|
||||
fast: false
|
||||
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
- abcdef
|
||||
- tools/.*
|
||||
- test/.*
|
||||
- third_party/.*
|
||||
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- linters:
|
||||
- revive
|
||||
path: (log/.*)\.go
|
||||
- linters:
|
||||
- wrapcheck
|
||||
path: (cmd/.*|pkg/.*)\.go
|
||||
- linters:
|
||||
- typecheck
|
||||
path: (pkg/storage/.*)\.go
|
||||
|
||||
- path: (cmd/.*|test/.*|tools/.*)\.go
|
||||
linters:
|
||||
- forbidigo
|
||||
- path: (cmd/[a-z]*/.*|store/.*)\.go
|
||||
linters:
|
||||
- dupl
|
||||
- linters:
|
||||
- gocritic
|
||||
text: (hugeParam:|rangeValCopy:)
|
||||
|
||||
- path: (cmd/[a-z]*/.*)\.go
|
||||
linters:
|
||||
- lll
|
||||
|
||||
- path: (validator/.*|code/.*|validator/.*)
|
||||
linters:
|
||||
- gochecknoinits
|
||||
- path: (pkg/app/.*)\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
|
||||
# Exclude known linters from partially hard-vendored code,
|
||||
# which is impossible to exclude via "nolint" comments.
|
||||
- path: internal/hmac/
|
||||
text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# Exclude some staticcheck messages
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA9003:"
|
||||
|
||||
# Exclude lll issues for long lines with go:generate
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
|
||||
# The default value is false. If set to true exclude and exclude-rules
|
||||
# regular expressions become case sensitive.
|
||||
exclude-case-sensitive: false
|
||||
|
||||
# The list of ids of default excludes to include or disable. By default it's empty.
|
||||
include:
|
||||
- EXC0002 # disable excluding of issues about comments from golint
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
|
||||
# Show only new issues created after git revision `REV`
|
||||
new-from-rev: REV
|
||||
|
||||
# Show only new issues created in git patch with set file path.
|
||||
# new-from-patch: path/to/patch/file
|
||||
|
||||
# Fix found issues (if it's supported by the linter)
|
||||
fix: true
|
||||
|
||||
severity:
|
||||
# Default value is empty string.
|
||||
# Set the default severity for issues. If severity rules are defined and the issues
|
||||
# do not match or no severity is provided to the rule this will be the default
|
||||
# severity applied. Severities should match the supported severity names of the
|
||||
# selected out format.
|
||||
# - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity
|
||||
# - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity
|
||||
# - GitHub: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
|
||||
default-severity: error
|
||||
|
||||
# The default value is false.
|
||||
# If set to true severity-rules regular expressions become case sensitive.
|
||||
case-sensitive: false
|
||||
|
||||
# Default value is empty list.
|
||||
# When a list of severity rules are provided, severity information will be added to lint
|
||||
# issues. Severity rules have the same filtering capability as exclude rules except you
|
||||
# are allowed to specify one matcher per severity rule.
|
||||
# Only affects out formats that support setting severity information.
|
||||
rules:
|
||||
- linters:
|
||||
- dupl
|
||||
severity: info
|
||||
38
.travis.yml
38
.travis.yml
@ -1,38 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- redis-server
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
- local
|
||||
|
||||
before_install:
|
||||
- pwd
|
||||
|
||||
install:
|
||||
- cat /etc/hosts
|
||||
|
||||
script:
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
|
||||
22
DONATOR.MD
22
DONATOR.MD
@ -1,22 +0,0 @@
|
||||
# Donators
|
||||
|
||||
|
||||
| Name | Channel | Amount
|
||||
|---|---|---
|
||||
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥201.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
|
||||
|潘兄|wechat|¥100.00
|
||||
|Fly的狐狸|wechat|¥100.00
|
||||
|土豆相公|alipay|¥66.60
|
||||
|上海金保证网络科技|bank|¥2000.00
|
||||
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
117
README.MD
117
README.MD
@ -1,81 +1,106 @@
|
||||
# GoFrame
|
||||
# GoFrame
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/logo.png" width="100"/>
|
||||
<img src="https://goframe.org/statics/image/gf-head-large.png" width="100"/>
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/actions/workflows/gf.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
|
||||
</div>
|
||||
|
||||
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites and many more features.
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
|
||||
|
||||
# Features
|
||||
- modular, loosely coupled design
|
||||
- rich components, out-of-the-box
|
||||
- automatic codes generating for efficiency
|
||||
- simple and easy to use, detailed documentation
|
||||
- interface designed components, with high scalability
|
||||
- fully supported tracing and error stack feature
|
||||
- specially developed and powerful ORM component
|
||||
- robust engineering design specifications
|
||||
- convenient development CLI tool provide
|
||||
- OpenTelemetry observability features support
|
||||
- OpenAPIV3 documentation generating, automatically
|
||||
- much, much more...ready to explore?
|
||||
|
||||
# Installation
|
||||
Enter your repo. directory and execute following command:
|
||||
|
||||
## primary module
|
||||
```bash
|
||||
go get -u -v github.com/gogf/gf/v2
|
||||
```
|
||||
go get -u github.com/gogf/gf
|
||||
```
|
||||
or use `go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf latest
|
||||
|
||||
## cli tool
|
||||
```bash
|
||||
go install github.com/gogf/gf/cmd/gf/v2
|
||||
```
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.10
|
||||
golang version >= 1.15
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png"/>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# Quick Start
|
||||
|
||||
```go
|
||||
package main
|
||||
# Documentation
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[View More..](https://goframe.org/start/index)
|
||||
* Chinese Official Site(中文官网): [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.
|
||||
`GoFrame` 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/)
|
||||
- [VIVO](https://www.vivo.com/)
|
||||
- [MedLinker](https://www.medlinker.com/)
|
||||
- [KuCoin](https://www.kucoin.io/)
|
||||
- [LeYouJia](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [37](https://www.37.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://contributors-img.web.app/image?repo=gogf/gf" /></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 `GoFrame`, why not [buy developer a cup of coffee](https://goframe.org/pages/viewpage.action?pageId=1115633)?
|
||||
|
||||
# Sponsors
|
||||
We appreciate any kind of sponsorship for `GoFrame` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
<!--
|
||||
# Sponsor
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.
|
||||
-->
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
86
README_ZH.MD
86
README_ZH.MD
@ -1,86 +0,0 @@
|
||||
# GoFrame
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/logo.png" width="100"/>
|
||||
|
||||
[](https://godoc.org/github.com/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)
|
||||
[](https://github.com/gogf/gf)
|
||||
|
||||
</div>
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
|
||||
并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 致力于项目的通用方案;
|
||||
* 更适合企业及团队使用;
|
||||
* 更多请查阅文档及源码;
|
||||
|
||||
# 安装
|
||||
```html
|
||||
go get -u github.com/gogf/gf
|
||||
```
|
||||
或者
|
||||
`go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf latest
|
||||
```
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
golang版本 >= 1.10
|
||||
```
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png"/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:[https://goframe.org](https://goframe.org)
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
|
||||
# 使用
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[更多..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 捐赠
|
||||
|
||||
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](DONATOR.MD)!
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
494
RELEASE.MD
494
RELEASE.MD
@ -1,494 +0,0 @@
|
||||
# `v1.7.0`
|
||||
## 新功能/改进
|
||||
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/g/container/garray
|
||||
1. 对常用的`container`集合模块: `gset`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gset
|
||||
1. 对常用的`container`MAP模块: `gmap`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gmap
|
||||
1. 对常用的字符串模块: `gstr`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/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/g/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);
|
||||
|
||||
|
||||
|
||||
127
TODO.MD
127
TODO.MD
@ -1,127 +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增加支持阻塞添加任务接口;
|
||||
|
||||
|
||||
|
||||
# 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标签失效问题;
|
||||
21
cmd/gf/LICENSE
Normal file
21
cmd/gf/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 john@goframe.org https://goframe.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
18
cmd/gf/Makefile
Normal file
18
cmd/gf/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
pack: pack.template-single pack.template-mono
|
||||
|
||||
pack.template-single:
|
||||
@rm -fr temp
|
||||
@mkdir temp || exit 0
|
||||
@cd temp && git clone https://github.com/gogf/template-single
|
||||
@rm -fr temp/template-single/.git
|
||||
@cd temp && gf pack template-single ../internal/packed/template-single.go -n=packed -y
|
||||
@rm -fr temp
|
||||
|
||||
pack.template-mono:
|
||||
@rm -fr temp
|
||||
@mkdir temp || exit 0
|
||||
@cd temp && git clone https://github.com/gogf/template-mono
|
||||
@rm -fr temp/template-mono/.git
|
||||
@cd temp && gf pack template-mono ../internal/packed/template-mono.go -n=packed -y
|
||||
@rm -fr temp
|
||||
61
cmd/gf/README.MD
Normal file
61
cmd/gf/README.MD
Normal file
@ -0,0 +1,61 @@
|
||||
# gf
|
||||
|
||||
`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience.
|
||||
|
||||
|
||||
## 1. Install
|
||||
|
||||
You can also install `gf` tool using pre-built binaries: https://github.com/gogf/gf/releases
|
||||
|
||||
1. `Mac` & `Linux`
|
||||
```shell
|
||||
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) && chmod +x gf && ./gf install -y && rm ./gf
|
||||
```
|
||||
> If you're using `zsh`, you might need rename your alias by command `alias gf=gf` to resolve the conflicts between `gf` and `git fetch`.
|
||||
|
||||
2. `Windows`
|
||||
|
||||
Manually download, execute it and then follow the instruction.
|
||||
|
||||
3. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building.
|
||||
|
||||
## 2. Commands
|
||||
```html
|
||||
$ gf
|
||||
USAGE
|
||||
gf COMMAND [OPTION]
|
||||
|
||||
COMMAND
|
||||
env show current Golang environment variables
|
||||
run running go codes with hot-compiled-like feature
|
||||
gen automatically generate go files for dao/do/entity/pb/pbentity
|
||||
tpl template parsing and building commands
|
||||
init create and initialize an empty GoFrame project
|
||||
pack packing any file/directory to a resource file, or a go file
|
||||
build cross-building go project for lots of platforms
|
||||
docker build docker image for current GoFrame project
|
||||
install install gf binary to system (might need root/admin permission)
|
||||
version show version information of current binary
|
||||
|
||||
OPTION
|
||||
-y, --yes all yes for all command without prompt ask
|
||||
-v, --version show version information of current binary
|
||||
-d, --debug show internal detailed debugging information
|
||||
-h, --help more information about this command
|
||||
|
||||
ADDITIONAL
|
||||
Use "gf COMMAND -h" for details about a command.
|
||||
```
|
||||
|
||||
## 3. FAQ
|
||||
|
||||
### 1). Command `gf run` returns `pipe: too many open files`
|
||||
|
||||
Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
18
cmd/gf/go.mod
Normal file
18
cmd/gf/go.mod
Normal file
@ -0,0 +1,18 @@
|
||||
module github.com/gogf/gf/cmd/gf/v2
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/v2 v2.0.0-rc
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 => ../../contrib/drivers/mssql/
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 => ../../contrib/drivers/pgsql/
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 => ../../contrib/drivers/sqlite/
|
||||
github.com/gogf/gf/v2 => ../../
|
||||
)
|
||||
159
cmd/gf/go.sum
Normal file
159
cmd/gf/go.sum
Normal file
@ -0,0 +1,159 @@
|
||||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0 h1:BNPMYUONPNbLneMttKSjQhOTlFLOD9U22HNG1KrIN2Y=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM=
|
||||
go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
|
||||
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
64
cmd/gf/internal/cmd/cmd.go
Normal file
64
cmd/gf/internal/cmd/cmd.go
Normal file
@ -0,0 +1,64 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
GF = cGF{}
|
||||
)
|
||||
|
||||
type cGF struct {
|
||||
g.Meta `name:"gf" ad:"{cGFAd}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cGFAd = `
|
||||
ADDITIONAL
|
||||
Use "gf COMMAND -h" for details about a command.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGFAd`: cGFAd,
|
||||
})
|
||||
}
|
||||
|
||||
type cGFInput struct {
|
||||
g.Meta `name:"gf"`
|
||||
Yes bool `short:"y" name:"yes" brief:"all yes for all command without prompt ask" orphan:"true"`
|
||||
Version bool `short:"v" name:"version" brief:"show version information of current binary" orphan:"true"`
|
||||
Debug bool `short:"d" name:"debug" brief:"show internal detailed debugging information" orphan:"true"`
|
||||
}
|
||||
type cGFOutput struct{}
|
||||
|
||||
func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) {
|
||||
// Version.
|
||||
if in.Version {
|
||||
_, err = Version.Index(ctx, cVersionInput{})
|
||||
return
|
||||
}
|
||||
// No argument or option, do installation checks.
|
||||
if !service.Install.IsInstalled() {
|
||||
mlog.Print("hi, it seams it's the first time you installing gf cli.")
|
||||
s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ")
|
||||
if strings.EqualFold(s, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
return
|
||||
}
|
||||
}
|
||||
// Print help content.
|
||||
gcmd.CommandFromCtx(ctx).Print()
|
||||
return
|
||||
}
|
||||
314
cmd/gf/internal/cmd/cmd_build.go
Normal file
314
cmd/gf/internal/cmd/cmd_build.go
Normal file
@ -0,0 +1,314 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Build = cBuild{
|
||||
nodeNameInConfigFile: "gfcli.build",
|
||||
packedGoFileName: "build_pack_data.go",
|
||||
}
|
||||
)
|
||||
|
||||
type cBuild struct {
|
||||
g.Meta `name:"build" brief:"{cBuildBrief}" dc:"{cBuildDc}" eg:"{cBuildEg}" ad:"{cBuildAd}"`
|
||||
nodeNameInConfigFile string // nodeNameInConfigFile is the node name for compiler configurations in configuration file.
|
||||
packedGoFileName string // packedGoFileName specifies the file name for packing common folders into one single go file.
|
||||
}
|
||||
|
||||
const (
|
||||
cBuildBrief = `cross-building go project for lots of platforms`
|
||||
cBuildEg = `
|
||||
gf build main.go
|
||||
gf build main.go --pack public,template
|
||||
gf build main.go --cgo
|
||||
gf build main.go -m none
|
||||
gf build main.go -n my-app -a all -s all
|
||||
gf build main.go -n my-app -a amd64,386 -s linux -p .
|
||||
gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
|
||||
`
|
||||
cBuildDc = `
|
||||
The "build" command is most commonly used command, which is designed as a powerful wrapper for
|
||||
"go build" command for convenience cross-compiling usage.
|
||||
It provides much more features for building binary:
|
||||
1. Cross-Compiling for many platforms and architectures.
|
||||
2. Configuration file support for compiling.
|
||||
3. Build-In Variables.
|
||||
`
|
||||
cBuildAd = `
|
||||
PLATFORMS
|
||||
darwin amd64,arm64
|
||||
freebsd 386,amd64,arm
|
||||
linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le
|
||||
netbsd 386,amd64,arm
|
||||
openbsd 386,amd64,arm
|
||||
windows 386,amd64
|
||||
`
|
||||
// https://golang.google.cn/doc/install/source
|
||||
cBuildPlatforms = `
|
||||
darwin amd64
|
||||
darwin arm64
|
||||
ios amd64
|
||||
ios arm64
|
||||
freebsd 386
|
||||
freebsd amd64
|
||||
freebsd arm
|
||||
linux 386
|
||||
linux amd64
|
||||
linux arm
|
||||
linux arm64
|
||||
linux ppc64
|
||||
linux ppc64le
|
||||
linux mips
|
||||
linux mipsle
|
||||
linux mips64
|
||||
linux mips64le
|
||||
netbsd 386
|
||||
netbsd amd64
|
||||
netbsd arm
|
||||
openbsd 386
|
||||
openbsd amd64
|
||||
openbsd arm
|
||||
windows 386
|
||||
windows amd64
|
||||
android arm
|
||||
dragonfly amd64
|
||||
plan9 386
|
||||
plan9 amd64
|
||||
solaris amd64
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cBuildBrief`: cBuildBrief,
|
||||
`cBuildDc`: cBuildDc,
|
||||
`cBuildEg`: cBuildEg,
|
||||
`cBuildAd`: cBuildAd,
|
||||
})
|
||||
}
|
||||
|
||||
type cBuildInput struct {
|
||||
g.Meta `name:"build" config:"gfcli.build"`
|
||||
File string `name:"FILE" arg:"true" brief:"building file path"`
|
||||
Name string `short:"n" name:"name" brief:"output binary name"`
|
||||
Version string `short:"v" name:"version" brief:"output binary version"`
|
||||
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
|
||||
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
|
||||
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
|
||||
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
|
||||
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
|
||||
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
|
||||
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
Exit bool `name:"exit" brief:"exit building when any error occurs, default is false" orphan:"true"`
|
||||
Pack string `name:"pack" brief:"pack specified folder into temporary go file before building and removes it after built"`
|
||||
}
|
||||
type cBuildOutput struct{}
|
||||
|
||||
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
|
||||
mlog.SetHeaderPrint(true)
|
||||
|
||||
mlog.Debugf(`build input: %+v`, in)
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
var (
|
||||
parser = gcmd.ParserFromCtx(ctx)
|
||||
file = parser.GetArg(2).String()
|
||||
)
|
||||
if len(file) < 1 {
|
||||
// Check and use the main.go file.
|
||||
if gfile.Exists("main.go") {
|
||||
file = "main.go"
|
||||
} else {
|
||||
mlog.Fatal("build file path cannot be empty")
|
||||
}
|
||||
}
|
||||
if in.Name == "" {
|
||||
in.Name = gfile.Name(file)
|
||||
}
|
||||
if len(in.Name) < 1 || in.Name == "*" {
|
||||
mlog.Fatal("name cannot be empty")
|
||||
}
|
||||
if in.Mod != "" && in.Mod != "none" {
|
||||
mlog.Debugf(`mod is %s`, in.Mod)
|
||||
if in.Extra == "" {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s`, in.Mod)
|
||||
} else {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s %s`, in.Mod, in.Extra)
|
||||
}
|
||||
}
|
||||
if in.Extra != "" {
|
||||
in.Extra += " "
|
||||
}
|
||||
var (
|
||||
customSystems = gstr.SplitAndTrim(in.System, ",")
|
||||
customArches = gstr.SplitAndTrim(in.Arch, ",")
|
||||
)
|
||||
if len(in.Version) > 0 {
|
||||
in.Path += "/" + in.Version
|
||||
}
|
||||
// System and arch checks.
|
||||
var (
|
||||
spaceRegex = regexp.MustCompile(`\s+`)
|
||||
platformMap = make(map[string]map[string]bool)
|
||||
)
|
||||
for _, line := range strings.Split(strings.TrimSpace(cBuildPlatforms), "\n") {
|
||||
line = gstr.Trim(line)
|
||||
line = spaceRegex.ReplaceAllString(line, " ")
|
||||
var (
|
||||
array = strings.Split(line, " ")
|
||||
system = strings.TrimSpace(array[0])
|
||||
arch = strings.TrimSpace(array[1])
|
||||
)
|
||||
if platformMap[system] == nil {
|
||||
platformMap[system] = make(map[string]bool)
|
||||
}
|
||||
platformMap[system][arch] = true
|
||||
}
|
||||
// Auto packing.
|
||||
if len(in.Pack) > 0 {
|
||||
dataFilePath := fmt.Sprintf(`packed/%s`, c.packedGoFileName)
|
||||
if !gfile.Exists(dataFilePath) {
|
||||
// Remove the go file that is automatically packed resource.
|
||||
defer func() {
|
||||
_ = gfile.Remove(dataFilePath)
|
||||
mlog.Printf(`remove the automatically generated resource go file: %s`, dataFilePath)
|
||||
}()
|
||||
}
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s`, in.Pack, dataFilePath)
|
||||
mlog.Print(packCmd)
|
||||
gproc.MustShellRun(packCmd)
|
||||
}
|
||||
|
||||
// Injected information by building flags.
|
||||
ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`, c.getBuildInVarStr(in))
|
||||
|
||||
// start building
|
||||
mlog.Print("start building...")
|
||||
if in.Cgo {
|
||||
genv.MustSet("CGO_ENABLED", "1")
|
||||
} else {
|
||||
genv.MustSet("CGO_ENABLED", "0")
|
||||
}
|
||||
var (
|
||||
cmd = ""
|
||||
ext = ""
|
||||
)
|
||||
for system, item := range platformMap {
|
||||
cmd = ""
|
||||
ext = ""
|
||||
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
|
||||
continue
|
||||
}
|
||||
for arch, _ := range item {
|
||||
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
|
||||
continue
|
||||
}
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
// Single binary building, output the binary to current working folder.
|
||||
output := ""
|
||||
if len(in.Output) > 0 {
|
||||
output = "-o " + in.Output + ext
|
||||
} else {
|
||||
output = "-o " + in.Name + ext
|
||||
}
|
||||
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
|
||||
} else {
|
||||
// Cross-building, output the compiled binary to specified path.
|
||||
if system == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
genv.MustSet("GOOS", system)
|
||||
genv.MustSet("GOARCH", arch)
|
||||
cmd = fmt.Sprintf(
|
||||
`go build -o %s/%s/%s%s -ldflags "%s" %s%s`,
|
||||
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
|
||||
)
|
||||
}
|
||||
mlog.Debug(cmd)
|
||||
// It's not necessary printing the complete command string.
|
||||
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
|
||||
mlog.Print(cmdShow)
|
||||
if result, err := gproc.ShellExec(cmd); err != nil {
|
||||
mlog.Printf(
|
||||
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
|
||||
system, arch, gstr.Trim(result),
|
||||
`you may use command option "--debug" to enable debug info and check the details`,
|
||||
)
|
||||
if in.Exit {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
mlog.Debug(gstr.Trim(result))
|
||||
}
|
||||
// single binary building.
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
goto buildDone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildDone:
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
|
||||
// file as json.
|
||||
func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
buildInVarMap := in.VarMap
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit()
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return gbase64.EncodeToString(b)
|
||||
}
|
||||
|
||||
// getGitCommit retrieves and returns the latest git commit hash string if present.
|
||||
func (c cBuild) getGitCommit() string {
|
||||
if gproc.SearchBinary("git") == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
|
||||
s, _ = gproc.ShellExec(cmd)
|
||||
)
|
||||
mlog.Debug(cmd)
|
||||
if s != "" {
|
||||
if !gstr.Contains(s, "fatal") {
|
||||
return gstr.Trim(s)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
116
cmd/gf/internal/cmd/cmd_docker.go
Normal file
116
cmd/gf/internal/cmd/cmd_docker.go
Normal file
@ -0,0 +1,116 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Docker = cDocker{}
|
||||
)
|
||||
|
||||
type cDocker struct {
|
||||
g.Meta `name:"docker" usage:"{cDockerUsage}" brief:"{cDockerBrief}" eg:"{cDockerEg}" dc:"{cDockerDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cDockerUsage = `gf docker [MAIN] [OPTION]`
|
||||
cDockerBrief = `build docker image for current GoFrame project`
|
||||
cDockerEg = `
|
||||
gf docker
|
||||
gf docker -t hub.docker.com/john/image:tag
|
||||
gf docker -p -t hub.docker.com/john/image:tag
|
||||
gf docker main.go
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -t hub.docker.com/john/image:tag
|
||||
`
|
||||
cDockerDc = `
|
||||
The "docker" command builds the GF project to a docker images.
|
||||
It runs "gf build" firstly to compile the project to binary file.
|
||||
It then runs "docker build" command automatically to generate the docker image.
|
||||
You should have docker installed, and there must be a Dockerfile in the root of the project.
|
||||
`
|
||||
cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build`
|
||||
cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default`
|
||||
cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default`
|
||||
cDockerShellBrief = `path of the shell file which is executed before docker build`
|
||||
cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed`
|
||||
cDockerTagBrief = `tag name for this docker, which is usually used for docker push`
|
||||
cDockerExtraBrief = `extra build options passed to "docker image"`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cDockerUsage`: cDockerUsage,
|
||||
`cDockerBrief`: cDockerBrief,
|
||||
`cDockerEg`: cDockerEg,
|
||||
`cDockerDc`: cDockerDc,
|
||||
`cDockerMainBrief`: cDockerMainBrief,
|
||||
`cDockerFileBrief`: cDockerFileBrief,
|
||||
`cDockerShellBrief`: cDockerShellBrief,
|
||||
`cDockerBuildBrief`: cDockerBuildBrief,
|
||||
`cDockerPushBrief`: cDockerPushBrief,
|
||||
`cDockerTagBrief`: cDockerTagBrief,
|
||||
`cDockerExtraBrief`: cDockerExtraBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cDockerInput struct {
|
||||
g.Meta `name:"docker" config:"gfcli.docker"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"`
|
||||
Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"`
|
||||
Tag string `name:"tag" short:"t" brief:"{cDockerTagBrief}"`
|
||||
Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"`
|
||||
}
|
||||
type cDockerOutput struct{}
|
||||
|
||||
func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("docker") == "" {
|
||||
mlog.Fatalf(`command "docker" not found in your environment, please install docker first to proceed this command`)
|
||||
}
|
||||
|
||||
// Binary build.
|
||||
in.Build += " --exit"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if gfile.Exists(in.Shell) {
|
||||
if err = gproc.ShellRun(gfile.GetContents(in.Shell)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Docker build.
|
||||
dockerBuildOptions := ""
|
||||
if in.Tag != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`-t %s`, in.Tag)
|
||||
}
|
||||
if in.Extra != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
|
||||
}
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions)); err != nil {
|
||||
return
|
||||
}
|
||||
// Docker push.
|
||||
if in.Tag == "" || !in.Push {
|
||||
return
|
||||
}
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, in.Tag)); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
61
cmd/gf/internal/cmd/cmd_env.go
Normal file
61
cmd/gf/internal/cmd/cmd_env.go
Normal file
@ -0,0 +1,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var (
|
||||
Env = cEnv{}
|
||||
)
|
||||
|
||||
type cEnv struct {
|
||||
g.Meta `name:"env" brief:"show current Golang environment variables"`
|
||||
}
|
||||
|
||||
type cEnvInput struct {
|
||||
g.Meta `name:"env"`
|
||||
}
|
||||
type cEnvOutput struct{}
|
||||
|
||||
func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err error) {
|
||||
result, err := gproc.ShellExec("go env")
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if result == "" {
|
||||
mlog.Fatal(`retrieving Golang environment variables failed, did you install Golang?`)
|
||||
}
|
||||
var (
|
||||
lines = gstr.Split(result, "\n")
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
array := make([][]string, 0)
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if gstr.Pos(line, "set ") == 0 {
|
||||
line = line[4:]
|
||||
}
|
||||
match, _ := gregex.MatchString(`(.+?)=(.*)`, line)
|
||||
if len(match) < 3 {
|
||||
mlog.Fatalf(`invalid Golang environment variable: "%s"`, line)
|
||||
}
|
||||
array = append(array, []string{gstr.Trim(match[1]), gstr.Trim(match[2])})
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
mlog.Print(buffer.String())
|
||||
return
|
||||
}
|
||||
30
cmd/gf/internal/cmd/cmd_gen.go
Normal file
30
cmd/gf/internal/cmd/cmd_gen.go
Normal file
@ -0,0 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Gen = cGen{}
|
||||
)
|
||||
|
||||
type cGen struct {
|
||||
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cGenBrief = `automatically generate go files for dao/do/entity/pb/pbentity`
|
||||
cGenDc = `
|
||||
The "gen" command is designed for multiple generating purposes.
|
||||
It's currently supporting generating go files for ORM models, protobuf and protobuf entity files.
|
||||
Please use "gf gen dao -h" for specified type help.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenBrief`: cGenBrief,
|
||||
`cGenDc`: cGenDc,
|
||||
})
|
||||
}
|
||||
801
cmd/gf/internal/cmd/cmd_gen_dao.go
Normal file
801
cmd/gf/internal/cmd/cmd_gen_dao.go
Normal file
@ -0,0 +1,801 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDaoPath = `service/internal/dao`
|
||||
defaultDoPath = `service/internal/do`
|
||||
defaultEntityPath = `model/entity`
|
||||
cGenDaoConfig = `gfcli.gen.dao`
|
||||
cGenDaoUsage = `gf gen dao [OPTION]`
|
||||
cGenDaoBrief = `automatically generate go files for dao/do/entity`
|
||||
cGenDaoEg = `
|
||||
gf gen dao
|
||||
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen dao -p ./model -c config.yaml -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -r user_
|
||||
`
|
||||
|
||||
cGenDaoAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
tables: "order,products"
|
||||
jsonCase: "CamelLower"
|
||||
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "./my-app"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
`
|
||||
cGenDaoBriefPath = `directory path for generated files`
|
||||
cGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
|
||||
cGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
|
||||
cGenDaoBriefWithTime = `add created time for auto produced go files`
|
||||
cGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
|
||||
cGenDaoBriefImportPrefix = `custom import prefix for generated go files`
|
||||
cGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
|
||||
cGenDaoBriefModelFile = `custom file name for storing generated model content`
|
||||
cGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
|
||||
cGenDaoBriefDescriptionTag = `add comment to description tag for each field`
|
||||
cGenDaoBriefNoJsonTag = `no json tag will be added for each field`
|
||||
cGenDaoBriefNoModelComment = `no model comment will be added for each field`
|
||||
cGenDaoBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
cGenDaoBriefJsonCase = `
|
||||
generated json tag case for model struct, cases are as follows:
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
tplVarTableName = `{TplTableName}`
|
||||
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
|
||||
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
|
||||
tplVarPackageImports = `{TplPackageImports}`
|
||||
tplVarImportPrefix = `{TplImportPrefix}`
|
||||
tplVarStructDefine = `{TplStructDefine}`
|
||||
tplVarColumnDefine = `{TplColumnDefine}`
|
||||
tplVarColumnNames = `{TplColumnNames}`
|
||||
tplVarGroupName = `{TplGroupName}`
|
||||
tplVarDatetimeStr = `{TplDatetimeStr}`
|
||||
)
|
||||
|
||||
var (
|
||||
createdAt = gtime.Now()
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenDaoConfig`: cGenDaoConfig,
|
||||
`cGenDaoUsage`: cGenDaoUsage,
|
||||
`cGenDaoBrief`: cGenDaoBrief,
|
||||
`cGenDaoEg`: cGenDaoEg,
|
||||
`cGenDaoAd`: cGenDaoAd,
|
||||
`cGenDaoBriefPath`: cGenDaoBriefPath,
|
||||
`cGenDaoBriefLink`: cGenDaoBriefLink,
|
||||
`cGenDaoBriefTables`: cGenDaoBriefTables,
|
||||
`cGenDaoBriefTablesEx`: cGenDaoBriefTablesEx,
|
||||
`cGenDaoBriefPrefix`: cGenDaoBriefPrefix,
|
||||
`cGenDaoBriefRemovePrefix`: cGenDaoBriefRemovePrefix,
|
||||
`cGenDaoBriefStdTime`: cGenDaoBriefStdTime,
|
||||
`cGenDaoBriefWithTime`: cGenDaoBriefWithTime,
|
||||
`cGenDaoBriefGJsonSupport`: cGenDaoBriefGJsonSupport,
|
||||
`cGenDaoBriefImportPrefix`: cGenDaoBriefImportPrefix,
|
||||
`cGenDaoBriefOverwriteDao`: cGenDaoBriefOverwriteDao,
|
||||
`cGenDaoBriefModelFile`: cGenDaoBriefModelFile,
|
||||
`cGenDaoBriefModelFileForDao`: cGenDaoBriefModelFileForDao,
|
||||
`cGenDaoBriefDescriptionTag`: cGenDaoBriefDescriptionTag,
|
||||
`cGenDaoBriefNoJsonTag`: cGenDaoBriefNoJsonTag,
|
||||
`cGenDaoBriefNoModelComment`: cGenDaoBriefNoModelComment,
|
||||
`cGenDaoBriefGroup`: cGenDaoBriefGroup,
|
||||
`cGenDaoBriefJsonCase`: cGenDaoBriefJsonCase,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{cGenDaoConfig}" usage:"{cGenDaoUsage}" brief:"{cGenDaoBrief}" eg:"{cGenDaoEg}" ad:"{cGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"e" brief:"{cGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{cGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{cGenDaoBriefImportPrefix}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{cGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"c" brief:"{cGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{cGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"o" brief:"{cGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"d" brief:"{cGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{cGenDaoBriefNoJsonTag" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{cGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
}
|
||||
cGenDaoOutput struct{}
|
||||
|
||||
cGenDaoInternalInput struct {
|
||||
cGenDaoInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
ModName string // ModName specifies the module name of current golang project, which is used for import purpose.
|
||||
}
|
||||
)
|
||||
|
||||
func (c cGen) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
|
||||
if g.Cfg().Available(ctx) {
|
||||
v := g.Cfg().MustGet(ctx, cGenDaoConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenDaoForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// doGenDaoForArray implements the "gen dao" command for configuration array.
|
||||
func doGenDaoForArray(ctx context.Context, index int, in cGenDaoInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
modName string // Go module name, eg: github.com/gogf/gf.
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
|
||||
mlog.Fatalf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
if db, err = gdb.Instance(tempGroup); err != nil {
|
||||
mlog.Debugf(`database initialization failed: %+v`, err)
|
||||
}
|
||||
} else {
|
||||
mlog.Fatalf(`invalid database configuration: %s`, in.Link)
|
||||
}
|
||||
} else {
|
||||
db = g.DB(in.Group)
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
|
||||
}
|
||||
|
||||
var tableNames []string
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: %+v", err)
|
||||
}
|
||||
}
|
||||
// Table excluding.
|
||||
if in.TablesEx != "" {
|
||||
array := garray.NewStrArrayFrom(tableNames)
|
||||
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
|
||||
array.RemoveValue(v)
|
||||
}
|
||||
tableNames = array.Slice()
|
||||
}
|
||||
|
||||
// Generating dao & model go files one by one according to given table name.
|
||||
newTableNames := make([]string, len(tableNames))
|
||||
for i, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
newTableName = in.Prefix + newTableName
|
||||
newTableNames[i] = newTableName
|
||||
// Dao.
|
||||
generateDao(ctx, db, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
// Do.
|
||||
generateDo(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
// Entity.
|
||||
generateEntity(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
|
||||
// generateDaoContentFile generates the dao and model content of given table.
|
||||
func generateDao(ctx context.Context, db gdb.DB, in cGenDaoInternalInput) {
|
||||
// Generating table data preparing.
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
|
||||
}
|
||||
var (
|
||||
dirRealPath = gfile.RealPath(in.Path)
|
||||
dirPathDao = gfile.Join(in.Path, defaultDaoPath)
|
||||
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
|
||||
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
|
||||
importPrefix = in.ImportPrefix
|
||||
)
|
||||
if importPrefix == "" {
|
||||
if dirRealPath == "" {
|
||||
dirRealPath = in.Path
|
||||
importPrefix = dirRealPath
|
||||
importPrefix = gstr.Trim(dirRealPath, "./")
|
||||
} else {
|
||||
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
|
||||
}
|
||||
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
|
||||
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, defaultDaoPath}, "/")
|
||||
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
|
||||
}
|
||||
|
||||
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
|
||||
// Add suffix to avoid the table name which contains "_test",
|
||||
// which would make the go file a testing file.
|
||||
fileName += "_table"
|
||||
}
|
||||
|
||||
// dao - index
|
||||
generateDaoIndex(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName)
|
||||
|
||||
// dao - internal
|
||||
generateDaoInternal(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap)
|
||||
}
|
||||
|
||||
func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
doDirPath = gfile.Join(in.Path, defaultDoPath)
|
||||
)
|
||||
in.NoJsonTag = true
|
||||
in.DescriptionTag = false
|
||||
in.NoModelComment = false
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
in.TableName = tableName
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition = generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: true,
|
||||
})
|
||||
)
|
||||
// replace all types to interface{}.
|
||||
structDefinition, _ = gregex.ReplaceStringFuncMatch(
|
||||
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
|
||||
structDefinition,
|
||||
func(match []string) string {
|
||||
// If the type is already a pointer/slice/map, it does nothing.
|
||||
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
|
||||
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
modelContent := generateDoContent(
|
||||
in,
|
||||
tableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
structDefinition,
|
||||
)
|
||||
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(doFilePath)
|
||||
mlog.Print("generated:", doFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
entityDirPath = gfile.Join(in.Path, defaultEntityPath)
|
||||
)
|
||||
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
entityFilePath = gfile.Join(entityDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
entityContent = generateEntityContent(
|
||||
in,
|
||||
newTableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
}),
|
||||
)
|
||||
)
|
||||
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(entityFilePath)
|
||||
mlog.Print("generated:", entityFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getImportPartContent(source string, isDo bool) string {
|
||||
var (
|
||||
packageImportsArray = garray.NewStrArray()
|
||||
)
|
||||
|
||||
if isDo {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
|
||||
}
|
||||
|
||||
// Time package recognition.
|
||||
if strings.Contains(source, "gtime.Time") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
|
||||
} else if strings.Contains(source, "time.Time") {
|
||||
packageImportsArray.Append(`"time"`)
|
||||
}
|
||||
|
||||
// Json type.
|
||||
if strings.Contains(source, "gjson.Json") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
|
||||
}
|
||||
|
||||
// Generate and write content to golang file.
|
||||
packageImportsStr := ""
|
||||
if packageImportsArray.Len() > 0 {
|
||||
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
|
||||
}
|
||||
return packageImportsStr
|
||||
}
|
||||
|
||||
func generateEntityContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
entityContent := gstr.ReplaceByMap(consts.TemplateGenDaoEntityContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, false),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
entityContent = replaceDefaultVar(in, entityContent)
|
||||
return entityContent
|
||||
}
|
||||
|
||||
func generateDoContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
doContent := gstr.ReplaceByMap(consts.TemplateGenDaoDoContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, true),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
doContent = replaceDefaultVar(in, doContent)
|
||||
return doContent
|
||||
}
|
||||
|
||||
func generateDaoIndex(in cGenDaoInternalInput, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string) {
|
||||
path := gfile.Join(dirPathDao, fileName+".go")
|
||||
if in.OverwriteDao || !gfile.Exists(path) {
|
||||
indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
})
|
||||
indexContent = replaceDefaultVar(in, indexContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateDaoInternal(
|
||||
in cGenDaoInternalInput,
|
||||
tableNameCamelCase, tableNameCamelLowerCase, importPrefix string,
|
||||
dirPathDao, fileName string,
|
||||
fieldMap map[string]*gdb.TableField,
|
||||
) {
|
||||
path := gfile.Join(dirPathDao, "internal", fileName+".go")
|
||||
modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarGroupName: in.Group,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(fieldMap)),
|
||||
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(fieldMap)),
|
||||
})
|
||||
modelContent = replaceDefaultVar(in, modelContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
func replaceDefaultVar(in cGenDaoInternalInput, origin string) string {
|
||||
var tplDatetimeStr string
|
||||
if in.WithTime {
|
||||
tplDatetimeStr = fmt.Sprintf(`Created at %s`, createdAt.String())
|
||||
}
|
||||
return gstr.ReplaceByMap(origin, g.MapStrStr{
|
||||
tplVarDatetimeStr: tplDatetimeStr,
|
||||
})
|
||||
}
|
||||
|
||||
type generateStructDefinitionInput struct {
|
||||
cGenDaoInternalInput
|
||||
StructName string // Struct name.
|
||||
FieldMap map[string]*gdb.TableField // Table field map.
|
||||
IsDo bool // Is generating DTO struct.
|
||||
}
|
||||
|
||||
func generateStructDefinition(in generateStructDefinitionInput) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
array := make([][]string, len(in.FieldMap))
|
||||
names := sortFieldKeyForDao(in.FieldMap)
|
||||
for index, name := range names {
|
||||
field := in.FieldMap[name]
|
||||
array[index] = generateStructFieldDefinition(field, in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
stContent = gstr.Replace(stContent, "` ", "`")
|
||||
stContent = gstr.Replace(stContent, "``", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
|
||||
if in.IsDo {
|
||||
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
|
||||
}
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateStructFieldForModel generates and returns the attribute definition for specified field.
|
||||
func generateStructFieldDefinition(field *gdb.TableField, in generateStructDefinitionInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "[]byte"
|
||||
|
||||
case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint"
|
||||
} else {
|
||||
typeName = "int"
|
||||
}
|
||||
|
||||
case "int4", "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float32"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney", "numeric":
|
||||
typeName = "float64"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
case "json", "jsonb":
|
||||
if in.GJsonSupport {
|
||||
typeName = "*gjson.Json"
|
||||
} else {
|
||||
typeName = "string"
|
||||
}
|
||||
default:
|
||||
// Automatically detect its data type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "float64"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "[]byte"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tagKey = "`"
|
||||
result = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + typeName,
|
||||
}
|
||||
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
|
||||
)
|
||||
|
||||
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
for k, v := range result {
|
||||
if in.NoJsonTag {
|
||||
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
|
||||
}
|
||||
if !in.DescriptionTag {
|
||||
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
|
||||
}
|
||||
if in.NoModelComment {
|
||||
v, _ = gregex.ReplaceString(`//.+`, ``, v)
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// formatComment formats the comment string to fit the golang code without any lines.
|
||||
func formatComment(comment string) string {
|
||||
comment = gstr.ReplaceByArray(comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment = gstr.Trim(comment)
|
||||
return comment
|
||||
}
|
||||
|
||||
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
|
||||
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
var (
|
||||
field = fieldMap[name]
|
||||
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
}))
|
||||
)
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" # " + "string",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
defineContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
defineContent = gstr.Replace(defineContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(defineContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
|
||||
// for specified table.
|
||||
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
field := fieldMap[name]
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name) + ":",
|
||||
fmt.Sprintf(` #"%s",`, field.Name),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
namesContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
namesContent = gstr.Replace(namesContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(namesContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func getTplDaoIndexContent(tplDaoIndexPath string) string {
|
||||
if tplDaoIndexPath != "" {
|
||||
return gfile.GetContents(tplDaoIndexPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoIndexContent
|
||||
}
|
||||
|
||||
func getTplDaoInternalContent(tplDaoInternalPath string) string {
|
||||
if tplDaoInternalPath != "" {
|
||||
return gfile.GetContents(tplDaoInternalPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoInternalContent
|
||||
}
|
||||
|
||||
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
|
||||
func getJsonTagFromCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
j = 0
|
||||
result = make([]string, len(names))
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
78
cmd/gf/internal/cmd/cmd_gen_pb.go
Normal file
78
cmd/gf/internal/cmd/cmd_gen_pb.go
Normal file
@ -0,0 +1,78 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
)
|
||||
|
||||
func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
protoFolder := "protocol"
|
||||
if !gfile.Exists(protoFolder) {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder)
|
||||
}
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoFolder, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder)
|
||||
}
|
||||
dirSet := gset.NewStrSet()
|
||||
for _, file := range files {
|
||||
dirSet.Add(gfile.Dir(file))
|
||||
}
|
||||
var (
|
||||
servicePath = gfile.RealPath(".")
|
||||
goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH").String(), "src"))
|
||||
)
|
||||
dirSet.Iterator(func(protoDirPath string) bool {
|
||||
parsingCommand := fmt.Sprintf(
|
||||
"protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s",
|
||||
protoDirPath,
|
||||
servicePath,
|
||||
)
|
||||
if goPathSrc != "" {
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Custom replacement.
|
||||
//pbFolder := "protobuf"
|
||||
//_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string {
|
||||
// content := gfile.GetContents(path)
|
||||
// content = gstr.ReplaceByArray(content, g.SliceStr{
|
||||
// `gtime "gtime"`, `gtime "github.com/gogf/gf/v2/os/gtime"`,
|
||||
// })
|
||||
// _ = gfile.PutContents(path, content)
|
||||
// utils.GoFmt(path)
|
||||
// return path
|
||||
//})
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
409
cmd/gf/internal/cmd/cmd_gen_pbentity.go
Normal file
409
cmd/gf/internal/cmd/cmd_gen_pbentity.go
Normal file
@ -0,0 +1,409 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
cGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
cGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
cGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
cGenPbEntityBriefPath = `directory path for generated files`
|
||||
cGenPbEntityBriefPackage = `package name for all entity proto files`
|
||||
cGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
cGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenPbEntityBriefOption = `extra protobuf options`
|
||||
cGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
cGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
cGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenPbEntityConfig`: cGenPbEntityConfig,
|
||||
`cGenPbEntityBrief`: cGenPbEntityBrief,
|
||||
`cGenPbEntityEg`: cGenPbEntityEg,
|
||||
`cGenPbEntityAd`: cGenPbEntityAd,
|
||||
`cGenPbEntityBriefPath`: cGenPbEntityBriefPath,
|
||||
`cGenPbEntityBriefPackage`: cGenPbEntityBriefPackage,
|
||||
`cGenPbEntityBriefLink`: cGenPbEntityBriefLink,
|
||||
`cGenPbEntityBriefTables`: cGenPbEntityBriefTables,
|
||||
`cGenPbEntityBriefPrefix`: cGenPbEntityBriefPrefix,
|
||||
`cGenPbEntityBriefRemovePrefix`: cGenPbEntityBriefRemovePrefix,
|
||||
`cGenPbEntityBriefGroup`: cGenPbEntityBriefGroup,
|
||||
`cGenPbEntityBriefNameCase`: cGenPbEntityBriefNameCase,
|
||||
`cGenPbEntityBriefJsonCase`: cGenPbEntityBriefJsonCase,
|
||||
`cGenPbEntityBriefOption`: cGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c cGen) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, cGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in cGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Fatal("package name should not be empty")
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, db, cGenPbEntityInternalInput{
|
||||
cGenPbEntityInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, db gdb.DB, in cGenPbEntityInternalInput) {
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := "Entity_" + in.Prefix + in.NewTableName
|
||||
var (
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
)
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{PackageName}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in cGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in cGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
comment string
|
||||
jsonTagStr string
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "bytes"
|
||||
|
||||
case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint32"
|
||||
} else {
|
||||
typeName = "int32"
|
||||
}
|
||||
|
||||
case "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney":
|
||||
typeName = "double"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
typeName = "int64"
|
||||
|
||||
default:
|
||||
// Auto detecting type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "double"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "bytes"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
typeName = "int64"
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName)
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + typeName,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
101
cmd/gf/internal/cmd/cmd_init.go
Normal file
101
cmd/gf/internal/cmd/cmd_init.go
Normal file
@ -0,0 +1,101 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Init = cInit{}
|
||||
)
|
||||
|
||||
type cInit struct {
|
||||
g.Meta `name:"init" brief:"{cInitBrief}" eg:"{cInitEg}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cInitRepoPrefix = `github.com/gogf/`
|
||||
cInitMonoRepo = `template-mono`
|
||||
cInitSingleRepo = `template-single`
|
||||
cInitBrief = `create and initialize an empty GoFrame project`
|
||||
cInitEg = `
|
||||
gf init my-project
|
||||
gf init my-mono-repo -m
|
||||
`
|
||||
cInitNameBrief = `
|
||||
name for the project. It will create a folder with NAME in current directory.
|
||||
The NAME will also be the module name for the project.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cInitBrief`: cInitBrief,
|
||||
`cInitEg`: cInitEg,
|
||||
`cInitNameBrief`: cInitNameBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cInitInput struct {
|
||||
g.Meta `name:"init"`
|
||||
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
}
|
||||
type cInitOutput struct{}
|
||||
|
||||
func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err error) {
|
||||
if !gfile.IsEmpty(in.Name) && !allyes.Check() {
|
||||
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, in.Name)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
}
|
||||
mlog.Print("initializing...")
|
||||
|
||||
// Create project folder and files.
|
||||
var (
|
||||
templateRepoName string
|
||||
)
|
||||
if in.Mono {
|
||||
templateRepoName = cInitMonoRepo
|
||||
} else {
|
||||
templateRepoName = cInitSingleRepo
|
||||
}
|
||||
err = gres.Export(templateRepoName, in.Name, gres.ExportOption{
|
||||
RemovePrefix: templateRepoName,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Replace template name to project name.
|
||||
err = gfile.ReplaceDir(
|
||||
cInitRepoPrefix+templateRepoName,
|
||||
gfile.Basename(gfile.RealPath(in.Name)),
|
||||
in.Name,
|
||||
"*",
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mlog.Print("initialization done! ")
|
||||
if !in.Mono {
|
||||
enjoyCommand := `gf run main.go`
|
||||
if in.Name != "." {
|
||||
enjoyCommand = fmt.Sprintf(`cd %s && %s`, in.Name, enjoyCommand)
|
||||
}
|
||||
mlog.Printf(`you can now run "%s" to start your journey, enjoy!`, enjoyCommand)
|
||||
}
|
||||
return
|
||||
}
|
||||
26
cmd/gf/internal/cmd/cmd_install.go
Normal file
26
cmd/gf/internal/cmd/cmd_install.go
Normal file
@ -0,0 +1,26 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var (
|
||||
Install = cInstall{}
|
||||
)
|
||||
|
||||
type cInstall struct {
|
||||
g.Meta `name:"install" brief:"install gf binary to system (might need root/admin permission)"`
|
||||
}
|
||||
|
||||
type cInstallInput struct {
|
||||
g.Meta `name:"install"`
|
||||
}
|
||||
type cInstallOutput struct{}
|
||||
|
||||
func (c cInstall) Index(ctx context.Context, in cInstallInput) (out *cInstallOutput, err error) {
|
||||
err = service.Install.Run(ctx)
|
||||
return
|
||||
}
|
||||
89
cmd/gf/internal/cmd/cmd_pack.go
Normal file
89
cmd/gf/internal/cmd/cmd_pack.go
Normal file
@ -0,0 +1,89 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Pack = cPack{}
|
||||
)
|
||||
|
||||
type cPack struct {
|
||||
g.Meta `name:"pack" usage:"{cPackUsage}" brief:"{cPackBrief}" eg:"{cPackEg}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cPackUsage = `gf pack SRC DST`
|
||||
cPackBrief = `packing any file/directory to a resource file, or a go file`
|
||||
cPackEg = `
|
||||
gf pack public data.bin
|
||||
gf pack public,template data.bin
|
||||
gf pack public,template packed/data.go
|
||||
gf pack public,template,config packed/data.go
|
||||
gf pack public,template,config packed/data.go -n=packed -p=/var/www/my-app
|
||||
gf pack /var/www/public packed/data.go -n=packed
|
||||
`
|
||||
cPackSrcBrief = `source path for packing, which can be multiple source paths.`
|
||||
cPackDstBrief = `
|
||||
destination file path for packed file. if extension of the filename is ".go" and "-n" option is given,
|
||||
it enables packing SRC to go file, or else it packs SRC into a binary file.
|
||||
`
|
||||
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
|
||||
cPackPrefixBrief = `prefix for each file packed into the resource file`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cPackUsage`: cPackUsage,
|
||||
`cPackBrief`: cPackBrief,
|
||||
`cPackEg`: cPackEg,
|
||||
`cPackSrcBrief`: cPackSrcBrief,
|
||||
`cPackDstBrief`: cPackDstBrief,
|
||||
`cPackNameBrief`: cPackNameBrief,
|
||||
`cPackPrefixBrief`: cPackPrefixBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cPackInput struct {
|
||||
g.Meta `name:"pack"`
|
||||
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
|
||||
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
|
||||
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
|
||||
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
|
||||
}
|
||||
type cPackOutput struct{}
|
||||
|
||||
func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err error) {
|
||||
if gfile.Exists(in.Dst) && gfile.IsDir(in.Dst) {
|
||||
mlog.Fatalf("DST path '%s' cannot be a directory", in.Dst)
|
||||
}
|
||||
if !gfile.IsEmpty(in.Dst) && !allyes.Check() {
|
||||
s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", in.Dst)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
}
|
||||
if in.Name == "" && gfile.ExtName(in.Dst) == "go" {
|
||||
in.Name = gfile.Basename(gfile.Dir(in.Dst))
|
||||
}
|
||||
if in.Name != "" {
|
||||
if err = gres.PackToGoFile(in.Src, in.Dst, in.Name, in.Prefix); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err = gres.PackToFile(in.Src, in.Dst, in.Prefix); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
161
cmd/gf/internal/cmd/cmd_run.go
Normal file
161
cmd/gf/internal/cmd/cmd_run.go
Normal file
@ -0,0 +1,161 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gfsnotify"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Run = cRun{}
|
||||
)
|
||||
|
||||
type cRun struct {
|
||||
g.Meta `name:"run" usage:"{cRunUsage}" brief:"{cRunBrief}" eg:"{cRunEg}" dc:"{cRunDc}"`
|
||||
}
|
||||
|
||||
type cRunApp struct {
|
||||
File string // Go run file name.
|
||||
Path string // Directory storing built binary.
|
||||
Options string // Extra "go run" options.
|
||||
Args string // Custom arguments.
|
||||
}
|
||||
|
||||
const (
|
||||
cRunUsage = `gf run FILE [OPTION]`
|
||||
cRunBrief = `running go codes with hot-compiled-like feature`
|
||||
cRunEg = `
|
||||
gf run main.go
|
||||
gf run main.go --args "server -p 8080"
|
||||
gf run main.go -mod=vendor
|
||||
`
|
||||
cRunDc = `
|
||||
The "run" command is used for running go codes with hot-compiled-like feature,
|
||||
which compiles and runs the go codes asynchronously when codes change.
|
||||
`
|
||||
cRunFileBrief = `building file path.`
|
||||
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
|
||||
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
|
||||
)
|
||||
|
||||
var (
|
||||
process *gproc.Process
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cRunUsage`: cRunUsage,
|
||||
`cRunBrief`: cRunBrief,
|
||||
`cRunEg`: cRunEg,
|
||||
`cRunDc`: cRunDc,
|
||||
`cRunFileBrief`: cRunFileBrief,
|
||||
`cRunPathBrief`: cRunPathBrief,
|
||||
`cRunExtraBrief`: cRunExtraBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cRunInput struct {
|
||||
g.Meta `name:"run"`
|
||||
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
|
||||
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
|
||||
}
|
||||
cRunOutput struct{}
|
||||
)
|
||||
|
||||
func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
app := &cRunApp{
|
||||
File: in.File,
|
||||
Path: in.Path,
|
||||
Options: in.Extra,
|
||||
}
|
||||
dirty := gtype.NewBool()
|
||||
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
|
||||
if gfile.ExtName(event.Path) != "go" {
|
||||
return
|
||||
}
|
||||
// Variable `dirty` is used for running the changes only one in one second.
|
||||
if !dirty.Cas(false, true) {
|
||||
return
|
||||
}
|
||||
// With some delay in case of multiple code changes in very short interval.
|
||||
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
|
||||
defer dirty.Set(false)
|
||||
mlog.Printf(`go file changes: %s`, event.String())
|
||||
app.Run()
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
go app.Run()
|
||||
select {}
|
||||
}
|
||||
|
||||
func (app *cRunApp) Run() {
|
||||
// Rebuild and run the codes.
|
||||
renamePath := ""
|
||||
mlog.Printf("build: %s", app.File)
|
||||
outputPath := gfile.Join(app.Path, gfile.Name(app.File))
|
||||
if runtime.GOOS == "windows" {
|
||||
outputPath += ".exe"
|
||||
if gfile.Exists(outputPath) {
|
||||
renamePath = outputPath + "~"
|
||||
if err := gfile.Rename(outputPath, renamePath); err != nil {
|
||||
mlog.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case of `pipe: too many open files` error.
|
||||
// Build the app.
|
||||
buildCommand := fmt.Sprintf(
|
||||
`go build -o %s %s %s`,
|
||||
outputPath,
|
||||
app.Options,
|
||||
app.File,
|
||||
)
|
||||
mlog.Print(buildCommand)
|
||||
result, err := gproc.ShellExec(buildCommand)
|
||||
if err != nil {
|
||||
mlog.Printf("build error: \n%s%s", result, err.Error())
|
||||
return
|
||||
}
|
||||
// Kill the old process if build successfully.
|
||||
if process != nil {
|
||||
if err := process.Kill(); err != nil {
|
||||
mlog.Debugf("kill process error: %s", err.Error())
|
||||
//return
|
||||
}
|
||||
}
|
||||
// Run the binary file.
|
||||
runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args)
|
||||
mlog.Print(runCommand)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Special handling for windows platform.
|
||||
// DO NOT USE "cmd /c" command.
|
||||
process = gproc.NewProcess(runCommand, nil)
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(runCommand, nil)
|
||||
}
|
||||
if pid, err := process.Start(); err != nil {
|
||||
mlog.Printf("build running error: %s", err.Error())
|
||||
} else {
|
||||
mlog.Printf("build running pid: %d", pid)
|
||||
}
|
||||
}
|
||||
167
cmd/gf/internal/cmd/cmd_tpl.go
Normal file
167
cmd/gf/internal/cmd/cmd_tpl.go
Normal file
@ -0,0 +1,167 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
var (
|
||||
Tpl = cTpl{}
|
||||
)
|
||||
|
||||
type cTpl struct {
|
||||
g.Meta `name:"tpl" brief:"{cTplBrief}" dc:"{cTplDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cTplBrief = `template parsing and building commands`
|
||||
cTplDc = `
|
||||
The "tpl" command is used for template parsing and building purpose.
|
||||
It can parse either template file or folder with multiple types of values support,
|
||||
like json/xml/yaml/toml/ini.
|
||||
`
|
||||
cTplParseBrief = `parse either template file or folder with multiple types of values`
|
||||
cTplParseEg = `
|
||||
gf tpl parse -p ./template -v values.json -r
|
||||
gf tpl parse -p ./template -v values.json -n *.tpl -r
|
||||
gf tpl parse -p ./template -v values.json -d '${,}}' -r
|
||||
gf tpl parse -p ./template -v values.json -o ./template.parsed
|
||||
`
|
||||
cTplSupportValuesFilePattern = `*.json,*.xml,*.yaml,*.yml,*.toml,*.ini`
|
||||
)
|
||||
|
||||
type (
|
||||
cTplParseInput struct {
|
||||
g.Meta `name:"parse" brief:"{cTplParseBrief}" eg:"{cTplParseEg}"`
|
||||
Path string `name:"path" short:"p" brief:"template file or folder path" v:"required"`
|
||||
Pattern string `name:"pattern" short:"n" brief:"template file pattern when path is a folder, default is:*" d:"*"`
|
||||
Recursive bool `name:"recursive" short:"c" brief:"recursively parsing files if path is folder, default is:true" d:"true"`
|
||||
Values string `name:"values" short:"v" brief:"template values file/folder, support file types like: json/xml/yaml/toml/ini" v:"required"`
|
||||
Output string `name:"output" short:"o" brief:"output file/folder path"`
|
||||
Delimiters string `name:"delimiters" short:"d" brief:"delimiters for template content parsing, default is:{{,}}" d:"{{,}}"`
|
||||
Replace bool `name:"replace" short:"r" brief:"replace original files" orphan:"true"`
|
||||
}
|
||||
cTplParseOutput struct{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cTplBrief`: cTplBrief,
|
||||
`cTplDc`: cTplDc,
|
||||
`cTplParseEg`: cTplParseEg,
|
||||
`cTplParseBrief`: cTplParseBrief,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *cTpl) Parse(ctx context.Context, in cTplParseInput) (out *cTplParseOutput, err error) {
|
||||
if in.Output == "" && in.Replace == false {
|
||||
return nil, gerror.New(`parameter output and replace should not be both empty`)
|
||||
}
|
||||
delimiters := gstr.SplitAndTrim(in.Delimiters, ",")
|
||||
mlog.Debugf("delimiters input:%s, parsed:%#v", in.Delimiters, delimiters)
|
||||
if len(delimiters) != 2 {
|
||||
return nil, gerror.Newf(`invalid delimiters: %s`, in.Delimiters)
|
||||
}
|
||||
g.View().SetDelimiters(delimiters[0], delimiters[1])
|
||||
valuesMap, err := c.loadValues(ctx, in.Values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(valuesMap) == 0 {
|
||||
return nil, gerror.Newf(`empty values loaded from values file/folder "%s"`, in.Values)
|
||||
}
|
||||
err = c.parsePath(ctx, valuesMap, in)
|
||||
if err == nil {
|
||||
mlog.Print("done!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cTpl) parsePath(ctx context.Context, values g.Map, in cTplParseInput) (err error) {
|
||||
if !gfile.Exists(in.Path) {
|
||||
return gerror.Newf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
var (
|
||||
path string
|
||||
files []string
|
||||
relativePath string
|
||||
outputPath string
|
||||
)
|
||||
path = gfile.RealPath(in.Path)
|
||||
if gfile.IsDir(path) {
|
||||
files, err = gfile.ScanDirFile(path, in.Pattern, in.Recursive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
relativePath = gstr.Replace(file, path, "")
|
||||
if in.Output != "" {
|
||||
outputPath = gfile.Join(in.Output, relativePath)
|
||||
}
|
||||
if err = c.parseFile(ctx, file, outputPath, values, in); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if in.Output != "" {
|
||||
outputPath = in.Output
|
||||
}
|
||||
err = c.parseFile(ctx, path, outputPath, values, in)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cTpl) parseFile(ctx context.Context, file string, output string, values g.Map, in cTplParseInput) (err error) {
|
||||
output = gstr.ReplaceByMap(output, g.MapStrStr{
|
||||
`\\`: `\`,
|
||||
`//`: `/`,
|
||||
})
|
||||
content, err := g.View().Parse(ctx, file, values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if output != "" {
|
||||
mlog.Printf(`parse file "%s" to "%s"`, file, output)
|
||||
return gfile.PutContents(output, content)
|
||||
}
|
||||
if in.Replace {
|
||||
mlog.Printf(`parse and replace file "%s"`, file)
|
||||
return gfile.PutContents(file, content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cTpl) loadValues(ctx context.Context, valuesPath string) (data g.Map, err error) {
|
||||
if !gfile.Exists(valuesPath) {
|
||||
return nil, gerror.Newf(`values file/folder "%s" does not exist`, valuesPath)
|
||||
}
|
||||
var j *gjson.Json
|
||||
if gfile.IsDir(valuesPath) {
|
||||
var valueFiles []string
|
||||
valueFiles, err = gfile.ScanDirFile(valuesPath, cTplSupportValuesFilePattern, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = make(g.Map)
|
||||
for _, file := range valueFiles {
|
||||
if j, err = gjson.Load(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gutil.MapMerge(data, j.Map())
|
||||
}
|
||||
return
|
||||
}
|
||||
if j, err = gjson.Load(valuesPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = j.Map()
|
||||
return
|
||||
}
|
||||
87
cmd/gf/internal/cmd/cmd_version.go
Normal file
87
cmd/gf/internal/cmd/cmd_version.go
Normal file
@ -0,0 +1,87 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
var (
|
||||
Version = cVersion{}
|
||||
)
|
||||
|
||||
type cVersion struct {
|
||||
g.Meta `name:"version" brief:"show version information of current binary"`
|
||||
}
|
||||
|
||||
type cVersionInput struct {
|
||||
g.Meta `name:"version"`
|
||||
}
|
||||
type cVersionOutput struct{}
|
||||
|
||||
func (c cVersion) Index(ctx context.Context, in cVersionInput) (*cVersionOutput, error) {
|
||||
info := gbuild.Info()
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION)
|
||||
gfVersion, err := c.getGFVersionOfCurrentProject()
|
||||
if err != nil {
|
||||
gfVersion = err.Error()
|
||||
} else {
|
||||
gfVersion = gfVersion + " in current go.mod"
|
||||
}
|
||||
mlog.Printf(`GoFrame Version: %s`, gfVersion)
|
||||
mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
|
||||
if info.GoFrame == "" {
|
||||
mlog.Print(`Current is a custom installed version, no installation information.`)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mlog.Print(gstr.Trim(fmt.Sprintf(`
|
||||
CLI Built Detail:
|
||||
Go Version: %s
|
||||
GF Version: %s
|
||||
Git Commit: %s
|
||||
Build Time: %s
|
||||
`, info.Golang, info.GoFrame, info.Git, info.Time)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using.
|
||||
func (c cVersion) getGFVersionOfCurrentProject() (string, error) {
|
||||
goModPath := gfile.Join(gfile.Pwd(), "go.mod")
|
||||
if gfile.Exists(goModPath) {
|
||||
lines := gstr.SplitAndTrim(gfile.GetContents(goModPath), "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
// Version 1.
|
||||
match, err := gregex.MatchString(`^github\.com/gogf/gf\s+(.+)$`, line)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(match) <= 1 {
|
||||
// Version > 1.
|
||||
match, err = gregex.MatchString(`^github\.com/gogf/gf/v\d\s+(.+)$`, line)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(match) > 1 {
|
||||
return gstr.Trim(match[1]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", gerror.New("cannot find goframe requirement in go.mod")
|
||||
} else {
|
||||
return "", gerror.New("cannot find go.mod")
|
||||
}
|
||||
}
|
||||
1
cmd/gf/internal/consts/consts.go
Normal file
1
cmd/gf/internal/consts/consts.go
Normal file
@ -0,0 +1 @@
|
||||
package consts
|
||||
104
cmd/gf/internal/consts/consts_gen_dao_template_dao.go
Normal file
104
cmd/gf/internal/consts/consts_gen_dao_template_dao.go
Normal file
@ -0,0 +1,104 @@
|
||||
package consts
|
||||
|
||||
const TemplateDaoDaoIndexContent = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"{TplImportPrefix}/internal"
|
||||
)
|
||||
|
||||
// {TplTableNameCamelLowerCase}Dao is the data access object for table {TplTableName}.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type {TplTableNameCamelLowerCase}Dao struct {
|
||||
*internal.{TplTableNameCamelCase}Dao
|
||||
}
|
||||
|
||||
var (
|
||||
// {TplTableNameCamelCase} is globally public accessible object for table {TplTableName} operations.
|
||||
{TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{
|
||||
internal.New{TplTableNameCamelCase}Dao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
||||
|
||||
`
|
||||
|
||||
const TemplateDaoDaoInternalContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// {TplTableNameCamelCase}Dao is the data access object for table {TplTableName}.
|
||||
type {TplTableNameCamelCase}Dao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns {TplTableNameCamelCase}Columns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// {TplTableNameCamelCase}Columns defines and stores column names for table {TplTableName}.
|
||||
type {TplTableNameCamelCase}Columns struct {
|
||||
{TplColumnDefine}
|
||||
}
|
||||
|
||||
// {TplTableNameCamelLowerCase}Columns holds the columns for table {TplTableName}.
|
||||
var {TplTableNameCamelLowerCase}Columns = {TplTableNameCamelCase}Columns{
|
||||
{TplColumnNames}
|
||||
}
|
||||
|
||||
// New{TplTableNameCamelCase}Dao creates and returns a new DAO object for table data access.
|
||||
func New{TplTableNameCamelCase}Dao() *{TplTableNameCamelCase}Dao {
|
||||
return &{TplTableNameCamelCase}Dao{
|
||||
group: "{TplGroupName}",
|
||||
table: "{TplTableName}",
|
||||
columns: {TplTableNameCamelLowerCase}Columns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *{TplTableNameCamelCase}Dao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Columns() {TplTableNameCamelCase}Columns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function f
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
`
|
||||
14
cmd/gf/internal/consts/consts_gen_dao_template_do.go
Normal file
14
cmd/gf/internal/consts/consts_gen_dao_template_do.go
Normal file
@ -0,0 +1,14 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoDoContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
{TplPackageImports}
|
||||
|
||||
// {TplTableNameCamelCase} is the golang structure of table {TplTableName} for DAO operations like Where/Data.
|
||||
{TplStructDefine}
|
||||
`
|
||||
14
cmd/gf/internal/consts/consts_gen_dao_template_entity.go
Normal file
14
cmd/gf/internal/consts/consts_gen_dao_template_entity.go
Normal file
@ -0,0 +1,14 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoEntityContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
{TplPackageImports}
|
||||
|
||||
// {TplTableNameCamelCase} is the golang structure for table {TplTableName}.
|
||||
{TplStructDefine}
|
||||
`
|
||||
17
cmd/gf/internal/consts/consts_gen_pbentity_template.go
Normal file
17
cmd/gf/internal/consts/consts_gen_pbentity_template.go
Normal file
@ -0,0 +1,17 @@
|
||||
package consts
|
||||
|
||||
const TemplatePbEntityMessageContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package {PackageName};
|
||||
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
{OptionContent}
|
||||
|
||||
{EntityMessage}
|
||||
`
|
||||
1
cmd/gf/internal/packed/packed.go
Normal file
1
cmd/gf/internal/packed/packed.go
Normal file
@ -0,0 +1 @@
|
||||
package packed
|
||||
9
cmd/gf/internal/packed/template-mono.go
Normal file
9
cmd/gf/internal/packed/template-mono.go
Normal file
File diff suppressed because one or more lines are too long
9
cmd/gf/internal/packed/template-single.go
Normal file
9
cmd/gf/internal/packed/template-single.go
Normal file
File diff suppressed because one or more lines are too long
235
cmd/gf/internal/service/install.go
Normal file
235
cmd/gf/internal/service/install.go
Normal file
@ -0,0 +1,235 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var (
|
||||
Install = serviceInstall{}
|
||||
)
|
||||
|
||||
type serviceInstall struct{}
|
||||
|
||||
type serviceInstallAvailablePath struct {
|
||||
dirPath string
|
||||
filePath string
|
||||
writable bool
|
||||
installed bool
|
||||
}
|
||||
|
||||
func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
// Ask where to install.
|
||||
paths := s.getAvailablePaths()
|
||||
if len(paths) <= 0 {
|
||||
mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.")
|
||||
return
|
||||
}
|
||||
mlog.Printf("I found some installable paths for you(from $PATH): ")
|
||||
mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path")
|
||||
|
||||
// Print all paths status and determine the default selectedID value.
|
||||
var (
|
||||
selectedID = -1
|
||||
newPaths []serviceInstallAvailablePath
|
||||
pathSet = gset.NewStrSet() // Used for repeated items filtering.
|
||||
)
|
||||
for _, path := range paths {
|
||||
if !pathSet.AddIfNotExist(path.dirPath) {
|
||||
continue
|
||||
}
|
||||
newPaths = append(newPaths, path)
|
||||
}
|
||||
paths = newPaths
|
||||
for id, path := range paths {
|
||||
mlog.Printf(" %2d | %8t | %9t | %s", id, path.writable, path.installed, path.dirPath)
|
||||
if selectedID == -1 {
|
||||
// Use the previously installed path as the most priority choice.
|
||||
if path.installed {
|
||||
selectedID = id
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's no previously installed path, use the first writable path.
|
||||
if selectedID == -1 {
|
||||
// Order by choosing priority.
|
||||
commonPaths := garray.NewStrArrayFrom(g.SliceStr{
|
||||
s.getGoPathBin(),
|
||||
`/usr/local/bin`,
|
||||
`/usr/bin`,
|
||||
`/usr/sbin`,
|
||||
`C:\Windows`,
|
||||
`C:\Windows\system32`,
|
||||
`C:\Go\bin`,
|
||||
`C:\Program Files`,
|
||||
`C:\Program Files (x86)`,
|
||||
})
|
||||
// Check the common installation directories.
|
||||
commonPaths.Iterator(func(k int, v string) bool {
|
||||
for id, aPath := range paths {
|
||||
if strings.EqualFold(aPath.dirPath, v) {
|
||||
selectedID = id
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if selectedID == -1 {
|
||||
selectedID = 0
|
||||
}
|
||||
}
|
||||
|
||||
if allyes.Check() {
|
||||
// Use the default selectedID.
|
||||
mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID)
|
||||
} else {
|
||||
for {
|
||||
// Get input and update selectedID.
|
||||
var (
|
||||
inputID int
|
||||
input = gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID)
|
||||
)
|
||||
if input != "" {
|
||||
inputID = gconv.Int(input)
|
||||
}
|
||||
// Check if out of range.
|
||||
if inputID >= len(paths) || inputID < 0 {
|
||||
mlog.Printf("invalid install destination Id: %d", inputID)
|
||||
continue
|
||||
}
|
||||
selectedID = inputID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get selected destination path.
|
||||
dstPath := paths[selectedID]
|
||||
|
||||
// Install the new binary.
|
||||
err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
|
||||
if err != nil {
|
||||
mlog.Printf("install gf binary to '%s' failed: %v", dstPath.dirPath, err)
|
||||
mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.dirPath)
|
||||
} else {
|
||||
mlog.Printf("gf binary is successfully installed to: %s", dstPath.dirPath)
|
||||
}
|
||||
|
||||
// Uninstall the old binary.
|
||||
for _, path := range paths {
|
||||
// Do not delete myself.
|
||||
if path.filePath != "" && path.filePath != dstPath.filePath && gfile.SelfPath() != path.filePath {
|
||||
_ = gfile.Remove(path.filePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsInstalled checks and returns whether the binary is installed.
|
||||
func (s serviceInstall) IsInstalled() bool {
|
||||
paths := s.getAvailablePaths()
|
||||
for _, aPath := range paths {
|
||||
if aPath.installed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getGoPathBinFilePath retrieves ad returns the GOPATH/bin path for binary.
|
||||
func (s serviceInstall) getGoPathBin() string {
|
||||
if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
|
||||
return gfile.Join(goPath, "bin")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getAvailablePaths returns the installation paths data for the binary.
|
||||
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
|
||||
var (
|
||||
folderPaths []serviceInstallAvailablePath
|
||||
binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
|
||||
)
|
||||
// $GOPATH/bin
|
||||
if goPathBin := s.getGoPathBin(); goPathBin != "" {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, goPathBin, binaryFileName,
|
||||
)
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
darwinInstallationCheckPaths := []string{"/usr/local/bin"}
|
||||
for _, v := range darwinInstallationCheckPaths {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
// Search and find the writable directory path.
|
||||
envPath := genv.Get("PATH", genv.Get("Path").String()).String()
|
||||
if gstr.Contains(envPath, ";") {
|
||||
// windows.
|
||||
for _, v := range gstr.SplitAndTrim(envPath, ";") {
|
||||
if v == "." {
|
||||
continue
|
||||
}
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
} else if gstr.Contains(envPath, ":") {
|
||||
// *nix.
|
||||
for _, v := range gstr.SplitAndTrim(envPath, ":") {
|
||||
if v == "." {
|
||||
continue
|
||||
}
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
} else if envPath != "" {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, envPath, binaryFileName,
|
||||
)
|
||||
} else {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, "/usr/local/bin", binaryFileName,
|
||||
)
|
||||
}
|
||||
}
|
||||
return folderPaths
|
||||
}
|
||||
|
||||
// checkAndAppendToAvailablePath checks if `path` is writable and already installed.
|
||||
// It adds the `path` to `folderPaths` if it is writable or already installed, or else it ignores the `path`.
|
||||
func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInstallAvailablePath, dirPath string, binaryFileName string) []serviceInstallAvailablePath {
|
||||
var (
|
||||
filePath = gfile.Join(dirPath, binaryFileName)
|
||||
writable = gfile.IsWritable(dirPath)
|
||||
installed = gfile.Exists(filePath)
|
||||
)
|
||||
if !writable && !installed {
|
||||
return folderPaths
|
||||
}
|
||||
return append(
|
||||
folderPaths,
|
||||
serviceInstallAvailablePath{
|
||||
dirPath: dirPath,
|
||||
writable: writable,
|
||||
filePath: filePath,
|
||||
installed: installed,
|
||||
})
|
||||
}
|
||||
22
cmd/gf/internal/utility/allyes/allyes.go
Normal file
22
cmd/gf/internal/utility/allyes/allyes.go
Normal file
@ -0,0 +1,22 @@
|
||||
package allyes
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
)
|
||||
|
||||
const (
|
||||
EnvName = "GF_CLI_ALL_YES"
|
||||
)
|
||||
|
||||
// Init initializes the package manually.
|
||||
func Init() {
|
||||
if gcmd.GetOpt("y") != nil {
|
||||
genv.MustSet(EnvName, "1")
|
||||
}
|
||||
}
|
||||
|
||||
// Check checks whether option allow all yes for command.
|
||||
func Check() bool {
|
||||
return genv.Get(EnvName).String() == "1"
|
||||
}
|
||||
66
cmd/gf/internal/utility/mlog/mlog.go
Normal file
66
cmd/gf/internal/utility/mlog/mlog.go
Normal file
@ -0,0 +1,66 @@
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
headerPrintEnvName = "GF_CLI_MLOG_HEADER"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.TODO()
|
||||
logger = glog.New()
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger.SetStack(false)
|
||||
if genv.Get(headerPrintEnvName).String() == "1" {
|
||||
logger.SetHeaderPrint(true)
|
||||
} else {
|
||||
logger.SetHeaderPrint(false)
|
||||
}
|
||||
if gcmd.GetOpt("debug") != nil || gcmd.GetOpt("gf.debug") != nil {
|
||||
logger.SetDebug(true)
|
||||
} else {
|
||||
logger.SetDebug(false)
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeaderPrint enables/disables header printing to stdout.
|
||||
func SetHeaderPrint(enabled bool) {
|
||||
logger.SetHeaderPrint(enabled)
|
||||
if enabled {
|
||||
_ = genv.Set(headerPrintEnvName, "1")
|
||||
} else {
|
||||
_ = genv.Set(headerPrintEnvName, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func Print(v ...interface{}) {
|
||||
logger.Print(ctx, v...)
|
||||
}
|
||||
|
||||
func Printf(format string, v ...interface{}) {
|
||||
logger.Printf(ctx, format, v...)
|
||||
}
|
||||
|
||||
func Fatal(v ...interface{}) {
|
||||
logger.Fatal(ctx, v...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, v ...interface{}) {
|
||||
logger.Fatalf(ctx, format, v...)
|
||||
}
|
||||
|
||||
func Debug(v ...interface{}) {
|
||||
logger.Debug(ctx, v...)
|
||||
}
|
||||
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
logger.Debugf(ctx, format, v...)
|
||||
}
|
||||
18
cmd/gf/internal/utility/utils/utils.go
Normal file
18
cmd/gf/internal/utility/utils/utils.go
Normal file
@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
)
|
||||
|
||||
var (
|
||||
// gofmtPath is the binary path of command `gofmt`.
|
||||
gofmtPath = gproc.SearchBinaryPath("gofmt")
|
||||
)
|
||||
|
||||
// GoFmt formats the source file using command `gofmt -w -s PATH`.
|
||||
func GoFmt(path string) {
|
||||
if gofmtPath != "" {
|
||||
gproc.ShellExec(fmt.Sprintf(`%s -w -s %s`, gofmtPath, path))
|
||||
}
|
||||
}
|
||||
86
cmd/gf/main.go
Normal file
86
cmd/gf/main.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/cmd/gf/v2/internal/packed"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
const (
|
||||
cliFolderName = `hack`
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if err, ok := exception.(error); ok {
|
||||
mlog.Print(err.Error())
|
||||
} else {
|
||||
panic(exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// CLI configuration.
|
||||
if path, _ := gfile.Search(cliFolderName); path != "" {
|
||||
if adapter, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile); ok {
|
||||
if err := adapter.SetPath(path); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
handleZshAlias()
|
||||
|
||||
// -y option checks.
|
||||
allyes.Init()
|
||||
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
)
|
||||
command, err := gcmd.NewFromObject(cmd.GF)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = command.AddObject(
|
||||
cmd.Env,
|
||||
cmd.Run,
|
||||
cmd.Gen,
|
||||
cmd.Tpl,
|
||||
cmd.Init,
|
||||
cmd.Pack,
|
||||
cmd.Build,
|
||||
cmd.Docker,
|
||||
cmd.Install,
|
||||
cmd.Version,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
command.Run(ctx)
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
func handleZshAlias() {
|
||||
if home, err := gfile.Home(); err == nil {
|
||||
zshPath := gfile.Join(home, ".zshrc")
|
||||
if gfile.Exists(zshPath) {
|
||||
var (
|
||||
aliasCommand = `alias gf=gf`
|
||||
content = gfile.GetContents(zshPath)
|
||||
)
|
||||
if !gstr.Contains(content, aliasCommand) {
|
||||
_ = gfile.PutContentsAppend(zshPath, "\n"+aliasCommand+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
cmd/gf/test/testdata/tpls/tpl1.yaml
vendored
Normal file
2
cmd/gf/test/testdata/tpls/tpl1.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
server:
|
||||
address: {{.server.address}}
|
||||
1
cmd/gf/test/testdata/tpls/tpl2.sql
vendored
Normal file
1
cmd/gf/test/testdata/tpls/tpl2.sql
vendored
Normal file
@ -0,0 +1 @@
|
||||
insert into {{.sql.table}}
|
||||
8
cmd/gf/test/testdata/values.json
vendored
Normal file
8
cmd/gf/test/testdata/values.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"server": {
|
||||
"address": "https://goframe.org"
|
||||
},
|
||||
"sql":{
|
||||
"table": "table_name"
|
||||
}
|
||||
}
|
||||
8
container/garray/garray.go
Normal file
8
container/garray/garray.go
Normal file
@ -0,0 +1,8 @@
|
||||
// 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 most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
69
container/garray/garray_func.go
Normal file
69
container/garray/garray_func.go
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import "strings"
|
||||
|
||||
// defaultComparatorInt for int comparison.
|
||||
func defaultComparatorInt(a, b int) int {
|
||||
if a < b {
|
||||
return -1
|
||||
}
|
||||
if a > b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// defaultComparatorStr for string comparison.
|
||||
func defaultComparatorStr(a, b string) int {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
// quickSortInt is the quick-sorting algorithm implements for int.
|
||||
func quickSortInt(values []int, comparator func(a, b int) int) {
|
||||
if len(values) <= 1 {
|
||||
return
|
||||
}
|
||||
mid, i := values[0], 1
|
||||
head, tail := 0, len(values)-1
|
||||
for head < tail {
|
||||
if comparator(values[i], mid) > 0 {
|
||||
values[i], values[tail] = values[tail], values[i]
|
||||
tail--
|
||||
} else {
|
||||
values[i], values[head] = values[head], values[i]
|
||||
head++
|
||||
i++
|
||||
}
|
||||
}
|
||||
values[head] = mid
|
||||
quickSortInt(values[:head], comparator)
|
||||
quickSortInt(values[head+1:], comparator)
|
||||
}
|
||||
|
||||
// quickSortStr is the quick-sorting algorithm implements for string.
|
||||
func quickSortStr(values []string, comparator func(a, b string) int) {
|
||||
if len(values) <= 1 {
|
||||
return
|
||||
}
|
||||
mid, i := values[0], 1
|
||||
head, tail := 0, len(values)-1
|
||||
for head < tail {
|
||||
if comparator(values[i], mid) > 0 {
|
||||
values[i], values[tail] = values[tail], values[i]
|
||||
tail--
|
||||
} else {
|
||||
values[i], values[head] = values[head], values[i]
|
||||
head++
|
||||
i++
|
||||
}
|
||||
}
|
||||
values[head] = mid
|
||||
quickSortStr(values[:head], comparator)
|
||||
quickSortStr(values[head+1:], comparator)
|
||||
}
|
||||
810
container/garray/garray_normal_any.go
Normal file
810
container/garray/garray_normal_any.go
Normal file
@ -0,0 +1,810 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// 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
|
||||
array []interface{}
|
||||
}
|
||||
|
||||
// New creates and returns an empty array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(safe ...bool) *Array {
|
||||
return NewArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewArray is alias of New, please see New.
|
||||
func NewArray(safe ...bool) *Array {
|
||||
return NewArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewArraySize create and returns an array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// NewArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewArrayRange(start, end, step int, safe ...bool) *Array {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]interface{}, (end-start+1)/step)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
index++
|
||||
}
|
||||
return NewArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewFrom is alias of NewArrayFrom.
|
||||
// See NewArrayFrom.
|
||||
func NewFrom(array []interface{}, safe ...bool) *Array {
|
||||
return NewArrayFrom(array, safe...)
|
||||
}
|
||||
|
||||
// NewFromCopy is alias of NewArrayFromCopy.
|
||||
// See NewArrayFromCopy.
|
||||
func NewFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
return NewArrayFromCopy(array, safe...)
|
||||
}
|
||||
|
||||
// NewArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `nil`.
|
||||
func (a *Array) At(index int) (value interface{}) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *Array) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
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{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *Array) SetArray(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *Array) Replace(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *Array) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
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 gerror.NewCodef(gcode.CodeInvalidParameter, "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, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *Array) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *Array) PushLeft(value ...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *Array) PushRight(value ...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *Array) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// 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()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
func (a *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 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *Array) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]interface{}, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *Array) SubSlice(offset int, length ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Append is alias of PushRight, please See PushRight.
|
||||
func (a *Array) Append(value ...interface{}) *Array {
|
||||
a.PushRight(value...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *Array) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *Array) Slice() []interface{} {
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
return array
|
||||
} else {
|
||||
return a.array
|
||||
}
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *Array) Interfaces() []interface{} {
|
||||
return a.Slice()
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *Array) Clone() (newArray *Array) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *Array) Clear() *Array {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *Array) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
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 {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
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++
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
return a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value `value`,
|
||||
// keys starting at the `startIndex` parameter.
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *Array) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]interface{}
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]interface{}, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = val
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *Array) Rand() (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// 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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *Array) Shuffle() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *Array) Reverse() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
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))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *Array) CountValues() map[interface{}]int {
|
||||
m := make(map[interface{}]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *Array) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
if a.array == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterNil removes all nil value of the array.
|
||||
func (a *Array) FilterNil() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty value of the array.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (a *Array) FilterEmpty() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
789
container/garray/garray_normal_int.go
Normal file
789
container/garray/garray_normal_int.go
Normal file
@ -0,0 +1,789 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// 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
|
||||
array []int
|
||||
}
|
||||
|
||||
// NewIntArray creates and returns an empty array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArray(safe ...bool) *IntArray {
|
||||
return NewIntArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewIntArraySize create and returns an array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]int, (end-start+1)/step)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
index++
|
||||
}
|
||||
return NewIntArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewIntArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `0`.
|
||||
func (a *IntArray) At(index int) (value int) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *IntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *IntArray) Set(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *IntArray) Replace(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *IntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing 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()
|
||||
if len(reverse) > 0 && reverse[0] {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.array[i] >= a.array[j]
|
||||
})
|
||||
} else {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]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 gerror.NewCodef(gcode.CodeInvalidParameter, "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, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *IntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *IntArray) PushLeft(value ...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *IntArray) PushRight(value ...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *IntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of 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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 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()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *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 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *IntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]int, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *IntArray) SubSlice(offset int, length ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Append is alias of PushRight,please See PushRight.
|
||||
func (a *IntArray) Append(value ...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *IntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *IntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *IntArray) Interfaces() []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *IntArray) Clear() *IntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *IntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
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); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
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) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *IntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]int
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = value
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *IntArray) Rand() (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// 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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *IntArray) Shuffle() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *IntArray) Reverse() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
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))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *IntArray) CountValues() map[int]int {
|
||||
m := make(map[int]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *IntArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// 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)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.array == nil {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEmpty removes all zero value of the array.
|
||||
func (a *IntArray) FilterEmpty() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
802
container/garray/garray_normal_str.go
Normal file
802
container/garray/garray_normal_str.go
Normal file
@ -0,0 +1,802 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// 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
|
||||
array []string
|
||||
}
|
||||
|
||||
// NewStrArray creates and returns an empty array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArray(safe ...bool) *StrArray {
|
||||
return NewStrArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewStrArraySize create and returns an array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &StrArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns an empty string.
|
||||
func (a *StrArray) At(index int) (value string) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *StrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *StrArray) Set(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *StrArray) SetArray(array []string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *StrArray) Replace(array []string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *StrArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
func (a *StrArray) Sort(reverse ...bool) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(reverse) > 0 && reverse[0] {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return strings.Compare(a.array[i], a.array[j]) >= 0
|
||||
})
|
||||
} else {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]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 gerror.NewCodef(gcode.CodeInvalidParameter, "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, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *StrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *StrArray) PushLeft(value ...string) *StrArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *StrArray) PushRight(value ...string) *StrArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *StrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *StrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of 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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 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()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *StrArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]string, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *StrArray) SubSlice(offset int, length ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
}
|
||||
return a.array[offset:end]
|
||||
}
|
||||
|
||||
// Append is alias of PushRight,please See PushRight.
|
||||
func (a *StrArray) Append(value ...string) *StrArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *StrArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *StrArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *StrArray) Interfaces() []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *StrArray) Clone() (newArray *StrArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *StrArray) Clear() *StrArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
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
|
||||
}
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
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); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *StrArray) LockFunc(f func(array []string)) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *StrArray) Merge(array interface{}) *StrArray {
|
||||
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) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *StrArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]string
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *StrArray) Pad(size int, value string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = value
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *StrArray) Rand() (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// 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 <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *StrArray) Shuffle() *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *StrArray) Reverse() *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
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)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *StrArray) CountValues() map[string]int {
|
||||
m := make(map[string]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *StrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *StrArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a StrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.array == nil {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *StrArray) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty string value of the array.
|
||||
func (a *StrArray) FilterEmpty() *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *StrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
795
container/garray/garray_sorted_any.go
Normal file
795
container/garray/garray_sorted_any.go
Normal file
@ -0,0 +1,795 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
// SortedArray is a golang sorted array with rich features.
|
||||
// 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
|
||||
array []interface{}
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
// NewSortedArray creates and returns an empty sorted array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default.
|
||||
// The parameter `comparator` used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
|
||||
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
|
||||
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
|
||||
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, comparator, safe...)
|
||||
}
|
||||
|
||||
// NewSortedArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, 0, cap),
|
||||
comparator: comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]interface{}, (end-start+1)/step)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
index++
|
||||
}
|
||||
return NewSortedArrayFrom(slice, comparator, safe...)
|
||||
}
|
||||
|
||||
// NewSortedArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
a := NewSortedArraySize(0, comparator, safe...)
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return NewSortedArrayFrom(newArray, comparator, safe...)
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `nil`.
|
||||
func (a *SortedArray) At(index int) (value interface{}) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// SetComparator sets/changes the comparator for sorting.
|
||||
// It resorts the array as the comparator is changed.
|
||||
func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.comparator = comparator
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
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
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
a.array = append(a.array[:index], append([]interface{}{value}, a.array[index:]...)...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedArray) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedArray) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]interface{}, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedArray) SubSlice(offset int, length ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *SortedArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *SortedArray) Slice() []interface{} {
|
||||
var array []interface{}
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *SortedArray) Interfaces() []interface{} {
|
||||
return a.Slice()
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *SortedArray) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = min + (max-min)/2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
case cmp > 0:
|
||||
min = mid + 1
|
||||
default:
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedArray) Unique() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedArray) Clone() (newArray *SortedArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedArrayFrom(array, a.comparator, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedArray) Clear() *SortedArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
return a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]interface{}
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedArray) Rand() (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedArray) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedArray) CountValues() map[interface{}]int {
|
||||
m := make(map[interface{}]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a 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.comparator == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.comparator != nil && a.array != nil {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
if a.comparator != nil && a.array != nil {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterNil removes all nil value of the array.
|
||||
func (a *SortedArray) FilterNil() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty value of the array.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (a *SortedArray) FilterEmpty() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it panics.
|
||||
func (a *SortedArray) getComparator() func(a, b interface{}) int {
|
||||
if a.comparator == nil {
|
||||
panic("comparator is missing for sorted array")
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
743
container/garray/garray_sorted_int.go
Normal file
743
container/garray/garray_sorted_int.go
Normal file
@ -0,0 +1,743 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// SortedIntArray is a golang sorted int array with rich features.
|
||||
// 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
|
||||
array []int
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
// NewSortedIntArray creates and returns an empty sorted array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArray(safe ...bool) *SortedIntArray {
|
||||
return NewSortedIntArraySize(0, safe...)
|
||||
}
|
||||
|
||||
// NewSortedIntArrayComparator creates and returns an empty sorted array with specified comparator.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default.
|
||||
func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *SortedIntArray {
|
||||
array := NewSortedIntArray(safe...)
|
||||
array.comparator = comparator
|
||||
return array
|
||||
}
|
||||
|
||||
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
|
||||
return &SortedIntArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, 0, cap),
|
||||
comparator: defaultComparatorInt,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedIntArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]int, (end-start+1)/step)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
index++
|
||||
}
|
||||
return NewSortedIntArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewSortedIntArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
|
||||
a := NewSortedIntArraySize(0, safe...)
|
||||
a.array = array
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return NewSortedIntArrayFrom(newArray, safe...)
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `0`.
|
||||
func (a *SortedIntArray) At(index int) (value int) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
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
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedIntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedIntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedIntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedIntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]int, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedIntArray) SubSlice(offset int, length ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *SortedIntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedIntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *SortedIntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *SortedIntArray) Interfaces() []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *SortedIntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedIntArray) Search(value int) (index int) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = min + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
case cmp > 0:
|
||||
min = mid + 1
|
||||
default:
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
return a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]int
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedIntArray) Rand() (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedIntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedIntArray) CountValues() map[int]int {
|
||||
m := make(map[int]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedIntArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.comparator == nil {
|
||||
a.array = make([]int, 0)
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterEmpty removes all zero value of the array.
|
||||
func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortInt(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedIntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedIntArray) getComparator() func(a, b int) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorInt
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
756
container/garray/garray_sorted_str.go
Normal file
756
container/garray/garray_sorted_str.go
Normal file
@ -0,0 +1,756 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// SortedStrArray is a golang sorted string array with rich features.
|
||||
// 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
|
||||
array []string
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b string) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
// NewSortedStrArray creates and returns an empty sorted array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArray(safe ...bool) *SortedStrArray {
|
||||
return NewSortedStrArraySize(0, safe...)
|
||||
}
|
||||
|
||||
// NewSortedStrArrayComparator creates and returns an empty sorted array with specified comparator.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default.
|
||||
func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool) *SortedStrArray {
|
||||
array := NewSortedStrArray(safe...)
|
||||
array.comparator = comparator
|
||||
return array
|
||||
}
|
||||
|
||||
// NewSortedStrArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
return &SortedStrArray{
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, 0, cap),
|
||||
comparator: defaultComparatorStr,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedStrArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
|
||||
a := NewSortedStrArraySize(0, safe...)
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return NewSortedStrArrayFrom(newArray, safe...)
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns an empty string.
|
||||
func (a *SortedStrArray) At(index int) (value string) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedStrArray) Sort() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
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
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
rear := append([]string{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedStrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedStrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopRand() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedStrArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
array = make([]string, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedStrArray) SubSlice(offset int, length ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedStrArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *SortedStrArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Slice returns the underlying data of array.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *SortedStrArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Interfaces returns current array as []interface{}.
|
||||
func (a *SortedStrArray) Interfaces() []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Contains checks whether a value exists in the array.
|
||||
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) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = min + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
case cmp > 0:
|
||||
min = mid + 1
|
||||
default:
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedStrArray) Unique() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedStrArray) Clone() (newArray *SortedStrArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedStrArray) Clear() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
|
||||
return a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedStrArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]string
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size:end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedStrArray) Rand() (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedStrArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedStrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedStrArray) CountValues() map[string]int {
|
||||
m := make(map[string]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for k, v := range a.array {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for i := len(a.array) - 1; i >= 0; i-- {
|
||||
if !f(i, a.array[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedStrArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.comparator == nil {
|
||||
a.array = make([]string, 0)
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty string value of the array.
|
||||
func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortStr(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedStrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedStrArray) getComparator() func(a, b string) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorStr
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
40
container/garray/garray_z_bench_any_test.go
Normal file
40
container/garray/garray_z_bench_any_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
)
|
||||
|
||||
type anySortedArrayItem struct {
|
||||
priority int64
|
||||
value interface{}
|
||||
}
|
||||
|
||||
var (
|
||||
anyArray = garray.NewArray()
|
||||
anySortedArray = garray.NewSortedArray(func(a, b interface{}) int {
|
||||
return int(a.(anySortedArrayItem).priority - b.(anySortedArrayItem).priority)
|
||||
})
|
||||
)
|
||||
|
||||
func Benchmark_AnyArray_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
anyArray.Append(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_AnySortedArray_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
anySortedArray.Add(anySortedArrayItem{
|
||||
priority: int64(i),
|
||||
value: i,
|
||||
})
|
||||
}
|
||||
}
|
||||
281
container/garray/garray_z_example_normal_any_test.go
Normal file
281
container/garray/garray_z_example_normal_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/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
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"}
|
||||
}
|
||||
710
container/garray/garray_z_example_normal_int_test.go
Normal file
710
container/garray/garray_z_example_normal_int_test.go
Normal file
@ -0,0 +1,710 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func ExampleIntArray_Walk() {
|
||||
var array garray.IntArray
|
||||
tables := g.SliceInt{10, 20}
|
||||
prefix := 99
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value int) int {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [109 119]
|
||||
}
|
||||
|
||||
func ExampleNewIntArray() {
|
||||
s := garray.NewIntArray()
|
||||
s.Append(10)
|
||||
s.Append(20)
|
||||
s.Append(15)
|
||||
s.Append(30)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30]
|
||||
}
|
||||
|
||||
func ExampleNewIntArraySize() {
|
||||
s := garray.NewIntArraySize(3, 5)
|
||||
s.Set(0, 10)
|
||||
s.Set(1, 20)
|
||||
s.Set(2, 15)
|
||||
s.Set(3, 30)
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [10 20 15] 3 5
|
||||
}
|
||||
|
||||
func ExampleNewIntArrayFrom() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30] 4 4
|
||||
}
|
||||
|
||||
func ExampleNewIntArrayFromCopy() {
|
||||
s := garray.NewIntArrayFromCopy(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30] 4 4
|
||||
}
|
||||
|
||||
func ExampleIntArray_At() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
sAt := s.At(2)
|
||||
fmt.Println(sAt)
|
||||
|
||||
// Output:
|
||||
// 15
|
||||
}
|
||||
|
||||
func ExampleIntArray_Get() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
sGet, sBool := s.Get(3)
|
||||
fmt.Println(sGet, sBool)
|
||||
|
||||
// Output:
|
||||
// 30 true
|
||||
}
|
||||
|
||||
func ExampleIntArray_Set() {
|
||||
s := garray.NewIntArraySize(3, 5)
|
||||
s.Set(0, 10)
|
||||
s.Set(1, 20)
|
||||
s.Set(2, 15)
|
||||
s.Set(3, 30)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 15]
|
||||
}
|
||||
|
||||
func ExampleIntArray_SetArray() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Replace() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s.Slice())
|
||||
s.Replace(g.SliceInt{12, 13})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30]
|
||||
// [12 13 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Sum() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
a := s.Sum()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// 75
|
||||
}
|
||||
|
||||
func ExampleIntArray_Sort() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
a := s.Sort()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// [10,15,20,30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_SortFunc() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.SortFunc(func(v1, v2 int) bool {
|
||||
// fmt.Println(v1,v2)
|
||||
return v1 > v2
|
||||
})
|
||||
fmt.Println(s)
|
||||
s.SortFunc(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [30,20,15,10]
|
||||
// [10,15,20,30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_InsertBefore() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
s.InsertBefore(1, 99)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 99 20 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_InsertAfter() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
s.InsertAfter(1, 99)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 99 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Remove() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.Remove(1)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [10 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_RemoveValue() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.RemoveValue(20)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [10 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PushLeft() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.PushLeft(96, 97, 98, 99)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [96 97 98 99 10 20 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PushRight() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.PushRight(96, 97, 98, 99)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [10 20 15 30 96 97 98 99]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopLeft() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.PopLeft()
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [20 15 30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopRight() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s)
|
||||
s.PopRight()
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30]
|
||||
// [10 20 15]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopRand() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60, 70})
|
||||
fmt.Println(s)
|
||||
r, _ := s.PopRand()
|
||||
fmt.Println(s)
|
||||
fmt.Println(r)
|
||||
|
||||
// May Output:
|
||||
// [10,20,15,30,40,50,60,70]
|
||||
// [10,20,15,30,40,60,70]
|
||||
// 50
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopRands() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.PopRands(2)
|
||||
fmt.Println(s)
|
||||
fmt.Println(r)
|
||||
|
||||
// May Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,15,30,40]
|
||||
// [50 60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopLefts() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.PopLefts(2)
|
||||
fmt.Println(s)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [15,30,40,50,60]
|
||||
// [10 20]
|
||||
}
|
||||
|
||||
func ExampleIntArray_PopRights() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.PopRights(2)
|
||||
fmt.Println(s)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,15,30,40]
|
||||
// [50 60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Range() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.Range(2, 5)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [15 30 40]
|
||||
}
|
||||
|
||||
func ExampleIntArray_SubSlice() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.SubSlice(3, 4)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [30 40 50 60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Append() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
s.Append(96, 97, 98)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,15,30,40,50,60,96,97,98]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Len() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Len())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// 7
|
||||
}
|
||||
|
||||
func ExampleIntArray_Slice() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30 40 50 60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Interfaces() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
r := s.Interfaces()
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10 20 15 30 40 50 60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Clone() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.Clone()
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,15,30,40,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Clear() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Clear())
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleIntArray_Contains() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s.Contains(20))
|
||||
fmt.Println(s.Contains(21))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIntArray_Search() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s.Search(20))
|
||||
fmt.Println(s.Search(21))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleIntArray_Unique() {
|
||||
s := garray.NewIntArray()
|
||||
s.SetArray(g.SliceInt{10, 20, 15, 15, 20, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Unique())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,15,20,50,60]
|
||||
// [10,20,15,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_LockFunc() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.LockFunc(func(array []int) {
|
||||
for i := 0; i < len(array)-1; i++ {
|
||||
fmt.Println(array[i])
|
||||
}
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// 20
|
||||
// 15
|
||||
// 30
|
||||
// 40
|
||||
// 50
|
||||
}
|
||||
|
||||
func ExampleIntArray_RLockFunc() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.RLockFunc(func(array []int) {
|
||||
for i := 0; i < len(array); i++ {
|
||||
fmt.Println(array[i])
|
||||
}
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// 20
|
||||
// 15
|
||||
// 30
|
||||
// 40
|
||||
// 50
|
||||
// 60
|
||||
}
|
||||
|
||||
func ExampleIntArray_Merge() {
|
||||
s1 := garray.NewIntArray()
|
||||
s2 := garray.NewIntArray()
|
||||
s1.SetArray(g.SliceInt{10, 20, 15})
|
||||
s2.SetArray(g.SliceInt{40, 50, 60})
|
||||
fmt.Println(s1)
|
||||
fmt.Println(s2)
|
||||
s1.Merge(s2)
|
||||
fmt.Println(s1)
|
||||
|
||||
// Output:
|
||||
// [10,20,15]
|
||||
// [40,50,60]
|
||||
// [10,20,15,40,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Fill() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
s.Fill(2, 3, 99)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,99,99,99,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Chunk() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
r := s.Chunk(3)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [[10 20 15] [30 40 50] [60]]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Pad() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.Pad(8, 99)
|
||||
fmt.Println(s)
|
||||
s.Pad(-10, 89)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60,99]
|
||||
// [89,89,10,20,15,30,40,50,60,99]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Rand() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Rand())
|
||||
|
||||
// May Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// 10 true
|
||||
}
|
||||
|
||||
func ExampleIntArray_Rands() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Rands(3))
|
||||
|
||||
// May Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [20 50 20]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Shuffle() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Shuffle())
|
||||
|
||||
// May Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,40,15,50,20,60,30]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Reverse() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Reverse())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [60,50,40,30,15,20,10]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Join() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Join(","))
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// 10,20,15,30,40,50,60
|
||||
}
|
||||
|
||||
func ExampleIntArray_CountValues() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 15, 40, 40, 40})
|
||||
fmt.Println(s.CountValues())
|
||||
|
||||
// Output:
|
||||
// map[10:1 15:2 20:1 40:3]
|
||||
}
|
||||
|
||||
func ExampleIntArray_Iterator() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.Iterator(func(k int, v int) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 10
|
||||
// 1 20
|
||||
// 2 15
|
||||
// 3 30
|
||||
// 4 40
|
||||
// 5 50
|
||||
// 6 60
|
||||
}
|
||||
|
||||
func ExampleIntArray_IteratorAsc() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.IteratorAsc(func(k int, v int) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 10
|
||||
// 1 20
|
||||
// 2 15
|
||||
// 3 30
|
||||
// 4 40
|
||||
// 5 50
|
||||
// 6 60
|
||||
}
|
||||
|
||||
func ExampleIntArray_IteratorDesc() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
s.IteratorDesc(func(k int, v int) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 6 60
|
||||
// 5 50
|
||||
// 4 40
|
||||
// 3 30
|
||||
// 2 15
|
||||
// 1 20
|
||||
// 0 10
|
||||
}
|
||||
|
||||
func ExampleIntArray_String() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.String())
|
||||
|
||||
// Output:
|
||||
// [10,20,15,30,40,50,60]
|
||||
// [10,20,15,30,40,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_MarshalJSON() {
|
||||
type Student struct {
|
||||
Id int
|
||||
Name string
|
||||
Scores garray.IntArray
|
||||
}
|
||||
var array garray.IntArray
|
||||
array.SetArray(g.SliceInt{98, 97, 96})
|
||||
s := Student{
|
||||
Id: 1,
|
||||
Name: "john",
|
||||
Scores: array,
|
||||
}
|
||||
b, _ := json.Marshal(s)
|
||||
fmt.Println(string(b))
|
||||
|
||||
// Output:
|
||||
// {"Id":1,"Name":"john","Scores":[98,97,96]}
|
||||
}
|
||||
|
||||
func ExampleIntArray_UnmarshalJSON() {
|
||||
b := []byte(`{"Id":1,"Name":"john","Scores":[98,96,97]}`)
|
||||
type Student struct {
|
||||
Id int
|
||||
Name string
|
||||
Scores *garray.IntArray
|
||||
}
|
||||
s := Student{}
|
||||
json.Unmarshal(b, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// {1 john [98,96,97]}
|
||||
}
|
||||
|
||||
func ExampleIntArray_UnmarshalValue() {
|
||||
type Student struct {
|
||||
Name string
|
||||
Scores *garray.IntArray
|
||||
}
|
||||
|
||||
var s *Student
|
||||
gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"scores": g.SliceInt{96, 98, 97},
|
||||
}, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// &{john [96,98,97]}
|
||||
}
|
||||
|
||||
func ExampleIntArray_FilterEmpty() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 40, 50, 0, 0, 0, 60})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.FilterEmpty())
|
||||
|
||||
// Output:
|
||||
// [10,40,50,0,0,0,60]
|
||||
// [10,40,50,60]
|
||||
}
|
||||
|
||||
func ExampleIntArray_IsEmpty() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60})
|
||||
fmt.Println(s.IsEmpty())
|
||||
s1 := garray.NewIntArray()
|
||||
fmt.Println(s1.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
}
|
||||
642
container/garray/garray_z_example_normal_str_test.go
Normal file
642
container/garray/garray_z_example_normal_str_test.go
Normal file
@ -0,0 +1,642 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
func ExampleNewStrArray() {
|
||||
s := garray.NewStrArray()
|
||||
s.Append("We")
|
||||
s.Append("are")
|
||||
s.Append("GF")
|
||||
s.Append("fans")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [We are GF fans]
|
||||
}
|
||||
|
||||
func ExampleNewStrArraySize() {
|
||||
s := garray.NewStrArraySize(3, 5)
|
||||
s.Set(0, "We")
|
||||
s.Set(1, "are")
|
||||
s.Set(2, "GF")
|
||||
s.Set(3, "fans")
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [We are GF] 3 5
|
||||
}
|
||||
|
||||
func ExampleNewStrArrayFrom() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"We", "are", "GF", "fans", "!"})
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [We are GF fans !] 5 5
|
||||
}
|
||||
|
||||
func ExampleStrArray_At() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"We", "are", "GF", "fans", "!"})
|
||||
sAt := s.At(2)
|
||||
fmt.Println(sAt)
|
||||
|
||||
// Output:
|
||||
// GF
|
||||
}
|
||||
|
||||
func ExampleStrArray_Get() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"We", "are", "GF", "fans", "!"})
|
||||
sGet, sBool := s.Get(3)
|
||||
fmt.Println(sGet, sBool)
|
||||
|
||||
// Output:
|
||||
// fans true
|
||||
}
|
||||
|
||||
func ExampleStrArray_Set() {
|
||||
s := garray.NewStrArraySize(3, 5)
|
||||
s.Set(0, "We")
|
||||
s.Set(1, "are")
|
||||
s.Set(2, "GF")
|
||||
s.Set(3, "fans")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [We are GF]
|
||||
}
|
||||
|
||||
func ExampleStrArray_SetArray() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"We", "are", "GF", "fans", "!"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [We are GF fans !]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Replace() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"We", "are", "GF", "fans", "!"})
|
||||
fmt.Println(s.Slice())
|
||||
s.Replace(g.SliceStr{"Happy", "coding"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [We are GF fans !]
|
||||
// [Happy coding GF fans !]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Sum() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"3", "5", "10"})
|
||||
a := s.Sum()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// 18
|
||||
}
|
||||
|
||||
func ExampleStrArray_Sort() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "a", "c"})
|
||||
a := s.Sort()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_SortFunc() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
fmt.Println(s)
|
||||
s.SortFunc(func(v1, v2 string) bool {
|
||||
return gstr.Compare(v1, v2) > 0
|
||||
})
|
||||
fmt.Println(s)
|
||||
s.SortFunc(func(v1, v2 string) bool {
|
||||
return gstr.Compare(v1, v2) < 0
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["b","c","a"]
|
||||
// ["c","b","a"]
|
||||
// ["a","b","c"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_InsertBefore() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.InsertBefore(1, "here")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a here b c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_InsertAfter() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.InsertAfter(1, "here")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b here c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Remove() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.Remove(1)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_RemoveValue() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.RemoveValue("b")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PushLeft() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.PushLeft("We", "are", "GF", "fans")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [We are GF fans a b c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PushRight() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.PushRight("We", "are", "GF", "fans")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d We are GF fans]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopLeft() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.PopLeft()
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [b c d]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopRight() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d"})
|
||||
s.PopRight()
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopRand() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r, _ := s.PopRand()
|
||||
fmt.Println(r)
|
||||
|
||||
// May Output:
|
||||
// e
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopRands() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.PopRands(2)
|
||||
fmt.Println(r)
|
||||
|
||||
// May Output:
|
||||
// [e c]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopLefts() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.PopLefts(2)
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [a b]
|
||||
// ["c","d","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_PopRights() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.PopRights(2)
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [g h]
|
||||
// ["a","b","c","d","e","f"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Range() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.Range(2, 5)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [c d e]
|
||||
}
|
||||
|
||||
func ExampleStrArray_SubSlice() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.SubSlice(3, 4)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [d e f g]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Append() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"We", "are", "GF", "fans"})
|
||||
s.Append("a", "b", "c")
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["We","are","GF","fans","a","b","c"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Len() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Len())
|
||||
|
||||
// Output:
|
||||
// 8
|
||||
}
|
||||
|
||||
func ExampleStrArray_Slice() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d e f g h]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Interfaces() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.Interfaces()
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [a b c d e f g h]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Clone() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.Clone()
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Clear() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Clear())
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleStrArray_Contains() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Contains("e"))
|
||||
fmt.Println(s.Contains("z"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrArray_ContainsI() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.ContainsI("E"))
|
||||
fmt.Println(s.ContainsI("z"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrArray_Search() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Search("e"))
|
||||
fmt.Println(s.Search("z"))
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleStrArray_Unique() {
|
||||
s := garray.NewStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"})
|
||||
fmt.Println(s.Unique())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_LockFunc() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
s.LockFunc(func(array []string) {
|
||||
array[len(array)-1] = "GF fans"
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","GF fans"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_RLockFunc() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e"})
|
||||
s.RLockFunc(func(array []string) {
|
||||
for i := 0; i < len(array); i++ {
|
||||
fmt.Println(array[i])
|
||||
}
|
||||
})
|
||||
|
||||
// Output:
|
||||
// a
|
||||
// b
|
||||
// c
|
||||
// d
|
||||
// e
|
||||
}
|
||||
|
||||
func ExampleStrArray_Merge() {
|
||||
s1 := garray.NewStrArray()
|
||||
s2 := garray.NewStrArray()
|
||||
s1.SetArray(g.SliceStr{"a", "b", "c"})
|
||||
s2.SetArray(g.SliceStr{"d", "e", "f"})
|
||||
s1.Merge(s2)
|
||||
fmt.Println(s1)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Fill() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
s.Fill(2, 3, "here")
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","here","here","here","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Chunk() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
r := s.Chunk(3)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [[a b c] [d e f] [g h]]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Pad() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
s.Pad(7, "here")
|
||||
fmt.Println(s)
|
||||
s.Pad(-10, "there")
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","here","here","here","here"]
|
||||
// ["there","there","there","a","b","c","here","here","here","here"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Rand() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Rand())
|
||||
|
||||
// May Output:
|
||||
// c true
|
||||
}
|
||||
|
||||
func ExampleStrArray_Rands() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Rands(3))
|
||||
|
||||
// May Output:
|
||||
// [e h e]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Shuffle() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Shuffle())
|
||||
|
||||
// May Output:
|
||||
// ["a","c","e","d","b","g","f","h"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Reverse() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "d", "e", "f", "g", "h"})
|
||||
fmt.Println(s.Reverse())
|
||||
|
||||
// Output:
|
||||
// ["h","g","f","e","d","c","b","a"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Join() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
fmt.Println(s.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c
|
||||
}
|
||||
|
||||
func ExampleStrArray_CountValues() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"})
|
||||
fmt.Println(s.CountValues())
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:1 c:3 d:2]
|
||||
}
|
||||
|
||||
func ExampleStrArray_Iterator() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
s.Iterator(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
}
|
||||
|
||||
func ExampleStrArray_IteratorAsc() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
s.IteratorAsc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
}
|
||||
|
||||
func ExampleStrArray_IteratorDesc() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
s.IteratorDesc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func ExampleStrArray_String() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
fmt.Println(s.String())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_MarshalJSON() {
|
||||
type Student struct {
|
||||
Id int
|
||||
Name string
|
||||
Lessons []string
|
||||
}
|
||||
s := Student{
|
||||
Id: 1,
|
||||
Name: "john",
|
||||
Lessons: []string{"Math", "English", "Music"},
|
||||
}
|
||||
b, _ := json.Marshal(s)
|
||||
fmt.Println(string(b))
|
||||
|
||||
// Output:
|
||||
// {"Id":1,"Name":"john","Lessons":["Math","English","Music"]}
|
||||
}
|
||||
|
||||
func ExampleStrArray_UnmarshalJSON() {
|
||||
b := []byte(`{"Id":1,"Name":"john","Lessons":["Math","English","Sport"]}`)
|
||||
type Student struct {
|
||||
Id int
|
||||
Name string
|
||||
Lessons *garray.StrArray
|
||||
}
|
||||
s := Student{}
|
||||
json.Unmarshal(b, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// {1 john ["Math","English","Sport"]}
|
||||
}
|
||||
|
||||
func ExampleStrArray_UnmarshalValue() {
|
||||
type Student struct {
|
||||
Name string
|
||||
Lessons *garray.StrArray
|
||||
}
|
||||
var s *Student
|
||||
gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"lessons": []byte(`["Math","English","Sport"]`),
|
||||
}, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
var s1 *Student
|
||||
gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"lessons": g.SliceStr{"Math", "English", "Sport"},
|
||||
}, &s1)
|
||||
fmt.Println(s1)
|
||||
|
||||
// Output:
|
||||
// &{john ["Math","English","Sport"]}
|
||||
// &{john ["Math","English","Sport"]}
|
||||
}
|
||||
|
||||
func ExampleStrArray_FilterEmpty() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "", "c", "", "", "d"})
|
||||
fmt.Println(s.FilterEmpty())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleStrArray_IsEmpty() {
|
||||
s := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "", "c", "", "", "d"})
|
||||
fmt.Println(s.IsEmpty())
|
||||
s1 := garray.NewStrArray()
|
||||
fmt.Println(s1.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
}
|
||||
574
container/garray/garray_z_example_sorted_str_test.go
Normal file
574
container/garray/garray_z_example_sorted_str_test.go
Normal file
@ -0,0 +1,574 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func ExampleSortedStrArray_Walk() {
|
||||
var array garray.SortedStrArray
|
||||
tables := g.SliceStr{"user", "user_detail"}
|
||||
prefix := "gf_"
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value string) string {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
|
||||
func ExampleNewSortedStrArray() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.Append("b")
|
||||
s.Append("d")
|
||||
s.Append("c")
|
||||
s.Append("a")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
}
|
||||
|
||||
func ExampleNewSortedStrArraySize() {
|
||||
s := garray.NewSortedStrArraySize(3)
|
||||
s.SetArray([]string{"b", "d", "a", "c"})
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [a b c d] 4 4
|
||||
}
|
||||
|
||||
func ExampleNewStrArrayFromCopy() {
|
||||
s := garray.NewSortedStrArrayFromCopy(g.SliceStr{"b", "d", "c", "a"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_At() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "d", "c", "a"})
|
||||
sAt := s.At(2)
|
||||
fmt.Println(s)
|
||||
fmt.Println(sAt)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
// c
|
||||
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Get() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "d", "c", "a", "e"})
|
||||
sGet, sBool := s.Get(3)
|
||||
fmt.Println(s)
|
||||
fmt.Println(sGet, sBool)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e"]
|
||||
// d true
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_SetArray() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray([]string{"b", "d", "a", "c"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_SetUnique() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray([]string{"b", "d", "a", "c", "c", "a"})
|
||||
fmt.Println(s.SetUnique(true))
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Sum() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray([]string{"5", "3", "2"})
|
||||
fmt.Println(s)
|
||||
a := s.Sum()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// ["2","3","5"]
|
||||
// 10
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Sort() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "a", "c"})
|
||||
fmt.Println(s)
|
||||
a := s.Sort()
|
||||
fmt.Println(a)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Remove() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
|
||||
fmt.Println(s.Slice())
|
||||
s.Remove(1)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
// [a c d]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_RemoveValue() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
|
||||
fmt.Println(s.Slice())
|
||||
s.RemoveValue("b")
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
// [a c d]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_PopLeft() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
|
||||
r, _ := s.PopLeft()
|
||||
fmt.Println(r)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// a
|
||||
// [b c d]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_PopRight() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
|
||||
fmt.Println(s.Slice())
|
||||
r, _ := s.PopRight()
|
||||
fmt.Println(r)
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d]
|
||||
// d
|
||||
// [a b c]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_PopRights() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.PopRights(2)
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [g h]
|
||||
// ["a","b","c","d","e","f"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Rand() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r, _ := s.PopRand()
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// May Output:
|
||||
// b
|
||||
// ["a","c","d","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_PopRands() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.PopRands(2)
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// May Output:
|
||||
// [d a]
|
||||
// ["b","c","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_PopLefts() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.PopLefts(2)
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// [a b]
|
||||
// ["c","d","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Range() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.Range(2, 5)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [c d e]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_SubSlice() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.SubSlice(3, 4)
|
||||
fmt.Println(s.Slice())
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [a b c d e f g h]
|
||||
// [d e f g]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Add() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.Add("b", "d", "c", "a")
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Append() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"b", "d", "c", "a"})
|
||||
fmt.Println(s)
|
||||
s.Append("f", "e", "g")
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d"]
|
||||
// ["a","b","c","d","e","f","g"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Len() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Len())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// 8
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Slice() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s.Slice())
|
||||
|
||||
// Output:
|
||||
// [a b c d e f g h]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Interfaces() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.Interfaces()
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [a b c d e f g h]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Clone() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.Clone()
|
||||
fmt.Println(r)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Clear() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Clear())
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Contains() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s.Contains("e"))
|
||||
fmt.Println(s.Contains("E"))
|
||||
fmt.Println(s.Contains("z"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_ContainsI() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.ContainsI("E"))
|
||||
fmt.Println(s.ContainsI("z"))
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Search() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Search("e"))
|
||||
fmt.Println(s.Search("E"))
|
||||
fmt.Println(s.Search("z"))
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// 4
|
||||
// -1
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Unique() {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.SetArray(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Unique())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c","c","c","d","d"]
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_LockFunc() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s.LockFunc(func(array []string) {
|
||||
array[len(array)-1] = "GF fans"
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// ["a","b","GF fans"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_RLockFunc() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s.RLockFunc(func(array []string) {
|
||||
array[len(array)-1] = "GF fans"
|
||||
fmt.Println(array[len(array)-1])
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// GF fans
|
||||
// ["a","b","GF fans"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Merge() {
|
||||
s1 := garray.NewSortedStrArray()
|
||||
s2 := garray.NewSortedStrArray()
|
||||
s1.SetArray(g.SliceStr{"b", "c", "a"})
|
||||
s2.SetArray(g.SliceStr{"e", "d", "f"})
|
||||
fmt.Println(s1)
|
||||
fmt.Println(s2)
|
||||
s1.Merge(s2)
|
||||
fmt.Println(s1)
|
||||
|
||||
// Output:
|
||||
// ["a","b","c"]
|
||||
// ["d","e","f"]
|
||||
// ["a","b","c","d","e","f"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Chunk() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
r := s.Chunk(3)
|
||||
fmt.Println(r)
|
||||
|
||||
// Output:
|
||||
// [[a b c] [d e f] [g h]]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Rands() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.Rands(3))
|
||||
|
||||
// May Output:
|
||||
// ["a","b","c","d","e","f","g","h"]
|
||||
// [h g c]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Join() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"})
|
||||
fmt.Println(s.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d,e,f,g,h
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_CountValues() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"})
|
||||
fmt.Println(s.CountValues())
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:1 c:3 d:2]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_Iterator() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s.Iterator(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_IteratorAsc() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s.IteratorAsc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_IteratorDesc() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s.IteratorDesc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_String() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
fmt.Println(s.String())
|
||||
|
||||
// Output:
|
||||
// ["a","b","c"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_MarshalJSON() {
|
||||
type Student struct {
|
||||
ID int
|
||||
Name string
|
||||
Levels garray.SortedStrArray
|
||||
}
|
||||
r := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"})
|
||||
s := Student{
|
||||
ID: 1,
|
||||
Name: "john",
|
||||
Levels: *r,
|
||||
}
|
||||
b, _ := json.Marshal(s)
|
||||
fmt.Println(string(b))
|
||||
|
||||
// Output:
|
||||
// {"ID":1,"Name":"john","Levels":["a","b","c"]}
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_UnmarshalJSON() {
|
||||
b := []byte(`{"Id":1,"Name":"john","Lessons":["Math","English","Sport"]}`)
|
||||
type Student struct {
|
||||
Id int
|
||||
Name string
|
||||
Lessons *garray.StrArray
|
||||
}
|
||||
s := Student{}
|
||||
json.Unmarshal(b, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// {1 john ["Math","English","Sport"]}
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_UnmarshalValue() {
|
||||
type Student struct {
|
||||
Name string
|
||||
Lessons *garray.StrArray
|
||||
}
|
||||
var s *Student
|
||||
gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"lessons": []byte(`["Math","English","Sport"]`),
|
||||
}, &s)
|
||||
fmt.Println(s)
|
||||
|
||||
var s1 *Student
|
||||
gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"lessons": g.SliceStr{"Math", "English", "Sport"},
|
||||
}, &s1)
|
||||
fmt.Println(s1)
|
||||
|
||||
// Output:
|
||||
// &{john ["Math","English","Sport"]}
|
||||
// &{john ["Math","English","Sport"]}
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_FilterEmpty() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"})
|
||||
fmt.Println(s)
|
||||
fmt.Println(s.FilterEmpty())
|
||||
|
||||
// Output:
|
||||
// ["","","","a","b","c","d"]
|
||||
// ["a","b","c","d"]
|
||||
}
|
||||
|
||||
func ExampleSortedStrArray_IsEmpty() {
|
||||
s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"})
|
||||
fmt.Println(s.IsEmpty())
|
||||
s1 := garray.NewSortedStrArray()
|
||||
fmt.Println(s1.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
}
|
||||
170
container/garray/garray_z_unit_all_basic_test.go
Normal file
170
container/garray/garray_z_unit_all_basic_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
func Test_Array_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.Array
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.IntArray
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.StrArray
|
||||
expect := []string{"b", "a"}
|
||||
array.Append("b", "a")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedArray
|
||||
array.SetComparator(gutil.ComparatorInt)
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedStrArray
|
||||
expect := []string{"a", "b", "c"}
|
||||
array.Add("c", "a", "b")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedIntArray_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 6}
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedArray2(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
|
||||
array1 := garray.NewFromCopy(a1)
|
||||
t.AssertIN(array1.PopRands(2), a1)
|
||||
t.Assert(len(array1.PopRands(1)), 1)
|
||||
t.Assert(len(array1.PopRands(9)), 3)
|
||||
})
|
||||
}
|
||||
759
container/garray/garray_z_unit_normal_any_test.go
Normal file
759
container/garray/garray_z_unit_normal_any_test.go
Normal file
@ -0,0 +1,759 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func Test_Array_Basic(t *testing.T) {
|
||||
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{}{})
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, 100)
|
||||
|
||||
v, ok := array.Get(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Get(1)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Search(100), 0)
|
||||
t.Assert(array3.Search(100), -1)
|
||||
t.Assert(array.Contains(100), true)
|
||||
|
||||
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)
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []interface{}{0, 1, 2, 3}
|
||||
expect2 := []interface{}{3, 2, 1, 0}
|
||||
array := garray.NewArray()
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
}
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) < v2.(int)
|
||||
})
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) > v2.(int)
|
||||
})
|
||||
t.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PushAndPop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []interface{}{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []interface{}{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
t.Assert(array.Slice(), []interface{}{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRands(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
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.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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
array2 := garray.NewArrayFrom(value1, true)
|
||||
t.Assert(array1.Range(0, 1), []interface{}{0})
|
||||
t.Assert(array1.Range(1, 2), []interface{}{1})
|
||||
t.Assert(array1.Range(0, 2), []interface{}{0, 1})
|
||||
t.Assert(array1.Range(-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.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
i1 := []interface{}{0, 1, 2, 3}
|
||||
i2 := []interface{}{4, 5, 6, 7}
|
||||
array1 := garray.NewArrayFrom(i1)
|
||||
array2 := garray.NewArrayFrom(i2)
|
||||
t.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
// s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i3 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i4 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStrArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewArrayFrom(i1)
|
||||
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i3).Len(), 9)
|
||||
t.Assert(a1.Merge(i4).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Fill(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0}
|
||||
a2 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2, true)
|
||||
|
||||
t.Assert(array1.Fill(1, 2, 100), nil)
|
||||
t.Assert(array1.Slice(), []interface{}{0, 100, 100})
|
||||
|
||||
t.Assert(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.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Pad(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
|
||||
t.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_SubSlice(t *testing.T) {
|
||||
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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(len(array1.Rands(2)), 2)
|
||||
t.Assert(len(array1.Rands(10)), 10)
|
||||
t.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
i1, 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.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Reverse(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Replace(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
a3 := []interface{}{"m", "n", "p", "z", "x", "y", "d", "u"}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.Replace(a2)
|
||||
t.Assert(array2.Len(), 7)
|
||||
t.Assert(array2.Contains("b"), true)
|
||||
t.Assert(array2.Contains(4), true)
|
||||
t.Assert(array2.Contains("v"), false)
|
||||
array3 := array1.Replace(a3)
|
||||
t.Assert(array3.Len(), 7)
|
||||
t.Assert(array3.Contains(4), false)
|
||||
t.Assert(array3.Contains("p"), true)
|
||||
t.Assert(array3.Contains("u"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_SetArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array1 = array1.SetArray(a2)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("b"), true)
|
||||
t.Assert(array1.Contains("5"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Sum(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
a3 := []interface{}{"a", "1", "2"}
|
||||
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
array3 := garray.NewArrayFrom(a3)
|
||||
|
||||
t.Assert(array1.Sum(), 6)
|
||||
t.Assert(array2.Sum(), 0)
|
||||
t.Assert(array3.Sum(), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array2.Sum(), 6)
|
||||
t.AssertEQ(array1, array2)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "b", "c", "d", "e", "d"}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.CountValues()
|
||||
t.Assert(len(array2), 5)
|
||||
t.Assert(array2["b"], 1)
|
||||
t.Assert(array2["d"], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []interface{}) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_RLockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []interface{}) { // 读锁
|
||||
time.Sleep(2 * time.Second) // 暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Json(t *testing.T) {
|
||||
// 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)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.UnmarshalUseNumber(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
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.Array
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(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.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_RemoveValue(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.Array
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_FilterNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
|
||||
array := garray.NewArrayFromCopy(values)
|
||||
t.Assert(array.FilterNil().Slice(), values)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
|
||||
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{"1", "2"})
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
790
container/garray/garray_z_unit_normal_int_test.go
Normal file
790
container/garray/garray_z_unit_normal_int_test.go
Normal file
@ -0,0 +1,790 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func Test_IntArray_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
expect2 := []int{}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
array2 := garray.NewIntArrayFrom(expect2)
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, 100)
|
||||
|
||||
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)
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []int{0, 1, 2, 3}
|
||||
expect2 := []int{3, 2, 1, 0}
|
||||
array := garray.NewIntArray()
|
||||
array2 := garray.NewIntArray(true)
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
array2.Append(i)
|
||||
}
|
||||
array.Sort()
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
t.Assert(array.Slice(), expect2)
|
||||
t.Assert(array2.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PushAndPop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []int{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []int{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
t.Assert(array.Slice(), []int{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value1, true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
n1 := []int{0, 1, 2, 3}
|
||||
n2 := []int{4, 5, 6, 7}
|
||||
i1 := []interface{}{"1", "2"}
|
||||
s1 := []string{"a", "b", "c"}
|
||||
s2 := []string{"e", "f"}
|
||||
a1 := garray.NewIntArrayFrom(n1)
|
||||
a2 := garray.NewIntArrayFrom(n2)
|
||||
a3 := garray.NewArrayFrom(i1)
|
||||
a4 := garray.NewStrArrayFrom(s1)
|
||||
|
||||
a5 := garray.NewSortedStrArrayFrom(s2)
|
||||
a6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
|
||||
a7 := garray.NewSortedStrArrayFrom(s1)
|
||||
a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
|
||||
t.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
t.Assert(a1.Merge(a3).Len(), 10)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0}
|
||||
a2 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Pad(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a1, true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(len(array1.Rands(2)), 2)
|
||||
t.Assert(len(array1.Rands(10)), 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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewIntArrayFrom(a1)
|
||||
ns1 := array.PopRands(2)
|
||||
t.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
|
||||
t.Assert(len(ns1), 2)
|
||||
|
||||
ns2 := array.PopRands(7)
|
||||
t.Assert(len(ns2), 4)
|
||||
t.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Shuffle(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Reverse(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.String(), "[0,1,2,3,4,5,6]")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SetArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
a2 := []int{6, 7}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array1.SetArray(a2)
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(array1, []int{6, 7})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Replace(t *testing.T) {
|
||||
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)
|
||||
t.Assert(array1, []int{6, 7, 3, 5})
|
||||
|
||||
array1.Replace(a3)
|
||||
t.Assert(array1, []int{9, 10, 11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array1.Clear()
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
t.Assert(array1, array2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Get(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.Sum(), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 3}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
m1 := array1.CountValues()
|
||||
t.Assert(len(m1), 4)
|
||||
t.Assert(m1[1], 1)
|
||||
t.Assert(m1[3], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewIntArrayFromCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 3}
|
||||
array1 := garray.NewIntArrayFromCopy(a1)
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Remove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 4}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
v, ok := array1.Remove(1)
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
v, ok = array1.Remove(0)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
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.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []int) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SortFunc(t *testing.T) {
|
||||
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)
|
||||
t.Assert(a11, []int{1, 2, 3, 4})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []int) { // 读锁
|
||||
time.Sleep(2 * time.Second) // 暂停1秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := *garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.UnmarshalUseNumber(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
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.IntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(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.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewIntArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue(99), false)
|
||||
t.Assert(array.RemoveValue(20), true)
|
||||
t.Assert(array.RemoveValue(10), true)
|
||||
t.Assert(array.RemoveValue(20), false)
|
||||
t.Assert(array.RemoveValue(88), false)
|
||||
t.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.IntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var v *V
|
||||
// err := gconv.Struct(g.Map{
|
||||
// "name": "john",
|
||||
// "array": g.Slice{1, 2, 3},
|
||||
// }, &v)
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(v.Name, "john")
|
||||
// t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
// })
|
||||
}
|
||||
|
||||
func TestIntArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
789
container/garray/garray_z_unit_normal_str_test.go
Normal file
789
container/garray/garray_z_unit_normal_str_test.go
Normal file
@ -0,0 +1,789 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func Test_StrArray_Basic(t *testing.T) {
|
||||
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{})
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, "100")
|
||||
|
||||
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")
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, "100")
|
||||
array.InsertAfter(0, "200")
|
||||
t.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
|
||||
array.InsertBefore(5, "300")
|
||||
array.InsertAfter(6, "400")
|
||||
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.C(t, func(t *gtest.T) {
|
||||
expect1 := []string{"0", "1", "2", "3"}
|
||||
expect2 := []string{"3", "2", "1", "0"}
|
||||
array := garray.NewStrArray()
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(gconv.String(i))
|
||||
}
|
||||
array.Sort()
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
t.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PushAndPop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, "0")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, "3")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []string{"1", "2"})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []string{"1", "2"})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft("1").PushRight("2")
|
||||
t.Assert(array.Slice(), []string{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1", "2"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"3"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopRights(2), g.Slice{"2", "3"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArray()
|
||||
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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(value1)
|
||||
array2 := garray.NewStrArrayFrom(value1, true)
|
||||
t.Assert(array1.Range(0, 1), []interface{}{"0"})
|
||||
t.Assert(array1.Range(1, 2), []interface{}{"1"})
|
||||
t.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
|
||||
t.Assert(array1.Range(-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.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)
|
||||
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) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStrArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0"}
|
||||
a2 := []string{"0"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := garray.NewStrArrayFrom(a2)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5"})
|
||||
t.Assert(len(array1.Chunk(0)), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Pad(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := garray.NewStrArrayFrom(a1, true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(len(array1.Rands(2)), "2")
|
||||
t.Assert(len(array1.Rands(10)), 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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Reverse(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", `"a"`, `\a`}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewStrArrayFromCopy(t *testing.T) {
|
||||
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)
|
||||
t.Assert(a2.Contains("1"), true)
|
||||
t.Assert(a2.Len(), 7)
|
||||
t.Assert(a2, a3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_SetArray(t *testing.T) {
|
||||
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)
|
||||
t.Assert(array1.Contains("2"), true)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
array1 = array1.SetArray(a2)
|
||||
t.Assert(array1.Contains("2"), false)
|
||||
t.Assert(array1.Contains("c"), true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Replace(t *testing.T) {
|
||||
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)
|
||||
t.Assert(array1.Contains("2"), true)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
array1 = array1.Replace(a2)
|
||||
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)
|
||||
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.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)
|
||||
t.Assert(array1.Sum(), 21)
|
||||
t.Assert(array2.Sum(), 18)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRand(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
str1, 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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
t.Assert(array2, array1)
|
||||
t.Assert(array2.Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "4", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
|
||||
m1 := array1.CountValues()
|
||||
t.Assert(len(m1), 6)
|
||||
t.Assert(m1["2"], 1)
|
||||
t.Assert(m1["4"], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Remove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
s1, 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.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStrArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []string) { // 读锁
|
||||
time.Sleep(2 * time.Second) // 暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_SortFunc(t *testing.T) {
|
||||
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)
|
||||
t.Assert(a11, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStrArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []string) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Json(t *testing.T) {
|
||||
// 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)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.UnmarshalUseNumber(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
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.StrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(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.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewStrArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.StrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","2","3"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "2", "3"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
880
container/garray/garray_z_unit_sorted_any_test.go
Normal file
880
container/garray/garray_z_unit_sorted_any_test.go
Normal file
@ -0,0 +1,880 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
a2 := []interface{}{"h", "j", "i", "k"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
func2 := func(v1, v2 interface{}) int {
|
||||
return -1
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a2, func2)
|
||||
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []interface{}{"k", "i", "j", "h"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedArrayFromCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
func2 := func(v1, v2 interface{}) int {
|
||||
return -1
|
||||
}
|
||||
array1 := garray.NewSortedArrayFromCopy(a1, func1)
|
||||
array2 := garray.NewSortedArrayFromCopy(a1, func2)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array2, []interface{}{"c", "f", "a"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SetArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
a2 := []interface{}{"e", "h", "g", "k"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1.SetArray(a2)
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1, []interface{}{"e", "g", "h", "k"})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1.Sort()
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Get(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
v, ok := array1.Get(2)
|
||||
t.Assert(v, "f")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(1)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Remove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1, ok := array1.Remove(1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "b")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("b"), false)
|
||||
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(100000)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
i2, ok := array1.Remove(0)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i2), "a")
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
|
||||
i3, ok := array1.Remove(1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i3), "d")
|
||||
t.Assert(array1.Len(), 1)
|
||||
t.Assert(array1.Contains("d"), false)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopLeft()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "a")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"b", "c", "d"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopRight()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "d")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "b", "c"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRand(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1, ok := array1.PopRand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRands(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRands(2)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 2)
|
||||
|
||||
i2 := array1.PopRands(3)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Empty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArray(gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopLefts(2)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
i2 := array1.PopLefts(5)
|
||||
t.Assert(len(i2), 4)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRights(2)
|
||||
t.Assert(len(i1), 2)
|
||||
t.Assert(i1, []interface{}{"e", "f"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
i2 := array1.PopRights(10)
|
||||
t.Assert(len(i2), 4)
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Range(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.Range(2, 5)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 6)
|
||||
|
||||
i2 := array1.Range(7, 5)
|
||||
t.Assert(len(i2), 0)
|
||||
i2 = array1.Range(-1, 2)
|
||||
t.Assert(i2, []interface{}{"a", "b"})
|
||||
|
||||
i2 = array1.Range(4, 10)
|
||||
t.Assert(len(i2), 2)
|
||||
t.Assert(i2, []interface{}{"e", "f"})
|
||||
|
||||
t.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Sum(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
a2 := []interface{}{"1", "2", "3", "b", "e", "f"}
|
||||
a3 := []interface{}{"4", "5", "6"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a2, func1)
|
||||
array3 := garray.NewSortedArrayFrom(a3, func1)
|
||||
t.Assert(array1.Sum(), 0)
|
||||
t.Assert(array2.Sum(), 6)
|
||||
t.Assert(array3.Sum(), 15)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := array1.Clone()
|
||||
t.Assert(array1, array2)
|
||||
array1.Remove(1)
|
||||
t.AssertNE(array1, array2)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
t.Assert(array1.Len(), 6)
|
||||
array1.Clear()
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Chunk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Chunk(2)
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1[0], []interface{}{"a", "b"})
|
||||
t.Assert(i1[2], []interface{}{"e"})
|
||||
|
||||
i1 = array1.Chunk(0)
|
||||
t.Assert(len(i1), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SubSlice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.SubSlice(2, 3)
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
|
||||
i1 = array1.SubSlice(2, 6)
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
|
||||
i1 = array1.SubSlice(7, 2)
|
||||
t.Assert(len(i1), 0)
|
||||
|
||||
s1 := array1.SubSlice(1, -2)
|
||||
t.Assert(s1, nil)
|
||||
|
||||
s1 = array1.SubSlice(-9, 2)
|
||||
t.Assert(s1, nil)
|
||||
t.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Rand(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1, ok := array1.Rand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Rands(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Rands(2)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
t.Assert(len(i1), 2)
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
i1 = array1.Rands(4)
|
||||
t.Assert(len(i1), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
t.Assert(array1.Join(","), `a,c,d`)
|
||||
t.Assert(array1.Join("."), `a.c.d`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
t.Assert(array1.Join("."), `"a".0.1.\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, "a", "b"}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
t.Assert(array1.String(), `[0,1,"a","b"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
m1 := array1.CountValues()
|
||||
t.Assert(len(m1), 3)
|
||||
t.Assert(m1["c"], 2)
|
||||
t.Assert(m1["a"], 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
array1.SetUnique(true)
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []interface{}) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []interface{}) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候不会被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStrArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1)
|
||||
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i1).Len(), 9)
|
||||
t.Assert(a1.Merge(i2).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.SortedArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Iterator(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_RemoveValue(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_FilterNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
|
||||
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterNil().Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{"1", "2"}, gutil.ComparatorString)
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
733
container/garray/garray_z_unit_sorted_int_test.go
Normal file
733
container/garray/garray_z_unit_sorted_int_test.go
Normal file
@ -0,0 +1,733 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func TestNewSortedIntArrayFrom(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 5, 2, 1, 4, 3, 6}
|
||||
array1 := garray.NewSortedIntArrayFromCopy(a1, false)
|
||||
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SetArray(t *testing.T) {
|
||||
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)
|
||||
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1}
|
||||
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := array1.Sort()
|
||||
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []int{0, 1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Get(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 0}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 0}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(-100000)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Search(5), 2)
|
||||
|
||||
v, ok = array1.Remove(0)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Search(5), 1)
|
||||
|
||||
a2 := []int{1, 3, 4}
|
||||
array2 := garray.NewSortedIntArrayFrom(a2)
|
||||
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopRands(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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopLefts(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)
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopRights(2)
|
||||
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)
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Range(t *testing.T) {
|
||||
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)
|
||||
t.Assert(len(ns1), 3)
|
||||
t.Assert(ns1, []int{2, 3, 5})
|
||||
|
||||
ns2 := array1.Range(5, 4)
|
||||
t.Assert(len(ns2), 0)
|
||||
|
||||
ns3 := array1.Range(-1, 4)
|
||||
t.Assert(len(ns3), 4)
|
||||
|
||||
nsl := array1.Range(5, 8)
|
||||
t.Assert(len(nsl), 1)
|
||||
t.Assert(array2.Range(1, 2), []int{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Sum(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
n1 := array1.Sum()
|
||||
t.Assert(n1, 9)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `1.3.5`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
t.Assert(array1.String(), `[1,3,5]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Contains(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
t.Assert(array1.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
t.Assert(array2.Len(), 3)
|
||||
t.Assert(array2, array1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.Clear()
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Chunk(t *testing.T) {
|
||||
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)
|
||||
t.Assert(len(ns1), 3)
|
||||
t.Assert(ns1[0], []int{1, 2})
|
||||
t.Assert(ns1[2], []int{5})
|
||||
t.Assert(len(ns2), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SubSlice(t *testing.T) {
|
||||
gtest.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)
|
||||
t.Assert(len(ns1), 2)
|
||||
t.Assert(ns1, []int{2, 3})
|
||||
|
||||
ns2 := array1.SubSlice(7, 2)
|
||||
t.Assert(len(ns2), 0)
|
||||
|
||||
ns3 := array1.SubSlice(3, 5)
|
||||
t.Assert(len(ns3), 2)
|
||||
t.Assert(ns3, []int{4, 5})
|
||||
|
||||
ns4 := array1.SubSlice(3, 1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1, ok := array1.Rand()
|
||||
t.AssertIN(ns1, a1)
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Rands(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.Rands(2)
|
||||
t.AssertIN(ns1, a1)
|
||||
t.Assert(len(ns1), 2)
|
||||
|
||||
ns2 := array1.Rands(6)
|
||||
t.Assert(len(ns2), 6)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.CountValues() // 按每几个元素切成一个数组
|
||||
t.Assert(len(ns1), 5)
|
||||
t.Assert(ns1[2], 1)
|
||||
t.Assert(ns1[3], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.SetUnique(true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1, true)
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []int) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []int) { // 读锁
|
||||
time.Sleep(2 * time.Second) // 暂停1秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
i0 := []int{1, 2, 3, 4}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStrArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedIntArrayFrom(i0)
|
||||
|
||||
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) {
|
||||
// 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)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
a1 := *garray.NewSortedIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.UnmarshalUseNumber(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
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedIntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(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.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewSortedIntArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue(99), false)
|
||||
t.Assert(array.RemoveValue(20), true)
|
||||
t.Assert(array.RemoveValue(10), true)
|
||||
t.Assert(array.RemoveValue(20), false)
|
||||
t.Assert(array.RemoveValue(88), false)
|
||||
t.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedIntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
757
container/garray/garray_z_unit_sorted_str_test.go
Normal file
757
container/garray/garray_z_unit_sorted_str_test.go
Normal file
@ -0,0 +1,757 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func TestNewSortedStrArrayFrom(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
s1 := garray.NewSortedStrArrayFrom(a1, true)
|
||||
t.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
s2 := garray.NewSortedStrArrayFrom(a1, false)
|
||||
t.Assert(s2, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedStrArrayFromCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
s1 := garray.NewSortedStrArrayFromCopy(a1, true)
|
||||
t.Assert(s1.Len(), 4)
|
||||
t.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SetArray(t *testing.T) {
|
||||
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)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
|
||||
t.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
array1.Sort()
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(100000)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(2)
|
||||
t.Assert(v, "d")
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRands(2)
|
||||
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.PopRands(4)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopLefts(2)
|
||||
t.Assert(s1, []string{"a", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.PopLefts(4)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRights(2)
|
||||
t.Assert(s1, []string{"f", "g"})
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(len(s1), 2)
|
||||
s1 = array1.PopRights(6)
|
||||
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.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)
|
||||
t.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"c", "d"})
|
||||
|
||||
s1 = array1.Range(-1, 2)
|
||||
t.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"a", "b"})
|
||||
|
||||
s1 = array1.Range(4, 8)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"e", "f", "g"})
|
||||
t.Assert(array1.Range(10, 2), nil)
|
||||
|
||||
s2 := array2.Range(2, 4)
|
||||
t.Assert(s2, []string{"c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Sum(t *testing.T) {
|
||||
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)
|
||||
t.Assert(array1.Sum(), 0)
|
||||
t.Assert(array2.Sum(), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
t.Assert(array1, array2)
|
||||
array1.Remove(1)
|
||||
t.Assert(array2.Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array1.Clear()
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SubSlice(t *testing.T) {
|
||||
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)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"b", "c", "d"})
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
s2 := array1.SubSlice(1, 10)
|
||||
t.Assert(len(s2), 6)
|
||||
|
||||
s3 := array1.SubSlice(10, 2)
|
||||
t.Assert(len(s3), 0)
|
||||
|
||||
s3 = array1.SubSlice(-5, 2)
|
||||
t.Assert(s3, []string{"c", "d"})
|
||||
|
||||
s3 = array1.SubSlice(-10, 2)
|
||||
t.Assert(s3, nil)
|
||||
|
||||
s3 = array1.SubSlice(1, -2)
|
||||
t.Assert(s3, nil)
|
||||
|
||||
t.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Len(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Rand(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
v, ok := array1.Rand()
|
||||
t.AssertIN(v, []string{"e", "a", "d"})
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Rands(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.Rands(2)
|
||||
|
||||
t.AssertIN(s1, []string{"e", "a", "d"})
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.Rands(4)
|
||||
t.Assert(len(s1), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.Join(","), `a,d,e`)
|
||||
t.Assert(array1.Join("."), `a.d.e`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", `"b"`, `\c`}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `"b".\c.a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.String(), `["a","d","e"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_CountValues(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
m1 := array1.CountValues()
|
||||
t.Assert(m1["a"], 2)
|
||||
t.Assert(m1["d"], 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Chunk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.Chunk(2)
|
||||
t.Assert(len(array2), 3)
|
||||
t.Assert(len(array2[0]), 2)
|
||||
t.Assert(array2[1], []string{"c", "d"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.SetUnique(true)
|
||||
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.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStrArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
// go1
|
||||
go a1.LockFunc(func(n1 []string) { // 读写锁
|
||||
time.Sleep(2 * time.Second) // 暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_RLockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStrArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
// go1
|
||||
go a1.RLockFunc(func(n1 []string) { // 读锁
|
||||
time.Sleep(2 * time.Second) // 暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
// go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 // 等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStrArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedStrArrayFrom(s1)
|
||||
|
||||
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) {
|
||||
// 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)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
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)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.UnmarshalUseNumber(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
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedStrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(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.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedStrArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedStrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","3","2"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "3", "2"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
545
container/glist/glist.go
Normal file
545
container/glist/glist.go
Normal file
@ -0,0 +1,545 @@
|
||||
// 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 l file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
|
||||
package glist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type (
|
||||
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
|
||||
// The switch should be set when its initialization and cannot be changed then.
|
||||
List struct {
|
||||
mu rwmutex.RWMutex
|
||||
list *list.List
|
||||
}
|
||||
// Element the item type of the list.
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
// New creates and returns a new empty doubly linked list.
|
||||
func New(safe ...bool) *List {
|
||||
return &List{
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFrom creates and returns a list from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using list in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
l := list.New()
|
||||
for _, v := range array {
|
||||
l.PushBack(v)
|
||||
}
|
||||
return &List{
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: l,
|
||||
}
|
||||
}
|
||||
|
||||
// PushFront inserts a new element `e` with value `v` at the front of list `l` and returns `e`.
|
||||
func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushFront(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PushBack inserts a new element `e` with value `v` at the back of list `l` and returns `e`.
|
||||
func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushBack(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PushFronts inserts multiple new elements with values `values` at the front of list `l`.
|
||||
func (l *List) PushFronts(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushFront(v)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushBacks inserts multiple new elements with values `values` at the back of list `l`.
|
||||
func (l *List) PushBacks(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushBack(v)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PopBack removes the element from back of `l` and returns the value of the element.
|
||||
func (l *List) PopBack() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopFront removes the element from front of `l` and returns the value of the element.
|
||||
func (l *List) PopFront() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopBacks removes `max` elements from back of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
length = max
|
||||
}
|
||||
values = make([]interface{}, length)
|
||||
for i := 0; i < length; i++ {
|
||||
values[i] = l.list.Remove(l.list.Back())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopFronts removes `max` elements from front of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
length = max
|
||||
}
|
||||
values = make([]interface{}, length)
|
||||
for i := 0; i < length; i++ {
|
||||
values[i] = l.list.Remove(l.list.Front())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopBackAll removes all elements from back of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBackAll() []interface{} {
|
||||
return l.PopBacks(-1)
|
||||
}
|
||||
|
||||
// PopFrontAll removes all elements from front of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFrontAll() []interface{} {
|
||||
return l.PopFronts(-1)
|
||||
}
|
||||
|
||||
// FrontAll copies and returns values of all elements from front of `l` as slice.
|
||||
func (l *List) FrontAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BackAll copies and returns values of all elements from back of `l` as slice.
|
||||
func (l *List) BackAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FrontValue returns value of the first element of `l` or nil if the list is empty.
|
||||
func (l *List) FrontValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BackValue returns value of the last element of `l` or nil if the list is empty.
|
||||
func (l *List) BackValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Front returns the first element of list `l` or nil if the list is empty.
|
||||
func (l *List) Front() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Front()
|
||||
return
|
||||
}
|
||||
|
||||
// Back returns the last element of list `l` or nil if the list is empty.
|
||||
func (l *List) Back() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Back()
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of elements of list `l`.
|
||||
// The complexity is O(1).
|
||||
func (l *List) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length = l.list.Len()
|
||||
return
|
||||
}
|
||||
|
||||
// Size is alias of Len.
|
||||
func (l *List) Size() int {
|
||||
return l.Len()
|
||||
}
|
||||
|
||||
// MoveBefore moves element `e` to its new position before `p`.
|
||||
// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified.
|
||||
// The element and `p` must not be nil.
|
||||
func (l *List) MoveBefore(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveBefore(e, p)
|
||||
}
|
||||
|
||||
// MoveAfter moves element `e` to its new position after `p`.
|
||||
// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified.
|
||||
// The element and `p` must not be nil.
|
||||
func (l *List) MoveAfter(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveAfter(e, p)
|
||||
}
|
||||
|
||||
// MoveToFront moves element `e` to the front of list `l`.
|
||||
// If `e` is not an element of `l`, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToFront(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToFront(e)
|
||||
}
|
||||
|
||||
// MoveToBack moves element `e` to the back of list `l`.
|
||||
// If `e` is not an element of `l`, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToBack(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToBack(e)
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list `l`.
|
||||
// The lists `l` and `other` may be the same, but they must not be nil.
|
||||
func (l *List) PushBackList(other *List) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushBackList(other.list)
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list `l`.
|
||||
// The lists `l` and `other` may be the same, but they must not be nil.
|
||||
func (l *List) PushFrontList(other *List) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushFrontList(other.list)
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element `e` with value `v` immediately after `p` and returns `e`.
|
||||
// If `p` is not an element of `l`, the list is not modified.
|
||||
// The `p` must not be nil.
|
||||
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertAfter(v, p)
|
||||
return
|
||||
}
|
||||
|
||||
// InsertBefore inserts a new element `e` with value `v` immediately before `p` and returns `e`.
|
||||
// If `p` is not an element of `l`, the list is not modified.
|
||||
// The `p` must not be nil.
|
||||
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertBefore(v, p)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes `e` from `l` if `e` is an element of list `l`.
|
||||
// It returns the element value e.Value.
|
||||
// The element must not be nil.
|
||||
func (l *List) Remove(e *Element) (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
value = l.list.Remove(e)
|
||||
return
|
||||
}
|
||||
|
||||
// Removes removes multiple elements `es` from `l` if `es` are elements of list `l`.
|
||||
func (l *List) Removes(es []*Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveAll removes all elements from list `l`.
|
||||
func (l *List) RemoveAll() {
|
||||
l.mu.Lock()
|
||||
l.list = list.New()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Clear is alias of RemoveAll.
|
||||
func (l *List) Clear() {
|
||||
l.RemoveAll()
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (l *List) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list != nil {
|
||||
f(l.list)
|
||||
}
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (l *List) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (l *List) Iterator(f func(e *Element) bool) {
|
||||
l.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the list readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the list readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join joins list elements with a string `glue`.
|
||||
func (l *List) Join(glue string) string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
buffer.WriteString(gconv.String(e.Value))
|
||||
if i != length-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns current list as a string.
|
||||
func (l *List) String() string {
|
||||
return "[" + l.Join(",") + "]"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (l List) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.FrontAll())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (l *List) UnmarshalJSON(b []byte) error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
l.PushBacks(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for list.
|
||||
func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceAny(value)
|
||||
}
|
||||
l.PushBacks(array)
|
||||
return err
|
||||
}
|
||||
61
container/glist/glist_z_bench_test.go
Normal file
61
container/glist/glist_z_bench_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package glist
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
l = New(true)
|
||||
)
|
||||
|
||||
func Benchmark_PushBack(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
l.PushBack(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PushFront(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
l.PushFront(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Len(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.Len()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PopFront(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.PopFront()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PopBack(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.PopBack()
|
||||
}
|
||||
})
|
||||
}
|
||||
689
container/glist/glist_z_example_test.go
Normal file
689
container/glist/glist_z_example_test.go
Normal file
@ -0,0 +1,689 @@
|
||||
// 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 glist_test
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
n := 10
|
||||
l := glist.New()
|
||||
for i := 0; i < n; i++ {
|
||||
l.PushBack(i)
|
||||
}
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(l.PopFront())
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0,1,2,3,4,5,6,7,8,9]
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// [9 8 7 6 5 4 3 2 1 0]
|
||||
// 0123456789
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleNewFrom() {
|
||||
n := 10
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(l.PopFront())
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
// [1 2 3 4 5 6 7 8 9 10]
|
||||
// [10 9 8 7 6 5 4 3 2 1]
|
||||
// 12345678910
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleList_PushFront() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushFront(0)
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_PushBack() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushBack(6)
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,6]
|
||||
}
|
||||
|
||||
func ExampleList_PushFronts() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushFronts(g.Slice{0, -1, -2, -3, -4})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 10
|
||||
// [-4,-3,-2,-1,0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_PushBacks() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushBacks(g.Slice{6, 7, 8, 9, 10})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleList_PopBack() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBack())
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// 4
|
||||
// [1,2,3,4]
|
||||
}
|
||||
|
||||
func ExampleList_PopFront() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFront())
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
// 4
|
||||
// [2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_PopBacks() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBacks(2))
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [5 4]
|
||||
// 3
|
||||
// [1,2,3]
|
||||
}
|
||||
|
||||
func ExampleList_PopFronts() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFronts(2))
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1 2]
|
||||
// 3
|
||||
// [3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_PopBackAll() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBackAll())
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [5 4 3 2 1]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleList_PopFrontAll() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFrontAll())
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1 2 3 4 5]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleList_FrontAll() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleList_BackAll() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// [5 4 3 2 1]
|
||||
}
|
||||
|
||||
func ExampleList_FrontValue() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontValue())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleList_BackValue() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.BackValue())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleList_Front() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Front().Value)
|
||||
fmt.Println(l)
|
||||
|
||||
e := l.Front()
|
||||
l.InsertBefore(e, 0)
|
||||
l.InsertAfter(e, "a")
|
||||
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// [1,2,3,4,5]
|
||||
// [0,1,a,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_Back() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Back().Value)
|
||||
fmt.Println(l)
|
||||
|
||||
e := l.Back()
|
||||
l.InsertBefore(e, "a")
|
||||
l.InsertAfter(e, 6)
|
||||
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1,2,3,4,a,5,6]
|
||||
}
|
||||
|
||||
func ExampleList_Len() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleList_Size() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5})
|
||||
|
||||
fmt.Println(l.Size())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleList_MoveBefore() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
e := l.PushBack(6)
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
l.MoveBefore(e, l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e = &glist.Element{Value: 7}
|
||||
l.MoveBefore(e, l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,6]
|
||||
// 6
|
||||
// [6,1,2,3,4,5]
|
||||
// 6
|
||||
// [6,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_MoveAfter() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
e := l.PushFront(0)
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
l.MoveAfter(e, l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e = &glist.Element{Value: -1}
|
||||
l.MoveAfter(e, l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [0,1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,0]
|
||||
// 6
|
||||
// [1,2,3,4,5,0]
|
||||
}
|
||||
|
||||
func ExampleList_MoveToFront() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
l.MoveToFront(l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e := &glist.Element{Value: 6}
|
||||
l.MoveToFront(e)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [5,1,2,3,4]
|
||||
// 5
|
||||
// [5,1,2,3,4]
|
||||
}
|
||||
|
||||
func ExampleList_MoveToBack() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
l.MoveToBack(l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e := &glist.Element{Value: 0}
|
||||
l.MoveToBack(e)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [2,3,4,5,1]
|
||||
// 5
|
||||
// [2,3,4,5,1]
|
||||
}
|
||||
|
||||
func ExampleList_PushBackList() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
other := glist.NewFrom(g.Slice{6, 7, 8, 9, 10})
|
||||
|
||||
fmt.Println(other.Size())
|
||||
fmt.Println(other)
|
||||
|
||||
l.PushBackList(other)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [6,7,8,9,10]
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleList_PushFrontList() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
other := glist.NewFrom(g.Slice{-4, -3, -2, -1, 0})
|
||||
|
||||
fmt.Println(other.Size())
|
||||
fmt.Println(other)
|
||||
|
||||
l.PushFrontList(other)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [-4,-3,-2,-1,0]
|
||||
// 10
|
||||
// [-4,-3,-2,-1,0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleList_InsertAfter() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.InsertAfter(l.Front(), "a")
|
||||
l.InsertAfter(l.Back(), "b")
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 7
|
||||
// [1,a,2,3,4,5,b]
|
||||
}
|
||||
|
||||
func ExampleList_InsertBefore() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.InsertBefore(l.Front(), "a")
|
||||
l.InsertBefore(l.Back(), "b")
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 7
|
||||
// [a,1,2,3,4,b,5]
|
||||
}
|
||||
|
||||
func ExampleList_Remove() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
fmt.Println(l.Remove(l.Front()))
|
||||
fmt.Println(l.Remove(l.Back()))
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
// 5
|
||||
// 3
|
||||
// [2,3,4]
|
||||
}
|
||||
|
||||
func ExampleList_Removes() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.Removes([]*glist.Element{l.Front(), l.Back()})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 3
|
||||
// [2,3,4]
|
||||
}
|
||||
|
||||
func ExampleList_RemoveAll() {
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.RemoveAll()
|
||||
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleList_RLockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head.
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
fmt.Print(e.Value)
|
||||
}
|
||||
}
|
||||
})
|
||||
fmt.Println()
|
||||
// iterate reading from tail.
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
fmt.Print(e.Value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println()
|
||||
// Output:
|
||||
// 12345678910
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
func ExampleList_IteratorAsc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head using IteratorAsc.
|
||||
l.IteratorAsc(func(e *glist.Element) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 12345678910
|
||||
}
|
||||
|
||||
func ExampleList_IteratorDesc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from tail using IteratorDesc.
|
||||
l.IteratorDesc(func(e *glist.Element) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
// Output:
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
func ExampleList_LockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate writing from head.
|
||||
l.LockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
if e.Value == 6 {
|
||||
e.Value = "M"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5,M,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleList_Join() {
|
||||
var l glist.List
|
||||
l.PushBacks(g.Slice{"a", "b", "c", "d"})
|
||||
|
||||
fmt.Println(l.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
757
container/glist/glist_z_unit_test.go
Normal file
757
container/glist/glist_z_unit_test.go
Normal file
@ -0,0 +1,757 @@
|
||||
// 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 glist
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
if n := l.Len(); n != len {
|
||||
t.Errorf("l.Len() = %d, want %d", n, len)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkListPointers(t *gtest.T, l *List, es []*Element) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
for i, e := 0, l.list.Front(); i < list.Len(); i, e = i+1, e.Next() {
|
||||
if e.Prev() != es[i].Prev() {
|
||||
t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev())
|
||||
}
|
||||
if e.Next() != es[i].Next() {
|
||||
t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
var l List
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
l := New()
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
checkListPointers(t, l, []*Element{})
|
||||
|
||||
// Single element list
|
||||
e := l.PushFront("a")
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToFront(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToBack(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.Remove(e)
|
||||
checkListPointers(t, l, []*Element{})
|
||||
|
||||
// Bigger list
|
||||
e2 := l.PushFront(2)
|
||||
e1 := l.PushFront(1)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack("banana")
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.Remove(e2)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e4})
|
||||
|
||||
l.MoveToFront(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToFront(e1)
|
||||
l.MoveToBack(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
l.MoveToFront(e3) // move from back
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
l.MoveToFront(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToBack(e3) // move from front
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
l.MoveToBack(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
e2 = l.InsertBefore(e1, 2) // insert before front
|
||||
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e4, 2) // insert before middle
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e3, 2) // insert before back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
|
||||
e2 = l.InsertAfter(e1, 2) // insert after front
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e4, 2) // insert after middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e3, 2) // insert after back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
|
||||
l.Remove(e2)
|
||||
|
||||
// Check standard iteration.
|
||||
sum := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
if i, ok := e.Value.(int); ok {
|
||||
sum += i
|
||||
}
|
||||
}
|
||||
if sum != 4 {
|
||||
t.Errorf("sum over l = %d, want 4", sum)
|
||||
}
|
||||
|
||||
// Clear all elements by iterating
|
||||
var next *Element
|
||||
for e := l.Front(); e != nil; e = next {
|
||||
next = e.Next()
|
||||
l.Remove(e)
|
||||
}
|
||||
checkListPointers(t, l, []*Element{})
|
||||
})
|
||||
}
|
||||
|
||||
func checkList(t *gtest.T, l *List, es []interface{}) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
|
||||
switch e.Value.(type) {
|
||||
case int:
|
||||
if le := e.Value.(int); le != es[i] {
|
||||
t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
|
||||
}
|
||||
// default string
|
||||
default:
|
||||
if le := e.Value.(string); le != es[i] {
|
||||
t.Errorf("elt[%v].Value() = %v, want %v", i, le, es[i])
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// le := e.Value.(int)
|
||||
// if le != es[i] {
|
||||
// t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
|
||||
// }
|
||||
// i++
|
||||
// }
|
||||
}
|
||||
|
||||
func TestExtending(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := New()
|
||||
l2 := New()
|
||||
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
l1.PushBack(3)
|
||||
|
||||
l2.PushBack(4)
|
||||
l2.PushBack(5)
|
||||
|
||||
l3 := New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l2)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
|
||||
l3 = New()
|
||||
l3.PushFrontList(l2)
|
||||
checkList(t, l3, []interface{}{4, 5})
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
checkList(t, l2, []interface{}{4, 5})
|
||||
|
||||
l3 = New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushFrontList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = New()
|
||||
l1.PushBackList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
l1.PushFrontList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
checkListPointers(t, l, []*Element{e1, e2})
|
||||
// e := l.Front()
|
||||
// l.Remove(e)
|
||||
// checkListPointers(t, l, []*Element{e2})
|
||||
// l.Remove(e)
|
||||
// checkListPointers(t, l, []*Element{e2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue4103(t *testing.T) {
|
||||
l1 := New()
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
|
||||
l2 := New()
|
||||
l2.PushBack(3)
|
||||
l2.PushBack(4)
|
||||
|
||||
e := l1.Front()
|
||||
l2.Remove(e) // l2 should not change because e is not an element of l2
|
||||
if n := l2.Len(); n != 2 {
|
||||
t.Errorf("l2.Len() = %d, want 2", n)
|
||||
}
|
||||
|
||||
l1.InsertBefore(e, 8)
|
||||
if n := l1.Len(); n != 3 {
|
||||
t.Errorf("l1.Len() = %d, want 3", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue6349(t *testing.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
|
||||
e := l.Front()
|
||||
l.Remove(e)
|
||||
if e.Value != 1 {
|
||||
t.Errorf("e.value = %d, want 1", e.Value)
|
||||
}
|
||||
// if e.Next() != nil {
|
||||
// t.Errorf("e.Next() != nil")
|
||||
// }
|
||||
// if e.Prev() != nil {
|
||||
// t.Errorf("e.Prev() != nil")
|
||||
// }
|
||||
}
|
||||
|
||||
func TestMove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack(4)
|
||||
|
||||
l.MoveAfter(e3, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.MoveAfter(e3, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.MoveBefore(e2, e4)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
|
||||
l.MoveBefore(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
|
||||
e1, e2, e3, e4 = e4, e1, e2, e3
|
||||
|
||||
l.MoveAfter(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
e2, e3, e4 = e4, e2, e3
|
||||
|
||||
l.MoveAfter(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
})
|
||||
}
|
||||
|
||||
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
|
||||
func TestZeroList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l1 = New()
|
||||
l1.PushFront(1)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
|
||||
var l2 = New()
|
||||
l2.PushBack(1)
|
||||
checkList(t, l2, []interface{}{1})
|
||||
|
||||
var l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1})
|
||||
|
||||
var l4 = New()
|
||||
l4.PushBackList(l2)
|
||||
checkList(t, l4, []interface{}{1})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
|
||||
func TestInsertBeforeUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertBefore(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
|
||||
func TestInsertAfterUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertAfter(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
|
||||
func TestMoveUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := New()
|
||||
e1 := l1.PushBack(1)
|
||||
|
||||
l2 := New()
|
||||
e2 := l2.PushBack(2)
|
||||
|
||||
l1.MoveAfter(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
|
||||
l1.MoveBefore(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_RemoveAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.RemoveAll()
|
||||
checkList(t, l, []interface{}{})
|
||||
l.PushBack(2)
|
||||
checkList(t, l, []interface{}{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PushFronts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{2, 1})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PushBacks(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopBacks(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
a2 := []interface{}{"a", "c", "b", "e"}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBacks(2)
|
||||
t.Assert(i1, []interface{}{1, 2})
|
||||
|
||||
l.PushBacks(a2) // 4.3,a,c,b,e
|
||||
i1 = l.PopBacks(3)
|
||||
t.Assert(i1, []interface{}{"e", "b", "c"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopFronts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFronts(2)
|
||||
t.Assert(i1, []interface{}{4, 3})
|
||||
t.Assert(l.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopBackAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBackAll()
|
||||
t.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopFrontAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFrontAll()
|
||||
t.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_FrontAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontAll()
|
||||
t.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_BackAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackAll()
|
||||
t.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_FrontValue(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontValue()
|
||||
t.Assert(gconv.Int(i1), 4)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_BackValue(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackValue()
|
||||
t.Assert(gconv.Int(i1), 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Back(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
t.Assert(e1.Value, 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Size(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
t.Assert(l.Size(), 4)
|
||||
l.PopFront()
|
||||
t.Assert(l.Size(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Removes(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
l.Removes([]*Element{e1})
|
||||
t.Assert(l.Len(), 3)
|
||||
|
||||
e2 := l.Back()
|
||||
l.Removes([]*Element{e2})
|
||||
t.Assert(l.Len(), 2)
|
||||
checkList(t, l, []interface{}{4, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
t.Assert(l.PopBack(), 9)
|
||||
t.Assert(l.PopBacks(2), []interface{}{8, 7})
|
||||
t.Assert(l.PopFront(), 1)
|
||||
t.Assert(l.PopFronts(2), []interface{}{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
l.Clear()
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_IteratorAsc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 5, 6, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
l.IteratorAsc(fun1)
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_IteratorDesc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 6 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
l.IteratorDesc(fun1)
|
||||
t.Assert(l.Len(), 4)
|
||||
checkList(t, l, []interface{}{4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{"a", "b", "c", "d", "e"}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.String(e1.Value) > "c" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
l.Iterator(fun1)
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.Join(","), `1,2,a,"b",\c`)
|
||||
t.Assert(l.Join("."), `1.2.a."b".\c`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.String(), `[1,2,a,"b",\c]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
l := New()
|
||||
l.PushBacks(a)
|
||||
b1, err1 := json.Marshal(l)
|
||||
b2, err2 := json.Marshal(a)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
l := New()
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, l)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l List
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, &l)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_UnmarshalValue(t *testing.T) {
|
||||
type TList struct {
|
||||
Name string
|
||||
List *List
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *TList
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"list": []byte(`[1,2,3]`),
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *TList
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"list": []interface{}{1, 2, 3},
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
45
container/gmap/gmap.go
Normal file
45
container/gmap/gmap.go
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
|
||||
package gmap
|
||||
|
||||
type (
|
||||
Map = AnyAnyMap // Map is alias of AnyAnyMap.
|
||||
HashMap = AnyAnyMap // HashMap is alias of AnyAnyMap.
|
||||
)
|
||||
|
||||
// New creates and returns an empty hash map.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(safe ...bool) *Map {
|
||||
return NewAnyAnyMap(safe...)
|
||||
}
|
||||
|
||||
// NewFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewFrom(data map[interface{}]interface{}, safe ...bool) *Map {
|
||||
return NewAnyAnyMapFrom(data, safe...)
|
||||
}
|
||||
|
||||
// NewHashMap creates and returns an empty hash map.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewHashMap(safe ...bool) *Map {
|
||||
return NewAnyAnyMap(safe...)
|
||||
}
|
||||
|
||||
// NewHashMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewHashMapFrom(data map[interface{}]interface{}, safe ...bool) *Map {
|
||||
return NewAnyAnyMapFrom(data, safe...)
|
||||
}
|
||||
496
container/gmap/gmap_hash_any_any_map.go
Normal file
496
container/gmap/gmap_hash_any_any_map.go
Normal file
@ -0,0 +1,496 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type AnyAnyMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// NewAnyAnyMap creates and returns an empty hash map.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAnyAnyMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *AnyAnyMap) Clone(safe ...bool) *AnyAnyMap {
|
||||
return NewFrom(m.MapCopy(), safe...)
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *AnyAnyMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *AnyAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *AnyAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *AnyAnyMap) Pop() (key, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[interface{}]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if `value` is type of `func() interface {}`,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with `key`.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a Var with the value by given `key`.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a Var with result from GetOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *AnyAnyMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
keys = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *AnyAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *AnyAnyMap) Contains(key interface{}) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *AnyAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *AnyAnyMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *AnyAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *AnyAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *AnyAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.UnmarshalUseNumber(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
498
container/gmap/gmap_hash_int_any_map.go
Normal file
498
container/gmap/gmap_hash_int_any_map.go
Normal file
@ -0,0 +1,498 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IntAnyMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]interface{}
|
||||
}
|
||||
|
||||
// NewIntAnyMap returns an empty IntAnyMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntAnyMap(safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntAnyMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntAnyMap) Clone() *IntAnyMap {
|
||||
return NewIntAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *IntAnyMap) Map() map[int]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *IntAnyMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *IntAnyMap) MapCopy() map[int]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *IntAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *IntAnyMap) Get(key int) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntAnyMap) Pop() (key int, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if `value` is type of `func() interface {}`,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with `key`.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a Var with the value by given `key`.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntAnyMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *IntAnyMap) Remove(key int) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntAnyMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *IntAnyMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntAnyMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *IntAnyMap) Replace(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.Int(v)] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
469
container/gmap/gmap_hash_int_int_map.go
Normal file
469
container/gmap/gmap_hash_int_int_map.go
Normal file
@ -0,0 +1,469 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]int
|
||||
}
|
||||
|
||||
// NewIntIntMap returns an empty IntIntMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntIntMap(safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]int),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *IntIntMap) Map() map[int]int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *IntIntMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *IntIntMap) MapCopy() map[int]int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntIntMap) Set(key int, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntIntMap) Sets(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *IntIntMap) Search(key int) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *IntIntMap) Get(key int) (value int) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntIntMap) Pop() (key, value int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *IntIntMap) GetOrSet(key int, value int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntIntMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *IntIntMap) Remove(key int) (value int) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntIntMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *IntIntMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntIntMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntIntMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntIntMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]int)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *IntIntMap) Replace(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntIntMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntIntMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.Int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
469
container/gmap/gmap_hash_int_str_map.go
Normal file
469
container/gmap/gmap_hash_int_str_map.go
Normal file
@ -0,0 +1,469 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IntStrMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]string
|
||||
}
|
||||
|
||||
// NewIntStrMap returns an empty IntStrMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntStrMap(safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]string),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntStrMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntStrMap) Clone() *IntStrMap {
|
||||
return NewIntStrMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *IntStrMap) Map() map[int]string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *IntStrMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *IntStrMap) MapCopy() map[int]string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntStrMap) Set(key int, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntStrMap) Sets(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *IntStrMap) Search(key int) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *IntStrMap) Get(key int) (value string) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntStrMap) Pop() (key int, value string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *IntStrMap) GetOrSet(key int, value string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntStrMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *IntStrMap) Remove(key int) (value string) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntStrMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *IntStrMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntStrMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntStrMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntStrMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]string)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *IntStrMap) Replace(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *IntStrMap) RLockFunc(f func(m map[int]string)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntStrMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.String(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
484
container/gmap/gmap_hash_str_any_map.go
Normal file
484
container/gmap/gmap_hash_str_any_map.go
Normal file
@ -0,0 +1,484 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type StrAnyMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
// NewStrAnyMap returns an empty StrAnyMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrAnyMap(safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrAnyMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrAnyMap) Clone() *StrAnyMap {
|
||||
return NewStrAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *StrAnyMap) Map() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *StrAnyMap) MapStrAny() map[string]interface{} {
|
||||
return m.Map()
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *StrAnyMap) MapCopy() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *StrAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *StrAnyMap) Get(key string) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrAnyMap) Pop() (key string, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if `value` is type of `func() interface {}`,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with `key`.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a Var with the value by given `key`.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrAnyMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *StrAnyMap) Remove(key string) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrAnyMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *StrAnyMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrAnyMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *StrAnyMap) Replace(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.String(v)] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.Map(value)
|
||||
return
|
||||
}
|
||||
473
container/gmap/gmap_hash_str_int_map.go
Normal file
473
container/gmap/gmap_hash_str_int_map.go
Normal file
@ -0,0 +1,473 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type StrIntMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]int
|
||||
}
|
||||
|
||||
// NewStrIntMap returns an empty StrIntMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrIntMap(safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrIntMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrIntMap) Clone() *StrIntMap {
|
||||
return NewStrIntMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *StrIntMap) Map() map[string]int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *StrIntMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *StrIntMap) MapCopy() map[string]int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrIntMap) Set(key string, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrIntMap) Sets(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *StrIntMap) Search(key string) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *StrIntMap) Get(key string) (value int) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrIntMap) Pop() (key string, value int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *StrIntMap) GetOrSet(key string, value int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrIntMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *StrIntMap) Remove(key string) (value int) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrIntMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *StrIntMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrIntMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrIntMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrIntMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]int)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *StrIntMap) Replace(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *StrIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrIntMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrIntMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = gconv.Int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
462
container/gmap/gmap_hash_str_str_map.go
Normal file
462
container/gmap/gmap_hash_str_str_map.go
Normal file
@ -0,0 +1,462 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type StrStrMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
// NewStrStrMap returns an empty StrStrMap object.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrStrMap(safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
data: make(map[string]string),
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrStrMapFrom creates and returns a hash map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map readonly with custom callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrStrMap) Clone() *StrStrMap {
|
||||
return NewStrStrMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (m *StrStrMap) Map() map[string]string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if !m.mu.IsSafe() {
|
||||
return m.data
|
||||
}
|
||||
data := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *StrStrMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
func (m *StrStrMap) MapCopy() map[string]string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrStrMap) Set(key string, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrStrMap) Sets(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *StrStrMap) Search(key string) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *StrStrMap) Get(key string) (value string) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrStrMap) Pop() (key, value string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *StrStrMap) GetOrSet(key string, value string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrStrMap) SetIfNotExist(key string, value string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the hash map.
|
||||
func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrStrMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *StrStrMap) Remove(key string) (value string) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrStrMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *StrStrMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrStrMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrStrMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrStrMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]string)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *StrStrMap) Replace(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (m *StrStrMap) RLockFunc(f func(m map[string]string)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrStrMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrStrMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.MapStrStr(value)
|
||||
return
|
||||
}
|
||||
589
container/gmap/gmap_list_map.go
Normal file
589
container/gmap/gmap_list_map.go
Normal file
@ -0,0 +1,589 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// ListMap is a map that preserves insertion-order.
|
||||
//
|
||||
// It is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// Reference: http://en.wikipedia.org/wiki/Associative_array
|
||||
type ListMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
list *glist.List
|
||||
}
|
||||
|
||||
type gListMapNode struct {
|
||||
key interface{}
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewListMap returns an empty link map.
|
||||
// ListMap is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
// The parameter `safe` is used to specify whether using map in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewListMap(safe ...bool) *ListMap {
|
||||
return &ListMap{
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]*glist.Element),
|
||||
list: glist.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewListMapFrom returns a link map from given map `data`.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewListMapFrom(data map[interface{}]interface{}, safe ...bool) *ListMap {
|
||||
m := NewListMap(safe...)
|
||||
m.Sets(data)
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
|
||||
m.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the map readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
var node *gListMapNode
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the map readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
var node *gListMapNode
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new link map with copy of current map data.
|
||||
func (m *ListMap) Clone(safe ...bool) *ListMap {
|
||||
return NewListMapFrom(m.Map(), safe...)
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *ListMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given `data`.
|
||||
func (m *ListMap) Replace(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Map returns a copy of the underlying data of the map.
|
||||
func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
var node *gListMapNode
|
||||
var data map[interface{}]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
var node *gListMapNode
|
||||
var data map[string]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[string]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[gconv.String(node.key)] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
func (m *ListMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
if m.list != nil {
|
||||
var (
|
||||
keys = make([]interface{}, 0)
|
||||
node *gListMapNode
|
||||
)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
keys = append(keys, node.key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Set sets key-value to the map.
|
||||
func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the map.
|
||||
func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given `key`.
|
||||
// Second return parameter `found` is true if key was found, otherwise false.
|
||||
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given `key`.
|
||||
func (m *ListMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *ListMap) Pop() (key, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, e := range m.data {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, k)
|
||||
m.list.Remove(e)
|
||||
return k, value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes `size` items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *ListMap) Pops(size int) map[interface{}]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[interface{}]interface{}, size)
|
||||
for k, e := range m.data {
|
||||
value := e.Value.(*gListMapNode).value
|
||||
delete(m.data, k)
|
||||
m.list.Remove(e)
|
||||
newMap[k] = value
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given `key`,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if `value` is type of `func() interface {}`,
|
||||
// it will be executed with mutex.Lock of the map,
|
||||
// and its return value will be set to the map with `key`.
|
||||
//
|
||||
// It returns value with given `key`.
|
||||
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; ok {
|
||||
return e.Value.(*gListMapNode).value
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or sets value with given `value` if it does not exist and then returns this value.
|
||||
func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with returned value of callback function `f` if it does not exist
|
||||
// and then returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
|
||||
// with mutex.Lock of the map.
|
||||
func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a Var with the value by given `key`.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
|
||||
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
|
||||
// It returns false if `key` exists, and `value` would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function `f` with mutex.Lock of the map.
|
||||
func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given `key`, and return this deleted value.
|
||||
func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *ListMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice in ascending order.
|
||||
func (m *ListMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
keys = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *ListMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
var (
|
||||
values = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the `key` exists, or else false.
|
||||
func (m *ListMap) Contains(key interface{}) (ok bool) {
|
||||
m.mu.RLock()
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *ListMap) Size() (size int) {
|
||||
m.mu.RLock()
|
||||
size = len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *ListMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *ListMap) Flip() {
|
||||
data := m.Map()
|
||||
m.Clear()
|
||||
for key, value := range data {
|
||||
m.Set(value, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges two link maps.
|
||||
// The `other` map will be merged into the map `m`.
|
||||
func (m *ListMap) Merge(other *ListMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
var node *gListMapNode
|
||||
other.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if e, ok := m.data[node.key]; !ok {
|
||||
m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{node.key, node.value}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *ListMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m ListMap) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if m.data == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
m.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.UnmarshalUseNumber(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
for k, v := range gconv.Map(value) {
|
||||
if e, ok := m.data[k]; !ok {
|
||||
m.data[k] = m.list.PushBack(&gListMapNode{k, v})
|
||||
} else {
|
||||
e.Value = &gListMapNode{k, v}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
30
container/gmap/gmap_tree_map.go
Normal file
30
container/gmap/gmap_tree_map.go
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gtree"
|
||||
)
|
||||
|
||||
// TreeMap based on red-black tree, alias of RedBlackTree.
|
||||
type TreeMap = gtree.RedBlackTree
|
||||
|
||||
// NewTreeMap instantiates a tree map with the custom comparator.
|
||||
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewTreeMap(comparator func(v1, v2 interface{}) int, safe ...bool) *TreeMap {
|
||||
return gtree.NewRedBlackTree(comparator, safe...)
|
||||
}
|
||||
|
||||
// NewTreeMapFrom instantiates a tree map with the custom comparator and `data` map.
|
||||
// Note that, the param `data` map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
// The parameter `safe` is used to specify whether using tree in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *TreeMap {
|
||||
return gtree.NewRedBlackTreeFrom(comparator, data, safe...)
|
||||
}
|
||||
187
container/gmap/gmap_z_basic_test.go
Normal file
187
container/gmap/gmap_z_basic_test.go
Normal file
@ -0,0 +1,187 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
|
||||
func Test_Map_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.Map
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "11")
|
||||
t.Assert(m.Get(1), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrStrMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrIntMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.ListMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.TreeMap
|
||||
m.SetComparator(gutil.ComparatorString)
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_Map_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
t.Assert(m.Get("funlock"), 3)
|
||||
t.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
t.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
})
|
||||
}
|
||||
func Test_Map_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
m := gmap.NewFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN("key1", m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_Map_Basic_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
})
|
||||
}
|
||||
80
container/gmap/gmap_z_bench_maps_test.go
Normal file
80
container/gmap/gmap_z_bench_maps_test.go
Normal file
@ -0,0 +1,80 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
var hashMap = gmap.New(true)
|
||||
var listMap = gmap.NewListMap(true)
|
||||
var treeMap = gmap.NewTreeMap(gutil.ComparatorInt, true)
|
||||
|
||||
func Benchmark_HashMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
hashMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
listMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
treeMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_HashMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
hashMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
listMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
treeMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
170
container/gmap/gmap_z_bench_safe_test.go
Normal file
170
container/gmap/gmap_z_bench_safe_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
var anyAnyMap = gmap.NewAnyAnyMap(true)
|
||||
var intIntMap = gmap.NewIntIntMap(true)
|
||||
var intAnyMap = gmap.NewIntAnyMap(true)
|
||||
var intStrMap = gmap.NewIntStrMap(true)
|
||||
var strIntMap = gmap.NewStrIntMap(true)
|
||||
var strAnyMap = gmap.NewStrAnyMap(true)
|
||||
var strStrMap = gmap.NewStrStrMap(true)
|
||||
|
||||
func Benchmark_IntIntMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intIntMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntAnyMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intAnyMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntStrMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intStrMap.Set(i, "123456789")
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_AnyAnyMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anyAnyMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrIntMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strIntMap.Set(strconv.Itoa(i), i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrAnyMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strAnyMap.Set(strconv.Itoa(i), i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrStrMap_Set(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strStrMap.Set(strconv.Itoa(i), "123456789")
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntIntMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intIntMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntAnyMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intAnyMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntStrMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intStrMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_AnyAnyMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anyAnyMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrIntMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strIntMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrAnyMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strAnyMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrStrMap_Get(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strStrMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
79
container/gmap/gmap_z_bench_syncmap_test.go
Normal file
79
container/gmap/gmap_z_bench_syncmap_test.go
Normal file
@ -0,0 +1,79 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
var gm = gmap.NewIntIntMap(true)
|
||||
var sm = sync.Map{}
|
||||
|
||||
func Benchmark_GMapSet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_SyncMapSet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Store(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_GMapGet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_SyncMapGet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Load(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_GMapRemove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Remove(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_SyncMapRmove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Delete(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
112
container/gmap/gmap_z_bench_unsafe_test.go
Normal file
112
container/gmap/gmap_z_bench_unsafe_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
var anyAnyMapUnsafe = gmap.New()
|
||||
var intIntMapUnsafe = gmap.NewIntIntMap()
|
||||
var intAnyMapUnsafe = gmap.NewIntAnyMap()
|
||||
var intStrMapUnsafe = gmap.NewIntStrMap()
|
||||
var strIntMapUnsafe = gmap.NewStrIntMap()
|
||||
var strAnyMapUnsafe = gmap.NewStrAnyMap()
|
||||
var strStrMapUnsafe = gmap.NewStrStrMap()
|
||||
|
||||
// Writing benchmarks.
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intIntMapUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intAnyMapUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intStrMapUnsafe.Set(i, strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_AnyAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
anyAnyMapUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strIntMapUnsafe.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strAnyMapUnsafe.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strStrMapUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
// Reading benchmarks.
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intIntMapUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intAnyMapUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intStrMapUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_AnyAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
anyAnyMapUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strIntMapUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strAnyMapUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StrStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strStrMapUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
670
container/gmap/gmap_z_example_any_any_test.go
Normal file
670
container/gmap/gmap_z_example_any_any_test.go
Normal file
@ -0,0 +1,670 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func ExampleAnyAnyMap_Iterator() {
|
||||
m := gmap.New()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
totalKey += k.(int)
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// May Output:
|
||||
// totalKey: 11
|
||||
// totalValue: 22
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Clone() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set("key1", "val2")
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.New(true)
|
||||
m2.Set("key1", "val1")
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set("key1", "val2")
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"key1":"val1"}
|
||||
// before n1: map[key1:val1]
|
||||
// after n1: map[key1:val2]
|
||||
// m2: {"key1":"val1"}
|
||||
// before n2: map[key1:val1]
|
||||
// after n2: map[key1:val1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_MapCopy() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2"}
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_MapStrAny() {
|
||||
m := gmap.New()
|
||||
m.Set(1001, "val1")
|
||||
m.Set(1002, "val2")
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"1001":"val1", "1002":"val2"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_FilterEmpty() {
|
||||
m := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k4:1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_FilterNil() {
|
||||
m := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterNil()
|
||||
fmt.Printf("%#v", m.Map())
|
||||
|
||||
// Output:
|
||||
// map[interface {}]interface {}{"k1":"", "k3":0, "k4":1}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Set() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Sets() {
|
||||
m := gmap.New()
|
||||
|
||||
addMap := make(map[interface{}]interface{})
|
||||
addMap["key1"] = "val1"
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Search() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
value, found := m.Search("key1")
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search("key2")
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: val1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Get() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println("key1 value:", m.Get("key1"))
|
||||
fmt.Println("key2 value:", m.Get("key2"))
|
||||
|
||||
// Output:
|
||||
// key1 value: val1
|
||||
// key2 value: <nil>
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Pop() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Pops() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
// size: 0
|
||||
// map[k1:v1 k2:v2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetOrSet() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetOrSetFunc() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetOrSetFuncLock() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetVar() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVar("key1"))
|
||||
fmt.Println(m.GetVar("key2").IsNil())
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetVarOrSet() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetVarOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetVarOrSetFunc() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_GetVarOrSetFuncLock() {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_SetIfNotExist() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v2"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_SetIfNotExistFunc() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Remove() {
|
||||
var m gmap.Map
|
||||
m.Set("k1", "v1")
|
||||
|
||||
fmt.Println(m.Remove("k1"))
|
||||
fmt.Println(m.Remove("k2"))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// v1
|
||||
// <nil>
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Removes() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
removeList := make([]interface{}, 2)
|
||||
removeList = append(removeList, "k1")
|
||||
removeList = append(removeList, "k2")
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Keys() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Values() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [v1 v2 v3 v4]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Contains() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains("k1"))
|
||||
fmt.Println(m.Contains("k5"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Size() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_IsEmpty() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set("k1", "v1")
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Clear() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Replace() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
var n gmap.Map
|
||||
n.Sets(g.MapAnyAny{
|
||||
"k2": "v2",
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set("k2", "v1")
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k1:v1]
|
||||
// map[k2:v2]
|
||||
// map[k2:v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_LockFunc() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_RLockFunc() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Flip() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[v1:k1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Merge() {
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_String() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_MarshalJSON() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_UnmarshalJSON() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
var n gmap.Map
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_UnmarshalValue() {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
Pass1 string `gconv:"password1"`
|
||||
Pass2 string `gconv:"password2"`
|
||||
}
|
||||
|
||||
var (
|
||||
m gmap.AnyAnyMap
|
||||
user = User{
|
||||
Uid: 1,
|
||||
Name: "john",
|
||||
Pass1: "123",
|
||||
Pass2: "456",
|
||||
}
|
||||
)
|
||||
if err := gconv.Scan(user, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[interface {}]interface {}{"Name":"john", "Uid":1, "password1":"123", "password2":"456"}
|
||||
}
|
||||
661
container/gmap/gmap_z_example_int_any_test.go
Normal file
661
container/gmap/gmap_z_example_int_any_test.go
Normal file
@ -0,0 +1,661 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func ExampleIntAnyMap_Iterator() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
totalKey += k
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// May Output:
|
||||
// totalKey: 11
|
||||
// totalValue: 22
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Clone() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1"}
|
||||
// {"1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.NewIntAnyMap()
|
||||
m1.Set(1, "val1")
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set(1, "val2")
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.New(true)
|
||||
m2.Set(1, "val1")
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set(1, "val2")
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"1":"val1"}
|
||||
// before n1: map[1:val1]
|
||||
// after n1: map[1:val2]
|
||||
// m2: {"1":"val1"}
|
||||
// before n2: map[1:val1]
|
||||
// after n2: map[1:val1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_MapCopy() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
m.Set(2, "val2")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1","2":"val2"}
|
||||
// map[1:val1 2:val2]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_MapStrAny() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1001, "val1")
|
||||
m.Set(1002, "val2")
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"1001":"val1", "1002":"val2"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_FilterEmpty() {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "",
|
||||
2: nil,
|
||||
3: 0,
|
||||
4: 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[4:1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_FilterNil() {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "",
|
||||
2: nil,
|
||||
3: 0,
|
||||
4: 1,
|
||||
})
|
||||
m.FilterNil()
|
||||
fmt.Printf("%#v", m.Map())
|
||||
|
||||
// Output:
|
||||
// map[int]interface {}{1:"", 3:0, 4:1}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Set() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Sets() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
addMap := make(map[int]interface{})
|
||||
addMap[1] = "val1"
|
||||
addMap[2] = "val2"
|
||||
addMap[3] = "val3"
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1","2":"val2","3":"val3"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Search() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
|
||||
value, found := m.Search(1)
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search(2)
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: val1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Get() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println("key1 value:", m.Get(1))
|
||||
fmt.Println("key2 value:", m.Get(2))
|
||||
|
||||
// Output:
|
||||
// key1 value: val1
|
||||
// key2 value: <nil>
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Pop() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// 1 v1
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Pops() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[1:v1 2:v2 3:v3 4:v4]
|
||||
// size: 0
|
||||
// map[1:v1 2:v2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetOrSet() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetOrSet(1, "NotExistValue"))
|
||||
fmt.Println(m.GetOrSet(2, "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetOrSetFunc() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFunc(1, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc(2, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock(1, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock(2, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetVar() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetVar(1))
|
||||
fmt.Println(m.GetVar(2).IsNil())
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetVarOrSet() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSet(1, "NotExistValue"))
|
||||
fmt.Println(m.GetVarOrSet(2, "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetVarOrSetFunc() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFunc(1, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFunc(2, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_GetVarOrSetFuncLock() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFuncLock(1, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFuncLock(2, func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_SetIfNotExist() {
|
||||
var m gmap.IntAnyMap
|
||||
fmt.Println(m.SetIfNotExist(1, "v1"))
|
||||
fmt.Println(m.SetIfNotExist(1, "v2"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:v1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_SetIfNotExistFunc() {
|
||||
var m gmap.IntAnyMap
|
||||
fmt.Println(m.SetIfNotExistFunc(1, func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc(1, func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:v1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.IntAnyMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock(1, func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock(1, func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:v1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Remove() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, "v1")
|
||||
|
||||
fmt.Println(m.Remove(1))
|
||||
fmt.Println(m.Remove(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// v1
|
||||
// <nil>
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Removes() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
removeList := make([]int, 2)
|
||||
removeList = append(removeList, 1)
|
||||
removeList = append(removeList, 2)
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[3:v3 4:v4]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Keys() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [1 2 3 4]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Values() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [v1 v2 v3 v4]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Contains() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains(1))
|
||||
fmt.Println(m.Contains(5))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Size() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_IsEmpty() {
|
||||
var m gmap.IntAnyMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set(1, "v1")
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Clear() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Replace() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
})
|
||||
|
||||
var n gmap.IntAnyMap
|
||||
n.Sets(g.MapIntAny{
|
||||
2: "v2",
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set(2, "v1")
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[1:v1]
|
||||
// map[2:v2]
|
||||
// map[2:v1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_LockFunc() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[int]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_RLockFunc() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[int]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Flip() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: 10,
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[10:1]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_Merge() {
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set(1, "val1")
|
||||
m2.Set(2, "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_String() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"1":"v1"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_MarshalJSON() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"1":"v1","2":"v2","3":"v3","4":"v4"}
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_UnmarshalJSON() {
|
||||
var m gmap.IntAnyMap
|
||||
m.Sets(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
4: "v4",
|
||||
})
|
||||
|
||||
var n gmap.Map
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[1:v1 2:v2 3:v3 4:v4]
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_UnmarshalValue() {
|
||||
var m gmap.IntAnyMap
|
||||
|
||||
goWeb := map[int]interface{}{
|
||||
1: "goframe",
|
||||
2: "gin",
|
||||
3: "echo",
|
||||
}
|
||||
|
||||
if err := gconv.Scan(goWeb, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[int]interface {}{1:"goframe", 2:"gin", 3:"echo"}
|
||||
}
|
||||
588
container/gmap/gmap_z_example_int_int_test.go
Normal file
588
container/gmap/gmap_z_example_int_int_test.go
Normal file
@ -0,0 +1,588 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func ExampleIntIntMap_Iterator() {
|
||||
m := gmap.NewIntIntMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
totalKey += k
|
||||
totalValue += v
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// May Output:
|
||||
// totalKey: 11
|
||||
// totalValue: 22
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Clone() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
// {"1":1}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.NewIntIntMap()
|
||||
m1.Set(1, 1)
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set(1, 2)
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.New(true)
|
||||
m2.Set(1, "1")
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set(1, "2")
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"1":1}
|
||||
// before n1: map[1:1]
|
||||
// after n1: map[1:2]
|
||||
// m2: {"1":"1"}
|
||||
// before n2: map[1:1]
|
||||
// after n2: map[1:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_MapCopy() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
m.Set(2, 2)
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":1,"2":2}
|
||||
// map[1:1 2:2]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_MapStrAny() {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1001, 1)
|
||||
m.Set(1002, 2)
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"1001":1, "1002":2}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_FilterEmpty() {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 0,
|
||||
2: 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[2:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Set() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Sets() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
addMap := make(map[int]int)
|
||||
addMap[1] = 1
|
||||
addMap[2] = 12
|
||||
addMap[3] = 123
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":1,"2":12,"3":123}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Search() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
|
||||
value, found := m.Search(1)
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search(2)
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: 1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Get() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
|
||||
fmt.Println("key1 value:", m.Get(1))
|
||||
fmt.Println("key2 value:", m.Get(2))
|
||||
|
||||
// Output:
|
||||
// key1 value: 1
|
||||
// key2 value: 0
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Pop() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// 1 1
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Pops() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[1:1 2:2 3:3 4:4]
|
||||
// size: 0
|
||||
// map[1:1 2:2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_GetOrSet() {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
fmt.Println(m.GetOrSet(1, 0))
|
||||
fmt.Println(m.GetOrSet(2, 2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_GetOrSetFunc() {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
fmt.Println(m.GetOrSetFunc(1, func() int {
|
||||
return 0
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc(2, func() int {
|
||||
return 0
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock(1, func() int {
|
||||
return 0
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock(2, func() int {
|
||||
return 0
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_SetIfNotExist() {
|
||||
var m gmap.IntIntMap
|
||||
fmt.Println(m.SetIfNotExist(1, 1))
|
||||
fmt.Println(m.SetIfNotExist(1, 2))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_SetIfNotExistFunc() {
|
||||
var m gmap.IntIntMap
|
||||
fmt.Println(m.SetIfNotExistFunc(1, func() int {
|
||||
return 1
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc(1, func() int {
|
||||
return 2
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.IntIntMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock(1, func() int {
|
||||
return 1
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock(1, func() int {
|
||||
return 2
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[1:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Remove() {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 1)
|
||||
|
||||
fmt.Println(m.Remove(1))
|
||||
fmt.Println(m.Remove(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Removes() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
removeList := make([]int, 2)
|
||||
removeList = append(removeList, 1)
|
||||
removeList = append(removeList, 2)
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[3:3 4:4]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Keys() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [1 2 3 4]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Values() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [1 v2 v3 4]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Contains() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains(1))
|
||||
fmt.Println(m.Contains(5))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Size() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_IsEmpty() {
|
||||
var m gmap.IntIntMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set(1, 1)
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Clear() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Replace() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
})
|
||||
|
||||
var n gmap.IntIntMap
|
||||
n.Sets(g.MapIntInt{
|
||||
2: 2,
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set(2, 1)
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[1:1]
|
||||
// map[2:2]
|
||||
// map[2:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_LockFunc() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[int]int) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_RLockFunc() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[int]int) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Flip() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 10,
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[10:1]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_Merge() {
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set(1, "1")
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:1 key2:2]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_String() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_MarshalJSON() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"1":1,"2":2,"3":3,"4":4}
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_UnmarshalJSON() {
|
||||
var m gmap.IntIntMap
|
||||
m.Sets(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
})
|
||||
|
||||
var n gmap.Map
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[1:1 2:2 3:3 4:4]
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_UnmarshalValue() {
|
||||
var m gmap.IntIntMap
|
||||
|
||||
n := map[int]int{
|
||||
1: 1001,
|
||||
2: 1002,
|
||||
3: 1003,
|
||||
}
|
||||
|
||||
if err := gconv.Scan(n, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
// Output:
|
||||
// map[int]int{1:1001, 2:1002, 3:1003}
|
||||
}
|
||||
623
container/gmap/gmap_z_example_list_test.go
Normal file
623
container/gmap/gmap_z_example_list_test.go
Normal file
@ -0,0 +1,623 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func ExampleListMap_Iterator() {
|
||||
m := gmap.NewListMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
totalKey += k.(int)
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// Output:
|
||||
// totalKey: 10
|
||||
// totalValue: 20
|
||||
}
|
||||
|
||||
func ExampleListMap_IteratorAsc() {
|
||||
m := gmap.NewListMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.IteratorAsc(func(k interface{}, v interface{}) bool {
|
||||
totalKey += k.(int)
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// Output:
|
||||
// totalKey: 10
|
||||
// totalValue: 20
|
||||
}
|
||||
|
||||
func ExampleListMap_IteratorDesc() {
|
||||
m := gmap.NewListMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(i, i*2)
|
||||
}
|
||||
|
||||
var totalKey, totalValue int
|
||||
m.IteratorDesc(func(k interface{}, v interface{}) bool {
|
||||
totalKey += k.(int)
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalKey < 10
|
||||
})
|
||||
|
||||
fmt.Println("totalKey:", totalKey)
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// Output:
|
||||
// totalKey: 17
|
||||
// totalValue: 34
|
||||
}
|
||||
|
||||
func ExampleListMap_Clone() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleListMap_Clear() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleListMap_Replace() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
var n gmap.ListMap
|
||||
n.Sets(g.MapAnyAny{
|
||||
"k2": "v2",
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k1:v1]
|
||||
// map[k2:v2]
|
||||
}
|
||||
|
||||
func ExampleListMap_Map() {
|
||||
m1 := gmap.NewListMap()
|
||||
m1.Set("key1", "val1")
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set("key1", "val2")
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// Output:
|
||||
// m1: {"key1":"val1"}
|
||||
// before n1: map[key1:val1]
|
||||
// after n1: map[key1:val1]
|
||||
}
|
||||
|
||||
func ExampleListMap_MapStrAny() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"key1":"val1", "key2":"val2"}
|
||||
}
|
||||
|
||||
func ExampleListMap_FilterEmpty() {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k4:1]
|
||||
}
|
||||
|
||||
func ExampleListMap_Set() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleListMap_Sets() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
addMap := make(map[interface{}]interface{})
|
||||
addMap["key1"] = "val1"
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// May Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
func ExampleListMap_Search() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
value, found := m.Search("key1")
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search("key2")
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: val1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleListMap_Get() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println("key1 value:", m.Get("key1"))
|
||||
fmt.Println("key2 value:", m.Get("key2"))
|
||||
|
||||
// Output:
|
||||
// key1 value: val1
|
||||
// key2 value: <nil>
|
||||
}
|
||||
|
||||
func ExampleListMap_Pop() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
}
|
||||
|
||||
func ExampleListMap_Pops() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
// size: 0
|
||||
// map[k1:v1 k2:v2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleListMap_GetOrSet() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleListMap_GetOrSetFunc() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleListMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleListMap_GetVar() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVar("key1"))
|
||||
fmt.Println(m.GetVar("key2").IsNil())
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleListMap_GetVarOrSet() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetVarOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleListMap_GetVarOrSetFunc() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleListMap_GetVarOrSetFuncLock() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleListMap_SetIfNotExist() {
|
||||
var m gmap.ListMap
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v2"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleListMap_SetIfNotExistFunc() {
|
||||
var m gmap.ListMap
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleListMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.ListMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleListMap_Remove() {
|
||||
var m gmap.ListMap
|
||||
m.Set("k1", "v1")
|
||||
|
||||
fmt.Println(m.Remove("k1"))
|
||||
fmt.Println(m.Remove("k2"))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// v1
|
||||
// <nil>
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleListMap_Removes() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
removeList := make([]interface{}, 2)
|
||||
removeList = append(removeList, "k1")
|
||||
removeList = append(removeList, "k2")
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleListMap_Keys() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
}
|
||||
|
||||
func ExampleListMap_Values() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [v1 v2 v3 v4]
|
||||
}
|
||||
|
||||
func ExampleListMap_Contains() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains("k1"))
|
||||
fmt.Println(m.Contains("k5"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleListMap_Size() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleListMap_IsEmpty() {
|
||||
var m gmap.ListMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set("k1", "v1")
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleListMap_Flip() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[v1:k1]
|
||||
}
|
||||
|
||||
func ExampleListMap_Merge() {
|
||||
var m1, m2 gmap.ListMap
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleListMap_String() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
}
|
||||
|
||||
func ExampleListMap_MarshalJSON() {
|
||||
var m gmap.ListMap
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
m.Set("k4", "v4")
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
|
||||
}
|
||||
|
||||
func ExampleListMap_UnmarshalJSON() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
var n gmap.ListMap
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleListMap_UnmarshalValue() {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
Pass1 string `gconv:"password1"`
|
||||
Pass2 string `gconv:"password2"`
|
||||
}
|
||||
|
||||
var (
|
||||
m gmap.AnyAnyMap
|
||||
user = User{
|
||||
Uid: 1,
|
||||
Name: "john",
|
||||
Pass1: "123",
|
||||
Pass2: "456",
|
||||
}
|
||||
)
|
||||
if err := gconv.Scan(user, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[interface {}]interface {}{"Name":"john", "Uid":1, "password1":"123", "password2":"456"}
|
||||
}
|
||||
658
container/gmap/gmap_z_example_str_any_test.go
Normal file
658
container/gmap/gmap_z_example_str_any_test.go
Normal file
@ -0,0 +1,658 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func ExampleStrAnyMap_Iterator() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
for i := 1; i <= 10; i++ {
|
||||
m.Set(gconv.String(i), i*2)
|
||||
}
|
||||
|
||||
var totalValue int
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
totalValue += v.(int)
|
||||
|
||||
return totalValue < 50
|
||||
})
|
||||
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// May Output:
|
||||
// totalValue: 52
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Clone() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.NewStrAnyMap()
|
||||
m1.Set("key1", "val1")
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set("key1", "val2")
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.NewStrAnyMap(true)
|
||||
m2.Set("key1", "val1")
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set("key1", "val2")
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"key1":"val1"}
|
||||
// before n1: map[key1:val1]
|
||||
// after n1: map[key1:val2]
|
||||
// m2: {"key1":"val1"}
|
||||
// before n2: map[key1:val1]
|
||||
// after n2: map[key1:val1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_MapCopy() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2"}
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_MapStrAny() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"key1":"val1", "key2":"val2"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_FilterEmpty() {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k4:1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_FilterNil() {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterNil()
|
||||
fmt.Printf("%#v", m.Map())
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"k1":"", "k3":0, "k4":1}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Set() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Sets() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
addMap := make(map[string]interface{})
|
||||
addMap["key1"] = "val1"
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Search() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
value, found := m.Search("key1")
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search("key2")
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: val1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Get() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println("key1 value:", m.Get("key1"))
|
||||
fmt.Println("key2 value:", m.Get("key2"))
|
||||
|
||||
// Output:
|
||||
// key1 value: val1
|
||||
// key2 value: <nil>
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Pop() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Pops() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
// size: 0
|
||||
// map[k1:v1 k2:v2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetOrSet() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetOrSetFunc() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetVar() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVar("key1"))
|
||||
fmt.Println(m.GetVar("key2").IsNil())
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetVarOrSet() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetVarOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetVarOrSetFunc() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFunc("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFunc("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_GetVarOrSetFuncLock() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key1", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetVarOrSetFuncLock("key2", func() interface{} {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_SetIfNotExist() {
|
||||
var m gmap.StrAnyMap
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v2"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_SetIfNotExistFunc() {
|
||||
var m gmap.StrAnyMap
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.StrAnyMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() interface{} {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Remove() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("k1", "v1")
|
||||
|
||||
fmt.Println(m.Remove("k1"))
|
||||
fmt.Println(m.Remove("k2"))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// v1
|
||||
// <nil>
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Removes() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
removeList := make([]string, 2)
|
||||
removeList = append(removeList, "k1")
|
||||
removeList = append(removeList, "k2")
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Keys() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Values() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [v1 v2 v3 v4]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Contains() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains("k1"))
|
||||
fmt.Println(m.Contains("k5"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Size() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_IsEmpty() {
|
||||
var m gmap.StrAnyMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set("k1", "v1")
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Clear() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Replace() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
var n gmap.StrAnyMap
|
||||
n.Sets(g.MapStrAny{
|
||||
"k2": "v2",
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set("k2", "v1")
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k1:v1]
|
||||
// map[k2:v2]
|
||||
// map[k2:v1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_LockFunc() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[string]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_RLockFunc() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[string]interface{}) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v.(int)
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Flip() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[v1:k1]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_Merge() {
|
||||
var m1, m2 gmap.StrAnyMap
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_String() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_MarshalJSON() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_UnmarshalJSON() {
|
||||
var m gmap.StrAnyMap
|
||||
m.Sets(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
var n gmap.StrAnyMap
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_UnmarshalValue() {
|
||||
var m gmap.StrAnyMap
|
||||
|
||||
goWeb := map[string]interface{}{
|
||||
"goframe": "https://goframe.org",
|
||||
"gin": "https://gin-gonic.com/",
|
||||
"echo": "https://echo.labstack.com/",
|
||||
}
|
||||
|
||||
if err := gconv.Scan(goWeb, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
// Output:
|
||||
// map[string]interface {}{"echo":"https://echo.labstack.com/", "gin":"https://gin-gonic.com/", "goframe":"https://goframe.org"}
|
||||
}
|
||||
594
container/gmap/gmap_z_example_str_int_test.go
Normal file
594
container/gmap/gmap_z_example_str_int_test.go
Normal file
@ -0,0 +1,594 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
func ExampleStrIntMap_Iterator() {
|
||||
m := gmap.NewStrIntMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set(gconv.String(i), i*2)
|
||||
}
|
||||
|
||||
var totalValue int
|
||||
m.Iterator(func(k string, v int) bool {
|
||||
totalValue += v
|
||||
|
||||
return totalValue < 50
|
||||
})
|
||||
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
|
||||
// May Output:
|
||||
// totalValue: 52
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Clone() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":1}
|
||||
// {"key1":1}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.NewStrIntMap()
|
||||
m1.Set("key1", 1)
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set("key1", 2)
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.NewStrIntMap(true)
|
||||
m2.Set("key1", 1)
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set("key1", 2)
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"key1":1}
|
||||
// before n1: map[key1:1]
|
||||
// after n1: map[key1:2]
|
||||
// m2: {"key1":1}
|
||||
// before n2: map[key1:1]
|
||||
// after n2: map[key1:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_MapCopy() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
m.Set("key2", 2)
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":1,"key2":2}
|
||||
// map[key1:1 key2:2]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_MapStrAny() {
|
||||
m := gmap.NewStrIntMap()
|
||||
m.Set("key1", 1)
|
||||
m.Set("key2", 2)
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"key1":1, "key2":2}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_FilterEmpty() {
|
||||
m := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"k1": 0,
|
||||
"k2": 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k2:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Set() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":1}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Sets() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
addMap := make(map[string]int)
|
||||
addMap["key1"] = 1
|
||||
addMap["key2"] = 2
|
||||
addMap["key3"] = 3
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":1,"key2":2,"key3":3}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Search() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
|
||||
value, found := m.Search("key1")
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search("key2")
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: 1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Get() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
|
||||
fmt.Println("key1 value:", m.Get("key1"))
|
||||
fmt.Println("key2 value:", m.Get("key2"))
|
||||
|
||||
// Output:
|
||||
// key1 value: 1
|
||||
// key2 value: 0
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Pop() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// k1 1
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Pops() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[k1:1 k2:2 k3:3 k4:4]
|
||||
// size: 0
|
||||
// map[k1:1 k2:2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_GetOrSet() {
|
||||
m := gmap.NewStrIntMap()
|
||||
m.Set("key1", 1)
|
||||
|
||||
fmt.Println(m.GetOrSet("key1", 0))
|
||||
fmt.Println(m.GetOrSet("key2", 2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_GetOrSetFunc() {
|
||||
m := gmap.NewStrIntMap()
|
||||
m.Set("key1", 1)
|
||||
|
||||
fmt.Println(m.GetOrSetFunc("key1", func() int {
|
||||
return 0
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc("key2", func() int {
|
||||
return 0
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewStrIntMap()
|
||||
m.Set("key1", 1)
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock("key1", func() int {
|
||||
return 0
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock("key2", func() int {
|
||||
return 0
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_SetIfNotExist() {
|
||||
var m gmap.StrIntMap
|
||||
fmt.Println(m.SetIfNotExist("k1", 1))
|
||||
fmt.Println(m.SetIfNotExist("k1", 2))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_SetIfNotExistFunc() {
|
||||
var m gmap.StrIntMap
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() int {
|
||||
return 1
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() int {
|
||||
return 2
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.StrIntMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() int {
|
||||
return 1
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() int {
|
||||
return 2
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Remove() {
|
||||
var m gmap.StrIntMap
|
||||
m.Set("k1", 1)
|
||||
|
||||
fmt.Println(m.Remove("k1"))
|
||||
fmt.Println(m.Remove("k2"))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Removes() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
removeList := make([]string, 2)
|
||||
removeList = append(removeList, "k1")
|
||||
removeList = append(removeList, "k2")
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k3:3 k4:4]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Keys() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Values() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [1 2 3 4]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Contains() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains("k1"))
|
||||
fmt.Println(m.Contains("k5"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Size() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_IsEmpty() {
|
||||
var m gmap.StrIntMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set("k1", 1)
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Clear() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Replace() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
})
|
||||
|
||||
var n gmap.StrIntMap
|
||||
n.Sets(g.MapStrInt{
|
||||
"k2": 2,
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set("k2", 1)
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k1:1]
|
||||
// map[k2:2]
|
||||
// map[k2:1]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_LockFunc() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[string]int) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_RLockFunc() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[string]int) {
|
||||
totalValue := 0
|
||||
for _, v := range m {
|
||||
totalValue += v
|
||||
}
|
||||
fmt.Println("totalValue:", totalValue)
|
||||
})
|
||||
|
||||
// Output:
|
||||
// totalValue: 10
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Flip() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
var n gmap.StrIntMap
|
||||
n.Sets(g.MapStrInt{
|
||||
"11": 1,
|
||||
})
|
||||
n.Flip()
|
||||
fmt.Println(n.Map())
|
||||
|
||||
// Output:
|
||||
// map[1:0]
|
||||
// map[1:11]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_Merge() {
|
||||
var m1, m2 gmap.StrIntMap
|
||||
m1.Set("key1", 1)
|
||||
m2.Set("key2", 2)
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:1 key2:2]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_String() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"k1":1}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_MarshalJSON() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"k1":1,"k2":2,"k3":3,"k4":4}
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_UnmarshalJSON() {
|
||||
var m gmap.StrIntMap
|
||||
m.Sets(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
"k3": 3,
|
||||
"k4": 4,
|
||||
})
|
||||
|
||||
var n gmap.StrIntMap
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[k1:1 k2:2 k3:3 k4:4]
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_UnmarshalValue() {
|
||||
var m gmap.StrIntMap
|
||||
|
||||
goWeb := map[string]int{
|
||||
"goframe": 1,
|
||||
"gin": 2,
|
||||
"echo": 3,
|
||||
}
|
||||
|
||||
if err := gconv.Scan(goWeb, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
// Output:
|
||||
// map[string]int{"echo":3, "gin":2, "goframe":1}
|
||||
}
|
||||
590
container/gmap/gmap_z_example_str_str_test.go
Normal file
590
container/gmap/gmap_z_example_str_str_test.go
Normal file
@ -0,0 +1,590 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
func ExampleStrStrMap_Iterator() {
|
||||
m := gmap.NewStrStrMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
m.Set("key"+gconv.String(i), "var"+gconv.String(i))
|
||||
}
|
||||
|
||||
var str string
|
||||
m.Iterator(func(k string, v string) bool {
|
||||
|
||||
str += v + "|"
|
||||
|
||||
return len(str) < 20
|
||||
})
|
||||
|
||||
fmt.Println("str:", str)
|
||||
|
||||
// May Output:
|
||||
// var0|var1|var2|var3|
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Clone() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.Clone()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Map() {
|
||||
// non concurrent-safety, a pointer to the underlying data
|
||||
m1 := gmap.NewStrStrMap()
|
||||
m1.Set("key1", "val1")
|
||||
fmt.Println("m1:", m1)
|
||||
|
||||
n1 := m1.Map()
|
||||
fmt.Println("before n1:", n1)
|
||||
m1.Set("key1", "val2")
|
||||
fmt.Println("after n1:", n1)
|
||||
|
||||
// concurrent-safety, copy of underlying data
|
||||
m2 := gmap.NewStrStrMap(true)
|
||||
m2.Set("key1", "val1")
|
||||
fmt.Println("m2:", m2)
|
||||
|
||||
n2 := m2.Map()
|
||||
fmt.Println("before n2:", n2)
|
||||
m2.Set("key1", "val2")
|
||||
fmt.Println("after n2:", n2)
|
||||
|
||||
// Output:
|
||||
// m1: {"key1":"val1"}
|
||||
// before n1: map[key1:val1]
|
||||
// after n1: map[key1:val2]
|
||||
// m2: {"key1":"val1"}
|
||||
// before n2: map[key1:val1]
|
||||
// after n2: map[key1:val1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_MapCopy() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
fmt.Println(m)
|
||||
|
||||
n := m.MapCopy()
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2"}
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_MapStrAny() {
|
||||
m := gmap.NewStrStrMap()
|
||||
m.Set("key1", "val1")
|
||||
m.Set("key2", "val2")
|
||||
|
||||
n := m.MapStrAny()
|
||||
fmt.Printf("%#v", n)
|
||||
|
||||
// Output:
|
||||
// map[string]interface {}{"key1":"val1", "key2":"val2"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_FilterEmpty() {
|
||||
m := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"k1": "",
|
||||
"k2": "v2",
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k2:v2]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Set() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Sets() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
addMap := make(map[string]string)
|
||||
addMap["key1"] = "val1"
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Search() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
value, found := m.Search("key1")
|
||||
if found {
|
||||
fmt.Println("find key1 value:", value)
|
||||
}
|
||||
|
||||
value, found = m.Search("key2")
|
||||
if !found {
|
||||
fmt.Println("key2 not find")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// find key1 value: val1
|
||||
// key2 not find
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Get() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println("key1 value:", m.Get("key1"))
|
||||
fmt.Println("key2 value:", m.Get("key2"))
|
||||
|
||||
// Output:
|
||||
// key1 value: val1
|
||||
// key2 value:
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Pop() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Pop())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Pops() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(-1))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println("size:", m.Size())
|
||||
|
||||
// May Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
// size: 0
|
||||
// map[k1:v1 k2:v2]
|
||||
// size: 2
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_GetOrSet() {
|
||||
m := gmap.NewStrStrMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSet("key1", "NotExistValue"))
|
||||
fmt.Println(m.GetOrSet("key2", "val2"))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// val2
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_GetOrSetFunc() {
|
||||
m := gmap.NewStrStrMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFunc("key1", func() string {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFunc("key2", func() string {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_GetOrSetFuncLock() {
|
||||
m := gmap.NewStrStrMap()
|
||||
m.Set("key1", "val1")
|
||||
|
||||
fmt.Println(m.GetOrSetFuncLock("key1", func() string {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
fmt.Println(m.GetOrSetFuncLock("key2", func() string {
|
||||
return "NotExistValue"
|
||||
}))
|
||||
|
||||
// Output:
|
||||
// val1
|
||||
// NotExistValue
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_SetIfNotExist() {
|
||||
var m gmap.StrStrMap
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v2"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_SetIfNotExistFunc() {
|
||||
var m gmap.StrStrMap
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() string {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFunc("k1", func() string {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_SetIfNotExistFuncLock() {
|
||||
var m gmap.StrStrMap
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() string {
|
||||
return "v1"
|
||||
}))
|
||||
fmt.Println(m.SetIfNotExistFuncLock("k1", func() string {
|
||||
return "v2"
|
||||
}))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Remove() {
|
||||
var m gmap.StrStrMap
|
||||
m.Set("k1", "v1")
|
||||
|
||||
fmt.Println(m.Remove("k1"))
|
||||
fmt.Println(len(m.Remove("k2")))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// v1
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Removes() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
removeList := make([]string, 2)
|
||||
removeList = append(removeList, "k1")
|
||||
removeList = append(removeList, "k2")
|
||||
|
||||
m.Removes(removeList)
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Keys() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Values() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [v1 v2 v3 v4]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Contains() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Contains("k1"))
|
||||
fmt.Println(m.Contains("k5"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Size() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_IsEmpty() {
|
||||
var m gmap.StrStrMap
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
m.Set("k1", "v1")
|
||||
fmt.Println(m.IsEmpty())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Clear() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Replace() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
var n gmap.StrStrMap
|
||||
n.Sets(g.MapStrStr{
|
||||
"k2": "v2",
|
||||
})
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
m.Replace(n.Map())
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n.Set("k2", "v1")
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[k1:v1]
|
||||
// map[k2:v2]
|
||||
// map[k2:v1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_LockFunc() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.LockFunc(func(m map[string]string) {
|
||||
for k, v := range m {
|
||||
fmt.Println("key:", k, " value:", v)
|
||||
}
|
||||
})
|
||||
|
||||
// May Output:
|
||||
// key: k1 value: v1
|
||||
// key: k2 value: v2
|
||||
// key: k3 value: v3
|
||||
// key: k4 value: v4
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_RLockFunc() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
m.RLockFunc(func(m map[string]string) {
|
||||
for k, v := range m {
|
||||
fmt.Println("key:", k, " value:", v)
|
||||
}
|
||||
})
|
||||
|
||||
// May Output:
|
||||
// key: k1 value: v1
|
||||
// key: k2 value: v2
|
||||
// key: k3 value: v3
|
||||
// key: k4 value: v4
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Flip() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// map[v1:k1]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_Merge() {
|
||||
var m1, m2 gmap.StrStrMap
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_String() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
})
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_MarshalJSON() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
fmt.Println(gconv.String(bytes))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_UnmarshalJSON() {
|
||||
var m gmap.StrStrMap
|
||||
m.Sets(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
|
||||
var n gmap.StrStrMap
|
||||
|
||||
err := json.Unmarshal(gconv.Bytes(m.String()), &n)
|
||||
if err == nil {
|
||||
fmt.Println(n.Map())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[k1:v1 k2:v2 k3:v3 k4:v4]
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_UnmarshalValue() {
|
||||
var m gmap.StrStrMap
|
||||
|
||||
goWeb := map[string]string{
|
||||
"goframe": "https://goframe.org",
|
||||
"gin": "https://gin-gonic.com/",
|
||||
"echo": "https://echo.labstack.com/",
|
||||
}
|
||||
|
||||
if err := gconv.Scan(goWeb, &m); err == nil {
|
||||
fmt.Printf("%#v", m.Map())
|
||||
}
|
||||
// Output:
|
||||
// map[string]string{"echo":"https://echo.labstack.com/", "gin":"https://gin-gonic.com/", "goframe":"https://goframe.org"}
|
||||
}
|
||||
312
container/gmap/gmap_z_example_test.go
Normal file
312
container/gmap/gmap_z_example_test.go
Normal file
@ -0,0 +1,312 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
m := gmap.New()
|
||||
|
||||
// Add data.
|
||||
m.Set("key1", "val1")
|
||||
|
||||
// Print size.
|
||||
fmt.Println(m.Size())
|
||||
|
||||
addMap := make(map[interface{}]interface{})
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
addMap[1] = 1
|
||||
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// Batch add data.
|
||||
m.Sets(addMap)
|
||||
|
||||
// Gets the value of the corresponding key.
|
||||
fmt.Println(m.Get("key3"))
|
||||
|
||||
// Get the value by key, or set it with given key-value if not exist.
|
||||
fmt.Println(m.GetOrSet("key4", "val4"))
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
fmt.Println(m.SetIfNotExist("key3", "val3"))
|
||||
|
||||
// Remove key
|
||||
m.Remove("key2")
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// Batch remove keys.
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
fmt.Println(m.Contains("key3"))
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Clear deletes all data of the map.
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// 1
|
||||
// [val1]
|
||||
// val3
|
||||
// val4
|
||||
// false
|
||||
// [key4 key1 key3 1]
|
||||
// [key4 key3]
|
||||
// true
|
||||
// map[val3:key3 val4:key4]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleNewFrom() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewHashMap() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewHashMapFrom() {
|
||||
m := gmap.New()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewAnyAnyMap() {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewAnyAnyMapFrom() {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewAnyAnyMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"val1"}
|
||||
// {"key1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewIntAnyMap() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewIntAnyMapFrom() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Set(1, "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewIntAnyMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":"val1"}
|
||||
// {"1":"val1"}
|
||||
}
|
||||
|
||||
func ExampleNewIntIntMap() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
}
|
||||
|
||||
func ExampleNewIntIntMapFrom() {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Set(1, 1)
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewIntIntMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
// {"1":1}
|
||||
}
|
||||
|
||||
func ExampleNewStrAnyMap() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"var1"}
|
||||
}
|
||||
|
||||
func ExampleNewStrAnyMapFrom() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewStrAnyMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"var1"}
|
||||
// {"key1":"var1"}
|
||||
}
|
||||
|
||||
func ExampleNewStrIntMap() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":1}
|
||||
}
|
||||
|
||||
func ExampleNewStrIntMapFrom() {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Set("key1", 1)
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewStrIntMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":1}
|
||||
// {"key1":1}
|
||||
}
|
||||
|
||||
func ExampleNewStrStrMap() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"var1"}
|
||||
}
|
||||
|
||||
func ExampleNewStrStrMapFrom() {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewStrStrMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// {"key1":"var1"}
|
||||
// {"key1":"var1"}
|
||||
}
|
||||
|
||||
func ExampleNewListMap() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
m.Set("key2", "var2")
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// {"key1":"var1","key2":"var2"}
|
||||
}
|
||||
|
||||
func ExampleNewListMapFrom() {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
m.Set("key1", "var1")
|
||||
m.Set("key2", "var2")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewListMapFrom(m.Map(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// May Output:
|
||||
// {"key1":"var1","key2":"var2"}
|
||||
// {"key1":"var1","key2":"var2"}
|
||||
}
|
||||
|
||||
func ExampleNewTreeMap() {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
|
||||
m.Set("key2", "var2")
|
||||
m.Set("key1", "var1")
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:var1 key2:var2]
|
||||
}
|
||||
|
||||
func ExampleNewTreeMapFrom() {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
|
||||
m.Set("key2", "var2")
|
||||
m.Set("key1", "var1")
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
n := gmap.NewListMapFrom(m.Map(), true)
|
||||
fmt.Println(n.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:var1 key2:var2]
|
||||
// map[key1:var1 key2:var2]
|
||||
}
|
||||
367
container/gmap/gmap_z_unit_hash_any_any_test.go
Normal file
367
container/gmap/gmap_z_unit_hash_any_any_test.go
Normal file
@ -0,0 +1,367 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func Test_AnyAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.AnyAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
|
||||
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.Sets(map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]interface{}{1, 2})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{3: 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, "2")
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewAnyAnyMap()
|
||||
m2 := gmap.NewAnyAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_MapCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set("time1", time.Time{})
|
||||
m.Set("time2", time.Now())
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get("time1"), time.Time{})
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get("time1"), nil)
|
||||
t.AssertNE(m.Get("time2"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m1 := gmap.NewAnyAnyMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.New()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.Map
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
t.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
t.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.Map
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
342
container/gmap/gmap_z_unit_hash_int_any_test.go
Normal file
342
container/gmap/gmap_z_unit_hash_int_any_test.go
Normal file
@ -0,0 +1,342 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func getAny() interface{} {
|
||||
return 123
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
|
||||
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
t.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, "2")
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntAnyMap()
|
||||
m2 := gmap.NewIntAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_MapCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
m1 := gmap.NewIntAnyMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntAnyMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntAnyMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntAnyMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
348
container/gmap/gmap_z_unit_hash_int_int_test.go
Normal file
348
container/gmap/gmap_z_unit_hash_int_int_test.go
Normal file
@ -0,0 +1,348 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func getInt() int {
|
||||
return 123
|
||||
}
|
||||
|
||||
func intIntCallBack(int, int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, 2), 2)
|
||||
t.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), 2)
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, 2), 2)
|
||||
t.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), 2)
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.GetOrSetFunc(1, getInt)
|
||||
m.GetOrSetFuncLock(2, getInt)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
t.Assert(m.SetIfNotExistFunc(1, getInt), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getInt), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Iterator(intIntCallBack)
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]int{3: 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]int) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]int) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntIntMap()
|
||||
m2 := gmap.NewIntIntMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, 2)
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_MapCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 0)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntInt{
|
||||
1: 10,
|
||||
2: 20,
|
||||
}
|
||||
m1 := gmap.NewIntIntMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntInt{
|
||||
1: 10,
|
||||
2: 20,
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntIntMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{11, 22})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{11, 22})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
3: 33,
|
||||
})
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntIntMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntIntMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":1,"2":2}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
t.Assert(v.Map.Get(2), "2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: 1,
|
||||
2: 2,
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
t.Assert(v.Map.Get(2), "2")
|
||||
})
|
||||
}
|
||||
346
container/gmap/gmap_z_unit_hash_int_str_test.go
Normal file
346
container/gmap/gmap_z_unit_hash_int_str_test.go
Normal file
@ -0,0 +1,346 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func getStr() string {
|
||||
return "z"
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "a")
|
||||
|
||||
t.Assert(m.Get(1), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
t.Assert(m.Remove(2), "b")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
t.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "a")
|
||||
|
||||
t.Assert(m.Get(1), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
t.Assert(m.Remove(2), "b")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
// 反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
// t.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
t.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
t.Assert(m.Get(1), "z")
|
||||
t.Assert(m.Get(2), "z")
|
||||
t.Assert(m.SetIfNotExistFunc(1, getStr), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getStr), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
t.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
})
|
||||
}
|
||||
func Test_IntStrMap_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]string{1: "a", 2: "b"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]string{1: "a", 2: "b", 3: "c"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]string) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]string) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_IntStrMap_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntStrMap()
|
||||
m2 := gmap.NewIntStrMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Get(1), "0")
|
||||
t.Assert(m.Get(2), "2")
|
||||
data := m.Map()
|
||||
t.Assert(data[1], "0")
|
||||
t.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
t.Assert(m.Get(3), "3")
|
||||
m.Set(4, "4")
|
||||
t.Assert(data[4], "4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_MapCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Get(1), "0")
|
||||
t.Assert(m.Get(2), "2")
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], "0")
|
||||
t.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
t.Assert(m.Get(3), "")
|
||||
m.Set(4, "4")
|
||||
t.Assert(data[4], "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(2), "2")
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
m1 := gmap.NewIntStrMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntStrMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntStrMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
354
container/gmap/gmap_z_unit_hash_str_any_test.go
Normal file
354
container/gmap/gmap_z_unit_hash_str_any_test.go
Normal file
@ -0,0 +1,354 @@
|
||||
// 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func Test_StrAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("a", 1)
|
||||
|
||||
t.Assert(m.Get("a"), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("b", "2"), "2")
|
||||
t.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
t.Assert(m.Remove("b"), "2")
|
||||
t.Assert(m.Contains("b"), false)
|
||||
|
||||
t.AssertIN("c", m.Keys())
|
||||
t.AssertIN("a", m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
t.Assert(m.Get("a"), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("b", "2"), "2")
|
||||
t.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
t.Assert(m.Remove("b"), "2")
|
||||
t.Assert(m.Contains("b"), false)
|
||||
|
||||
t.AssertIN("c", m.Keys())
|
||||
t.AssertIN("a", m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
t.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.GetOrSetFunc("a", getAny)
|
||||
m.GetOrSetFuncLock("b", getAny)
|
||||
t.Assert(m.Get("a"), 123)
|
||||
t.Assert(m.Get("b"), 123)
|
||||
t.Assert(m.SetIfNotExistFunc("a", getAny), false)
|
||||
t.Assert(m.SetIfNotExistFunc("c", getAny), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock("b", getAny), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("d", getAny), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Batch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Sets(map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
t.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.Removes([]string{"a", "b"})
|
||||
t.Assert(m.Map(), map[string]interface{}{"c": 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[string]interface{}{"a": true, "b": false}
|
||||
m := gmap.NewStrAnyMapFrom(expect)
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Lock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[string]interface{}{"a": true, "b": false}
|
||||
|
||||
m := gmap.NewStrAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[string]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[string]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
func Test_StrAnyMap_Clone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// clone 方法是深克隆
|
||||
m := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
// 修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
// 修改clone map,原 map 不影响
|
||||
t.AssertIN("b", m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_StrAnyMap_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewStrAnyMap()
|
||||
m2 := gmap.NewStrAnyMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("1", 1)
|
||||
m.Set("2", 2)
|
||||
t.Assert(m.Get("1"), 1)
|
||||
t.Assert(m.Get("2"), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data["1"], 1)
|
||||
t.Assert(data["2"], 2)
|
||||
data["3"] = 3
|
||||
t.Assert(m.Get("3"), 3)
|
||||
m.Set("4", 4)
|
||||
t.Assert(data["4"], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_MapCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("1", 1)
|
||||
m.Set("2", 2)
|
||||
t.Assert(m.Get("1"), 1)
|
||||
t.Assert(m.Get("2"), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data["1"], 1)
|
||||
t.Assert(data["2"], 2)
|
||||
data["3"] = 3
|
||||
t.Assert(m.Get("3"), nil)
|
||||
m.Set("4", 4)
|
||||
t.Assert(data["4"], nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("1", 0)
|
||||
m.Set("2", 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get("1"), 0)
|
||||
t.Assert(m.Get("2"), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get("2"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m1 := gmap.NewStrAnyMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewStrAnyMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.StrAnyMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
t.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
t.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrAnyMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.StrAnyMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user