lijian 1 سال پیش
والد
کامیت
974a6daccd
100فایلهای تغییر یافته به همراه21005 افزوده شده و 87 حذف شده
  1. 9 4
      go.mod
  2. 24 5
      go.sum
  3. 9 7
      pkg/rpcs/common.go
  4. 13 0
      pkg/rpcs/task.go
  5. 0 7
      pkg/rule/task_lifecycle_message.go
  6. 44 0
      pkg/rule/task_message.go
  7. 1 1
      services/controller/controller.go
  8. 10 10
      services/emqx-agent/agent.go
  9. 0 6
      services/scene-access/internal/service/config.go
  10. 16 7
      services/scene-access/internal/service/scene.go
  11. 28 7
      services/scene-access/main.go
  12. 45 0
      services/timer-service/README.md
  13. 67 0
      services/timer-service/internal/executer.go
  14. 64 0
      services/timer-service/internal/scheduler.go
  15. 12 28
      services/timer-service/internal/timer_service.go
  16. 19 5
      services/timer-service/main.go
  17. 26 0
      vendor/github.com/go-logr/logr/.golangci.yaml
  18. 6 0
      vendor/github.com/go-logr/logr/CHANGELOG.md
  19. 17 0
      vendor/github.com/go-logr/logr/CONTRIBUTING.md
  20. 201 0
      vendor/github.com/go-logr/logr/LICENSE
  21. 282 0
      vendor/github.com/go-logr/logr/README.md
  22. 24 0
      vendor/github.com/go-logr/logr/discard.go
  23. 804 0
      vendor/github.com/go-logr/logr/funcr/funcr.go
  24. 550 0
      vendor/github.com/go-logr/logr/logr.go
  25. 201 0
      vendor/github.com/go-logr/stdr/LICENSE
  26. 6 0
      vendor/github.com/go-logr/stdr/README.md
  27. 170 0
      vendor/github.com/go-logr/stdr/stdr.go
  28. 21 0
      vendor/github.com/gogf/gf/v2/LICENSE
  29. 8 0
      vendor/github.com/gogf/gf/v2/container/garray/garray.go
  30. 69 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_func.go
  31. 870 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_normal_any.go
  32. 846 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_normal_int.go
  33. 857 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_normal_str.go
  34. 842 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_any.go
  35. 787 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_int.go
  36. 800 0
      vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_str.go
  37. 572 0
      vendor/github.com/gogf/gf/v2/container/glist/glist.go
  38. 45 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap.go
  39. 563 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_any_any_map.go
  40. 564 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_any_map.go
  41. 533 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_int_map.go
  42. 533 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_str_map.go
  43. 551 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_any_map.go
  44. 537 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_int_map.go
  45. 526 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_str_map.go
  46. 612 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_list_map.go
  47. 30 0
      vendor/github.com/gogf/gf/v2/container/gmap/gmap_tree_map.go
  48. 188 0
      vendor/github.com/gogf/gf/v2/container/gpool/gpool.go
  49. 144 0
      vendor/github.com/gogf/gf/v2/container/gqueue/gqueue.go
  50. 526 0
      vendor/github.com/gogf/gf/v2/container/gset/gset_any_set.go
  51. 489 0
      vendor/github.com/gogf/gf/v2/container/gset/gset_int_set.go
  52. 519 0
      vendor/github.com/gogf/gf/v2/container/gset/gset_str_set.go
  53. 10 0
      vendor/github.com/gogf/gf/v2/container/gtree/gtree.go
  54. 816 0
      vendor/github.com/gogf/gf/v2/container/gtree/gtree_avltree.go
  55. 979 0
      vendor/github.com/gogf/gf/v2/container/gtree/gtree_btree.go
  56. 991 0
      vendor/github.com/gogf/gf/v2/container/gtree/gtree_redblacktree.go
  57. 14 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype.go
  58. 106 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_bool.go
  59. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_byte.go
  60. 96 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_bytes.go
  61. 97 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_float32.go
  62. 97 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_float64.go
  63. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_int.go
  64. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_int32.go
  65. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_int64.go
  66. 82 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_interface.go
  67. 80 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_string.go
  68. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint.go
  69. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint32.go
  70. 85 0
      vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint64.go
  71. 205 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar.go
  72. 51 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_is.go
  73. 25 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_list.go
  74. 97 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_map.go
  75. 19 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_scan.go
  76. 77 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_slice.go
  77. 23 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_struct.go
  78. 131 0
      vendor/github.com/gogf/gf/v2/container/gvar/gvar_vars.go
  79. 78 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis.go
  80. 78 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_adapter.go
  81. 140 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_config.go
  82. 44 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_instance.go
  83. 137 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis.go
  84. 62 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_generic.go
  85. 32 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_hash.go
  86. 43 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_list.go
  87. 40 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_pubsub.go
  88. 30 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_script.go
  89. 33 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_set.go
  90. 85 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_sorted_set.go
  91. 63 0
      vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_string.go
  92. 8 0
      vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug.go
  93. 196 0
      vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_caller.go
  94. 29 0
      vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_grid.go
  95. 77 0
      vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_stack.go
  96. 57 0
      vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_version.go
  97. 134 0
      vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary.go
  98. 287 0
      vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_be.go
  99. 74 0
      vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_bit.go
  100. 7 0
      vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_func.go

+ 9 - 4
go.mod

@@ -8,6 +8,7 @@ require (
 	github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab
 	github.com/go-sql-driver/mysql v1.7.1
 	github.com/gogf/gf v1.16.9
+	github.com/gogf/gf/v2 v2.6.1
 	github.com/influxdata/influxdb-client-go/v2 v2.13.0
 	github.com/iris-contrib/middleware/cors v0.0.0-20231105204038-60b21ca77167
 	github.com/iris-contrib/middleware/jwt v0.0.0-20231105204038-60b21ca77167
@@ -58,6 +59,8 @@ require (
 	github.com/fatih/structs v1.1.0 // indirect
 	github.com/flosch/pongo2/v4 v4.0.2 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/go-logr/logr v1.2.4 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gobwas/glob v0.2.3 // indirect
 	github.com/gobwas/httphead v0.1.0 // indirect
 	github.com/gobwas/pool v0.2.1 // indirect
@@ -95,8 +98,8 @@ require (
 	github.com/mailgun/raymond/v2 v2.0.48 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
-	github.com/mattn/go-isatty v0.0.19 // indirect
-	github.com/mattn/go-runewidth v0.0.9 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
 	github.com/mediocregopher/radix/v3 v3.8.1 // indirect
 	github.com/microcosm-cc/bluemonday v1.0.26 // indirect
@@ -115,6 +118,7 @@ require (
 	github.com/prometheus/client_model v0.5.0 // indirect
 	github.com/prometheus/common v0.45.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
+	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/ryanuber/columnize v2.1.0+incompatible // indirect
 	github.com/sanity-io/litter v1.5.5 // indirect
@@ -135,8 +139,9 @@ require (
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	go.etcd.io/etcd/api/v3 v3.5.11 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.11 // indirect
-	go.opentelemetry.io/otel v1.0.0 // indirect
-	go.opentelemetry.io/otel/trace v1.0.0 // indirect
+	go.opentelemetry.io/otel v1.14.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.14.0 // indirect
+	go.opentelemetry.io/otel/trace v1.14.0 // indirect
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.17.0 // indirect

+ 24 - 5
go.sum

@@ -47,6 +47,8 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
 github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
@@ -94,6 +96,11 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
 github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
 github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
+github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
 github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -114,6 +121,8 @@ github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/K
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
 github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
+github.com/gogf/gf/v2 v2.6.1 h1:n/cfXM506WjhPa6Z1CEDuHNM1XZ7C8JzSDPn2AfuxgQ=
+github.com/gogf/gf/v2 v2.6.1/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@@ -246,6 +255,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
 github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
 github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -259,10 +270,11 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
 github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
 github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
@@ -335,6 +347,9 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne
 github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
 github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
 github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
+github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/robertkrimen/otto v0.3.0 h1:5RI+8860NSxvXywDY9ddF5HcPw0puRsd8EgbXV0oqRE=
 github.com/robertkrimen/otto v0.3.0/go.mod h1:uW9yN1CYflmUQYvAMS0m+ZiNo3dMzRUDQJX0jWbzgxw=
 github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
@@ -444,10 +459,14 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.11 h1:bT2xVspdiCj2910T0V+/KHcVKjkUrCZVtk8J2JF
 go.etcd.io/etcd/client/pkg/v3 v3.5.11/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=
 go.etcd.io/etcd/client/v3 v3.5.11 h1:ajWtgoNSZJ1gmS8k+icvPtqsqEav+iUorF7b0qozgUU=
 go.etcd.io/etcd/client/v3 v3.5.11/go.mod h1:a6xQUEqFJ8vztO1agJh/KQKOMfFI8og52ZconzcDJwE=
-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/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
+go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
+go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
+go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
+go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
 go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
+go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
+go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
 go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=

+ 9 - 7
pkg/rpcs/common.go

@@ -8,11 +8,13 @@ type ReplyEmptyResult struct{}
 
 // 定义 rpc服务的名字
 const (
-	RegistryServerName   = "registry"
-	MQTTAccessName       = "MQTTAccess"
-	DeviceManagerName    = "deviceManager"
-	ControllerName       = "controller"
-	HttpAccessName       = "HTTPAccess"
-	ShadowServiceName    = "ShadowServer"
-	EmqxAgentServiceName = "EmqxAgentServer"
+	RegistryServerName     = "registry"
+	MQTTAccessName         = "MQTTAccess"
+	DeviceManagerName      = "deviceManager"
+	ControllerName         = "controller"
+	HttpAccessName         = "HTTPAccess"
+	ShadowServiceName      = "ShadowServer"
+	EmqxAgentServiceName   = "EmqxAgentServer"
+	SceneAccessServiceName = "SceneAccess"
+	TimerServiceName       = "TimerService"
 )

+ 13 - 0
pkg/rpcs/task.go

@@ -0,0 +1,13 @@
+package rpcs
+
+// ArgsSubmitTask 任务提交参数
+type ArgsSubmitTask struct {
+	Type string // 任务类型,(timer|以后扩展)
+	Data string // 任务配置JSON字符串
+}
+
+type ArgsSubmitTaskLifecycle struct {
+	TaskId string
+	Action string
+	Data   string
+}

+ 0 - 7
pkg/rule/task_lifecycle_message.go

@@ -1,7 +0,0 @@
-package rule
-
-type TaskLifecycleMessage struct {
-	TaskId string `json:"task_id"`
-	Action string `json:"action"`
-	Data   string `json:"data"` // 具体的任务操作配置字符串
-}

+ 44 - 0
pkg/rule/task_message.go

@@ -0,0 +1,44 @@
+package rule
+
+type TaskLifecycleMessage struct {
+	TaskId string `json:"task_id"`
+	Action string `json:"action"`
+	Data   string `json:"data"` // 具体的任务操作配置JSON字符串
+}
+
+// TimerTaskMessage 定时任务消息
+type TimerTaskMessage struct {
+	TaskId  string        `json:"task_id"` // 任务Id
+	Cron    string        `json:"cron"`    // 任务执行的cron表达式
+	Actions []*TaskAction `json:"actions"` // 执行动作列表
+}
+
+// TaskAction 定时任务执行动作
+type TaskAction struct {
+	EntityId string `json:"entity_id"` // 被执行实体Id,指向设备编码
+	/*
+		动作对象类型。
+		delay:延时
+		device_issue:设备指令下发
+		device_group_issue:群组指令下发
+	*/
+	ActionExecutor   string                `json:"action_executor"`   // 动作对象类型。
+	SubEntityId      string                `json:"sub_entity_id"`     // 实体子设备Id,如果需要
+	ExecutorProperty *TaskExecutorProperty `json:"executor_property"` // 动作执行明细
+}
+
+// TaskExecutorProperty 定时任务执行动作执行参数
+type TaskExecutorProperty struct {
+	/*
+		指令 code。当 action_executor 是 device_issue 或 device_group_issue 时,此参数必填。
+	*/
+	FunctionCode string `json:"function_code"`
+	/*
+		指令 value。当 action_executor 是 device_issue 或 device_group_issue 时,此参数必填。
+	*/
+	FunctionValue map[string]interface{} `json:"function_value"`
+	/*
+		延时时间。当 action_executor 是 delay 时,此参数必填。
+	*/
+	DelaySeconds int64 `json:"delay_seconds"`
+}

+ 1 - 1
services/controller/controller.go

@@ -301,7 +301,7 @@ func (c *Controller) SendCommandV2(args rpcs.ArgsSendCommand, reply *rpcs.ReplyS
 	if err != nil {
 		return err
 	}
-	return server.RPCCallByHost(rpchost, "Agent.SendCommand", args, reply)
+	return server.RPCCallByHost(rpchost, "Access.SendCommand", args, reply)
 }
 
 func getAccessRPCHost(deviceid string) (string, error) {

+ 10 - 10
services/emqx-agent/agent.go

@@ -11,12 +11,12 @@ import (
 	"time"
 )
 
-type Agent struct {
+type Access struct {
 	client SubDev
 }
 
 // Message 收到设备上报消息处理
-func (a *Agent) Message(topic string, payload []byte) error {
+func (a *Access) Message(topic string, payload []byte) error {
 
 	topicInfo, err := protocol.GetTopicInfo(topic)
 	if err != nil {
@@ -49,7 +49,7 @@ func (a *Agent) Message(topic string, payload []byte) error {
 	return nil
 }
 
-func (a *Agent) processEvent(deviceId, vendorId string, message *gjson.Json) error {
+func (a *Access) processEvent(deviceId, vendorId string, message *gjson.Json) error {
 	reply := rpcs.ReplyOnEvent{}
 	args := rpcs.ArgsOnEvent{
 		DeviceId:    deviceId,
@@ -66,7 +66,7 @@ func (a *Agent) processEvent(deviceId, vendorId string, message *gjson.Json) err
 	return nil
 }
 
-func (a *Agent) processStatus(deviceId, vendorId string, message *gjson.Json) error {
+func (a *Access) processStatus(deviceId, vendorId string, message *gjson.Json) error {
 	act := klink.PacketAction(message.GetString("action"))
 	if act != "" {
 		switch act {
@@ -156,7 +156,7 @@ func processReportStatus(deviceid, vendorId string, message *gjson.Json) {
 }
 
 // Connected 设备接入时
-func (a *Agent) Connected(status *protocol.DevConnectStatus) error {
+func (a *Access) Connected(status *protocol.DevConnectStatus) error {
 	server.Log.Infof("设备上线;%s", status.DeviceId)
 	// 查询设备信息
 	device := &models.Device{}
@@ -187,7 +187,7 @@ func (a *Agent) Connected(status *protocol.DevConnectStatus) error {
 }
 
 // Disconnected 设备断开连接时
-func (a *Agent) Disconnected(status *protocol.DevConnectStatus) error {
+func (a *Access) Disconnected(status *protocol.DevConnectStatus) error {
 	// 查询设备信息
 	device := &models.Device{}
 	err := server.RPCCallByName(nil, rpcs.RegistryServerName, "Registry.FindDeviceByIdentifier", status.DeviceId, device)
@@ -208,7 +208,7 @@ func (a *Agent) Disconnected(status *protocol.DevConnectStatus) error {
 }
 
 // SendCommand rpc 发送设备命令
-func (a *Agent) SendCommand(args rpcs.ArgsSendCommand, reply *rpcs.ReplySendCommand) error {
+func (a *Access) SendCommand(args rpcs.ArgsSendCommand, reply *rpcs.ReplySendCommand) error {
 	// 查询设备信息
 	device := &models.Device{}
 	err := server.RPCCallByName(nil, rpcs.RegistryServerName, "Registry.FindDeviceByIdentifier", args.DeviceId, device)
@@ -241,7 +241,7 @@ func (a *Agent) SendCommand(args rpcs.ArgsSendCommand, reply *rpcs.ReplySendComm
 }
 
 // GetStatus rpc 获取设备状态
-func (a *Agent) GetStatus(args rpcs.ArgsGetStatus, reply *rpcs.ReplyGetStatus) error {
+func (a *Access) GetStatus(args rpcs.ArgsGetStatus, reply *rpcs.ReplyGetStatus) error {
 	server.Log.Infof("Access Get Status: %v", args)
 	// first send a get status command
 	cmdArgs := rpcs.ArgsSendCommand{
@@ -254,8 +254,8 @@ func (a *Agent) GetStatus(args rpcs.ArgsGetStatus, reply *rpcs.ReplyGetStatus) e
 	return a.SendCommand(cmdArgs, &cmdReply)
 }
 
-func NewAgent(client SubDev) *Agent {
-	return &Agent{
+func NewAgent(client SubDev) *Access {
+	return &Access{
 		client: client,
 	}
 }

+ 0 - 6
services/scene-access/internal/service/config.go

@@ -1,6 +0,0 @@
-package service
-
-// Config 服务配置
-type Config struct {
-	RabbitMQ string // rabbit mq broker 连接字符串
-}

+ 16 - 7
services/scene-access/internal/service/scene.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/streadway/amqp"
+	"sparrow/pkg/rpcs"
 	"sparrow/pkg/rule"
 	"sparrow/pkg/server"
 	"sparrow/services/scene-access/internal/service/manager"
@@ -33,16 +34,24 @@ func NewSceneService(mqAddr string) *SceneService {
 	return srv
 }
 
-// SubmitTask 提交一个任务
-func (s *SceneService) SubmitTask() error {
-	return s.taskManager.Publish(TimerTopic, []byte("this is a task"))
+// SubmitTask rpc 提交一个任务
+func (s *SceneService) SubmitTask(args rpcs.ArgsSubmitTask, reply *rpcs.ReplyEmptyResult) error {
+	var (
+		topic string
+	)
+	switch args.Type {
+	case "timer":
+		topic = TimerTopic
+	}
+	return s.taskManager.Publish(topic, []byte(args.Data))
 }
 
-func (s *SceneService) SubmitTaskLifecycle() error {
+// SubmitTaskLifecycle rpc 提交一个任务生命周期
+func (s *SceneService) SubmitTaskLifecycle(args rpcs.ArgsSubmitTaskLifecycle, reply *rpcs.ReplyEmptyResult) error {
 	taskMsg := rule.TaskLifecycleMessage{
-		TaskId: "xxxxxx",
-		Action: "add",
-		Data:   "test",
+		TaskId: args.TaskId,
+		Action: args.Action,
+		Data:   args.Data,
 	}
 	data, err := json.Marshal(&taskMsg)
 	if err != nil {

+ 28 - 7
services/scene-access/main.go

@@ -1,16 +1,37 @@
 package main
 
 import (
+	"flag"
+	"sparrow/pkg/rpcs"
+	"sparrow/pkg/server"
 	"sparrow/services/scene-access/internal/service"
-	"time"
+)
+
+const (
+	flagRabbitHost    = "rabbithost"
+	defaultRabbitHost = "amqp://guest:guest@localhost:5672/"
+)
+
+var (
+	confRabbitHost = flag.String(flagRabbitHost, defaultRabbitHost, "rabbitmq host address, amqp://user:password@ip:port/")
 )
 
 func main() {
-	scene := service.NewSceneService("amqp://admin:YjT5tlNVKxDsbUQ5jqfPhXfae@127.0.0.1:5672")
-	time.Sleep(time.Second * 5)
-	for {
-		scene.SubmitTask()
-		scene.SubmitTaskLifecycle()
-		time.Sleep(time.Second * 5)
+	// init server
+	err := server.Init(rpcs.SceneAccessServiceName)
+	if err != nil {
+		server.Log.Fatal(err)
+		return
+	}
+	scene := service.NewSceneService(*confRabbitHost)
+
+	err = server.RegisterRPCHandler(scene)
+	if err != nil {
+		return
+	}
+	// start to run
+	err = server.Run()
+	if err != nil {
+		server.Log.Fatal(err)
 	}
 }

+ 45 - 0
services/timer-service/README.md

@@ -0,0 +1,45 @@
+## 定时任务执行服务
+
+监听rabbitmq中的topic为sparrow.task.timer主题的消息,执行定时任务
+
+
+### 消息数据模型
+
+```go
+
+// TimerTaskMessage 定时任务消息
+type TimerTaskMessage struct {
+Cron    string        `json:"cron"`    // 任务执行的cron表达式
+Actions []*TaskAction `json:"actions"` // 执行动作列表
+}
+
+// TaskAction 定时任务执行动作
+type TaskAction struct {
+EntityId string `json:"entity_id"` // 被执行实体Id,指向设备编码
+/*
+	动作对象类型。
+	delay:延时
+	device_issue:设备指令下发
+	device_group_issue:群组指令下发
+*/
+ActionExecutor   string                `json:"action_executor"`   // 动作对象类型。
+ExecutorProperty *TaskExecutorProperty `json:"executor_property"` // 动作执行明细
+}
+
+// TaskExecutorProperty 定时任务执行动作执行参数
+type TaskExecutorProperty struct {
+/*
+	指令 code。当 action_executor 是 device_issue 或 device_group_issue 时,此参数必填。
+*/
+FunctionCode string `json:"function_code"`
+/*
+	指令 value。当 action_executor 是 device_issue 或 device_group_issue 时,此参数必填。
+*/
+FunctionValue bool `json:"function_value"`
+/*
+	延时时间。当 action_executor 是 delay 时,此参数必填。
+*/
+DelaySeconds int64 `json:"delay_seconds"`
+}
+
+```

+ 67 - 0
services/timer-service/internal/executer.go

@@ -0,0 +1,67 @@
+package internal
+
+import (
+	"sparrow/pkg/rpcs"
+	"sparrow/pkg/rule"
+	"sparrow/pkg/server"
+	"time"
+)
+
+// TaskExecutor 任务执行器,用来执行具体的任务动作
+type TaskExecutor struct {
+	Actions []*rule.TaskAction
+}
+
+func NewTaskExecutor(actions []*rule.TaskAction) *TaskExecutor {
+	return &TaskExecutor{
+		Actions: actions,
+	}
+}
+
+func (a *TaskExecutor) Do() error {
+	for _, action := range a.Actions {
+		switch action.ActionExecutor {
+		case "delay":
+			return a.doDelayTask(action.EntityId, action.SubEntityId, action.ExecutorProperty)
+		case "device_issue":
+			return a.doDeviceIssueTask(action.EntityId, action.SubEntityId, action.ExecutorProperty)
+		}
+	}
+	return nil
+}
+
+func (a *TaskExecutor) doDeviceIssueTask(entityId, subEntityId string, action *rule.TaskExecutorProperty) error {
+	// 调用设备接入服务
+	rpchost, err := getAccessRPCHost(entityId)
+	if err != nil {
+		return err
+	}
+	args := rpcs.ArgsSendCommand{
+		DeviceId:  entityId,
+		SubDevice: subEntityId,
+		Cmd:       action.FunctionCode,
+		Params:    action.FunctionValue,
+	}
+	reply := &rpcs.ReplyEmptyResult{}
+	server.Log.Debugf("do Device Issue task args:%v", args)
+	return server.RPCCallByHost(rpchost, "Access.SendCommand", args, reply)
+}
+
+// 执行延时任务
+func (a *TaskExecutor) doDelayTask(entityId, subEntityId string, action *rule.TaskExecutorProperty) error {
+	time.Sleep(time.Duration(action.DelaySeconds) * time.Second)
+	return nil
+}
+
+func getAccessRPCHost(deviceid string) (string, error) {
+	args := rpcs.ArgsGetDeviceOnlineStatus{
+		Id: deviceid,
+	}
+	reply := &rpcs.ReplyGetDeviceOnlineStatus{}
+	err := server.RPCCallByName(nil, rpcs.DeviceManagerName, "DeviceManager.GetDeviceOnlineStatus", args, reply)
+	if err != nil {
+		return "", err
+	}
+
+	return reply.AccessRPCHost, nil
+}

+ 64 - 0
services/timer-service/internal/scheduler.go

@@ -0,0 +1,64 @@
+package internal
+
+import (
+	"context"
+	"encoding/json"
+	"github.com/gogf/gf/container/gmap"
+	"github.com/gogf/gf/v2/os/gcron"
+	"sparrow/pkg/rule"
+	"sparrow/pkg/server"
+)
+
+// TaskSchedule task schedule 任务调度
+type TaskSchedule struct {
+	tasks *gmap.HashMap // 保存任务名称与任务实体的映射
+}
+
+func NewTaskSchedule() *TaskSchedule {
+	return &TaskSchedule{
+		tasks: gmap.NewHashMap(true),
+	}
+}
+
+func (t *TaskSchedule) AddTask(msg []byte) error {
+	var task rule.TimerTaskMessage
+	err := json.Unmarshal([]byte(msg), &task)
+	if err != nil {
+		return err
+	}
+	// 如果已经存在相同任务Id
+	if t.tasks.Contains(task.TaskId) {
+		return nil
+	}
+	// 创建任务
+	entity, err := gcron.Add(context.Background(), task.Cron, func(ctx context.Context) {
+		if err = NewTaskExecutor(task.Actions).Do(); err != nil {
+			server.Log.Errorf("do taskid :%s error:%s", task.TaskId, err.Error())
+		}
+	}, task.TaskId)
+	if err != nil {
+		return err
+	}
+	t.tasks.Set(task.TaskId, entity)
+	server.Log.Debugf("add a new timer task :%s", task.TaskId)
+	return nil
+}
+
+func (t *TaskSchedule) AddMessageHandle(msg *rule.TaskLifecycleMessage) error {
+	return nil
+}
+
+func (t *TaskSchedule) RemoveMessageHandle(msg *rule.TaskLifecycleMessage) error {
+	server.Log.Debugf("RemoveMessageHandle :%s", msg.TaskId)
+	return nil
+}
+
+func (t *TaskSchedule) UpdateMessageHandle(msg *rule.TaskLifecycleMessage) error {
+	server.Log.Debugf("UpdateMessageHandle :%s", msg.TaskId)
+	return nil
+}
+
+func (t *TaskSchedule) SnapMessageHandle(msg *rule.TaskLifecycleMessage) error {
+	server.Log.Debugf("SnapMessageHandle :%s", msg.TaskId)
+	return nil
+}

+ 12 - 28
services/timer-service/internal/timer_service.go

@@ -3,6 +3,7 @@ package internal
 import (
 	"fmt"
 	"github.com/streadway/amqp"
+	"sparrow/pkg/rpcs"
 	"sparrow/pkg/rule"
 	"sparrow/pkg/server"
 	"time"
@@ -18,18 +19,24 @@ type TimerService struct {
 	notifyChanClose    chan *amqp.Error
 	ch                 *amqp.Channel
 	reconnectChan      chan struct{}
+	taskSchedule       *TaskSchedule
 }
 
 func NewTimerService(host string) *TimerService {
 	ts := &TimerService{
 		host:          host,
 		done:          make(chan bool),
+		taskSchedule:  NewTaskSchedule(),
 		reconnectChan: make(chan struct{}),
 	}
 	go ts.handleReconnect()
 	return ts
 }
 
+func (s *TimerService) Ping(args string, result rpcs.ReplyEmptyResult) error {
+	return nil
+}
+
 func (s *TimerService) init(conn *amqp.Connection) error {
 	ch, err := conn.Channel()
 	if err != nil {
@@ -45,7 +52,7 @@ func (s *TimerService) init(conn *amqp.Connection) error {
 	if err != nil {
 		return err
 	}
-	tc.SetExternalConsumer(s)
+	tc.SetExternalConsumer(s.taskSchedule)
 	s.tc = tc
 	s.notifyChanClose = make(chan *amqp.Error)
 	s.ch.NotifyClose(s.notifyChanClose)
@@ -81,7 +88,10 @@ func (s *TimerService) initTaskConsumer() error {
 }
 
 func (s *TimerService) handleTimerTask(msg []byte) {
-	fmt.Printf("%s", msg)
+	err := s.taskSchedule.AddTask(msg)
+	if err != nil {
+		server.Log.Errorf("create task error :%s", err.Error())
+	}
 }
 
 func (s *TimerService) connect() (*amqp.Connection, error) {
@@ -137,29 +147,3 @@ func (s *TimerService) handleReInit(conn *amqp.Connection) bool {
 		}
 	}
 }
-
-/* 实现任务生命周期消息管理 */
-
-// AddMessageHandle 新增任务
-func (s *TimerService) AddMessageHandle(msg *rule.TaskLifecycleMessage) error {
-	fmt.Printf("%v\r\n", msg)
-	return nil
-}
-
-// RemoveMessageHandle 删除任务
-func (s *TimerService) RemoveMessageHandle(msg *rule.TaskLifecycleMessage) error {
-	fmt.Printf("%v\r\n", msg)
-	return nil
-}
-
-// UpdateMessageHandle 更新任务
-func (s *TimerService) UpdateMessageHandle(msg *rule.TaskLifecycleMessage) error {
-	fmt.Printf("%v\r\n", msg)
-	return nil
-}
-
-// SnapMessageHandle 快照任务
-func (s *TimerService) SnapMessageHandle(msg *rule.TaskLifecycleMessage) error {
-	fmt.Printf("%v\r\n", msg)
-	return nil
-}

+ 19 - 5
services/timer-service/main.go

@@ -2,16 +2,30 @@ package main
 
 import (
 	"flag"
+	"sparrow/pkg/rpcs"
+	"sparrow/pkg/server"
 	"sparrow/services/timer-service/internal"
-	"time"
+)
+
+const (
+	flagRabbitHost    = "rabbithost"
+	defaultRabbitHost = "amqp://guest:guest@localhost:5672/"
 )
 
 var (
-	uri = flag.String("uri", "amqp://guest:guest@localhost:5672/", "AMQP URI")
+	confRabbitHost = flag.String(flagRabbitHost, defaultRabbitHost, "rabbitmq host address, amqp://user:password@ip:port/")
 )
 
 func main() {
-	internal.NewTimerService("amqp://admin:YjT5tlNVKxDsbUQ5jqfPhXfae@127.0.0.1:5672")
-	time.Sleep(time.Second * 5)
-	select {}
+	// init server
+	err := server.Init(rpcs.TimerServiceName)
+	if err != nil {
+		server.Log.Fatal(err)
+		return
+	}
+	internal.NewTimerService(*confRabbitHost)
+	err = server.Run()
+	if err != nil {
+		server.Log.Fatal(err)
+	}
 }

+ 26 - 0
vendor/github.com/go-logr/logr/.golangci.yaml

@@ -0,0 +1,26 @@
+run:
+  timeout: 1m
+  tests: true
+
+linters:
+  disable-all: true
+  enable:
+    - asciicheck
+    - errcheck
+    - forcetypeassert
+    - gocritic
+    - gofmt
+    - goimports
+    - gosimple
+    - govet
+    - ineffassign
+    - misspell
+    - revive
+    - staticcheck
+    - typecheck
+    - unused
+
+issues:
+  exclude-use-default: false
+  max-issues-per-linter: 0
+  max-same-issues: 10

+ 6 - 0
vendor/github.com/go-logr/logr/CHANGELOG.md

@@ -0,0 +1,6 @@
+# CHANGELOG
+
+## v1.0.0-rc1
+
+This is the first logged release.  Major changes (including breaking changes)
+have occurred since earlier tags.

+ 17 - 0
vendor/github.com/go-logr/logr/CONTRIBUTING.md

@@ -0,0 +1,17 @@
+# Contributing
+
+Logr is open to pull-requests, provided they fit within the intended scope of
+the project.  Specifically, this library aims to be VERY small and minimalist,
+with no external dependencies.
+
+## Compatibility
+
+This project intends to follow [semantic versioning](http://semver.org) and
+is very strict about compatibility.  Any proposed changes MUST follow those
+rules.
+
+## Performance
+
+As a logging library, logr must be as light-weight as possible.  Any proposed
+code change must include results of running the [benchmark](./benchmark)
+before and after the change.

+ 201 - 0
vendor/github.com/go-logr/logr/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.

+ 282 - 0
vendor/github.com/go-logr/logr/README.md

@@ -0,0 +1,282 @@
+# A minimal logging API for Go
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr)
+
+logr offers an(other) opinion on how Go programs and libraries can do logging
+without becoming coupled to a particular logging implementation.  This is not
+an implementation of logging - it is an API.  In fact it is two APIs with two
+different sets of users.
+
+The `Logger` type is intended for application and library authors.  It provides
+a relatively small API which can be used everywhere you want to emit logs.  It
+defers the actual act of writing logs (to files, to stdout, or whatever) to the
+`LogSink` interface.
+
+The `LogSink` interface is intended for logging library implementers.  It is a
+pure interface which can be implemented by logging frameworks to provide the actual logging
+functionality.
+
+This decoupling allows application and library developers to write code in
+terms of `logr.Logger` (which has very low dependency fan-out) while the
+implementation of logging is managed "up stack" (e.g. in or near `main()`.)
+Application developers can then switch out implementations as necessary.
+
+Many people assert that libraries should not be logging, and as such efforts
+like this are pointless.  Those people are welcome to convince the authors of
+the tens-of-thousands of libraries that *DO* write logs that they are all
+wrong.  In the meantime, logr takes a more practical approach.
+
+## Typical usage
+
+Somewhere, early in an application's life, it will make a decision about which
+logging library (implementation) it actually wants to use.  Something like:
+
+```
+    func main() {
+        // ... other setup code ...
+
+        // Create the "root" logger.  We have chosen the "logimpl" implementation,
+        // which takes some initial parameters and returns a logr.Logger.
+        logger := logimpl.New(param1, param2)
+
+        // ... other setup code ...
+```
+
+Most apps will call into other libraries, create structures to govern the flow,
+etc.  The `logr.Logger` object can be passed to these other libraries, stored
+in structs, or even used as a package-global variable, if needed.  For example:
+
+```
+    app := createTheAppObject(logger)
+    app.Run()
+```
+
+Outside of this early setup, no other packages need to know about the choice of
+implementation.  They write logs in terms of the `logr.Logger` that they
+received:
+
+```
+    type appObject struct {
+        // ... other fields ...
+        logger logr.Logger
+        // ... other fields ...
+    }
+
+    func (app *appObject) Run() {
+        app.logger.Info("starting up", "timestamp", time.Now())
+
+        // ... app code ...
+```
+
+## Background
+
+If the Go standard library had defined an interface for logging, this project
+probably would not be needed.  Alas, here we are.
+
+### Inspiration
+
+Before you consider this package, please read [this blog post by the
+inimitable Dave Cheney][warning-makes-no-sense].  We really appreciate what
+he has to say, and it largely aligns with our own experiences.
+
+### Differences from Dave's ideas
+
+The main differences are:
+
+1. Dave basically proposes doing away with the notion of a logging API in favor
+of `fmt.Printf()`.  We disagree, especially when you consider things like output
+locations, timestamps, file and line decorations, and structured logging.  This
+package restricts the logging API to just 2 types of logs: info and error.
+
+Info logs are things you want to tell the user which are not errors.  Error
+logs are, well, errors.  If your code receives an `error` from a subordinate
+function call and is logging that `error` *and not returning it*, use error
+logs.
+
+2. Verbosity-levels on info logs.  This gives developers a chance to indicate
+arbitrary grades of importance for info logs, without assigning names with
+semantic meaning such as "warning", "trace", and "debug."  Superficially this
+may feel very similar, but the primary difference is the lack of semantics.
+Because verbosity is a numerical value, it's safe to assume that an app running
+with higher verbosity means more (and less important) logs will be generated.
+
+## Implementations (non-exhaustive)
+
+There are implementations for the following logging libraries:
+
+- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr)
+- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr)
+- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)
+- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr)
+- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting)
+- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)
+- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)
+- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)
+- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)
+- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)
+- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr)
+- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0)
+- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing)
+
+## FAQ
+
+### Conceptual
+
+#### Why structured logging?
+
+- **Structured logs are more easily queryable**: Since you've got
+  key-value pairs, it's much easier to query your structured logs for
+  particular values by filtering on the contents of a particular key --
+  think searching request logs for error codes, Kubernetes reconcilers for
+  the name and namespace of the reconciled object, etc.
+
+- **Structured logging makes it easier to have cross-referenceable logs**:
+  Similarly to searchability, if you maintain conventions around your
+  keys, it becomes easy to gather all log lines related to a particular
+  concept.
+
+- **Structured logs allow better dimensions of filtering**: if you have
+  structure to your logs, you've got more precise control over how much
+  information is logged -- you might choose in a particular configuration
+  to log certain keys but not others, only log lines where a certain key
+  matches a certain value, etc., instead of just having v-levels and names
+  to key off of.
+
+- **Structured logs better represent structured data**: sometimes, the
+  data that you want to log is inherently structured (think tuple-link
+  objects.)  Structured logs allow you to preserve that structure when
+  outputting.
+
+#### Why V-levels?
+
+**V-levels give operators an easy way to control the chattiness of log
+operations**.  V-levels provide a way for a given package to distinguish
+the relative importance or verbosity of a given log message.  Then, if
+a particular logger or package is logging too many messages, the user
+of the package can simply change the v-levels for that library.
+
+#### Why not named levels, like Info/Warning/Error?
+
+Read [Dave Cheney's post][warning-makes-no-sense].  Then read [Differences
+from Dave's ideas](#differences-from-daves-ideas).
+
+#### Why not allow format strings, too?
+
+**Format strings negate many of the benefits of structured logs**:
+
+- They're not easily searchable without resorting to fuzzy searching,
+  regular expressions, etc.
+
+- They don't store structured data well, since contents are flattened into
+  a string.
+
+- They're not cross-referenceable.
+
+- They don't compress easily, since the message is not constant.
+
+(Unless you turn positional parameters into key-value pairs with numerical
+keys, at which point you've gotten key-value logging with meaningless
+keys.)
+
+### Practical
+
+#### Why key-value pairs, and not a map?
+
+Key-value pairs are *much* easier to optimize, especially around
+allocations.  Zap (a structured logger that inspired logr's interface) has
+[performance measurements](https://github.com/uber-go/zap#performance)
+that show this quite nicely.
+
+While the interface ends up being a little less obvious, you get
+potentially better performance, plus avoid making users type
+`map[string]string{}` every time they want to log.
+
+#### What if my V-levels differ between libraries?
+
+That's fine.  Control your V-levels on a per-logger basis, and use the
+`WithName` method to pass different loggers to different libraries.
+
+Generally, you should take care to ensure that you have relatively
+consistent V-levels within a given logger, however, as this makes deciding
+on what verbosity of logs to request easier.
+
+#### But I really want to use a format string!
+
+That's not actually a question.  Assuming your question is "how do
+I convert my mental model of logging with format strings to logging with
+constant messages":
+
+1. Figure out what the error actually is, as you'd write in a TL;DR style,
+   and use that as a message.
+
+2. For every place you'd write a format specifier, look to the word before
+   it, and add that as a key value pair.
+
+For instance, consider the following examples (all taken from spots in the
+Kubernetes codebase):
+
+- `klog.V(4).Infof("Client is returning errors: code %v, error %v",
+  responseCode, err)` becomes `logger.Error(err, "client returned an
+  error", "code", responseCode)`
+
+- `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v",
+  seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after
+  response when requesting url", "attempt", retries, "after
+  seconds", seconds, "url", url)`
+
+If you *really* must use a format string, use it in a key's value, and
+call `fmt.Sprintf` yourself.  For instance: `log.Printf("unable to
+reflect over type %T")` becomes `logger.Info("unable to reflect over
+type", "type", fmt.Sprintf("%T"))`.  In general though, the cases where
+this is necessary should be few and far between.
+
+#### How do I choose my V-levels?
+
+This is basically the only hard constraint: increase V-levels to denote
+more verbose or more debug-y logs.
+
+Otherwise, you can start out with `0` as "you always want to see this",
+`1` as "common logging that you might *possibly* want to turn off", and
+`10` as "I would like to performance-test your log collection stack."
+
+Then gradually choose levels in between as you need them, working your way
+down from 10 (for debug and trace style logs) and up from 1 (for chattier
+info-type logs.)
+
+#### How do I choose my keys?
+
+Keys are fairly flexible, and can hold more or less any string
+value. For best compatibility with implementations and consistency
+with existing code in other projects, there are a few conventions you
+should consider.
+
+- Make your keys human-readable.
+- Constant keys are generally a good idea.
+- Be consistent across your codebase.
+- Keys should naturally match parts of the message string.
+- Use lower case for simple keys and
+  [lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for
+  more complex ones. Kubernetes is one example of a project that has
+  [adopted that
+  convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments).
+
+While key names are mostly unrestricted (and spaces are acceptable),
+it's generally a good idea to stick to printable ascii characters, or at
+least match the general character set of your log lines.
+
+#### Why should keys be constant values?
+
+The point of structured logging is to make later log processing easier.  Your
+keys are, effectively, the schema of each log message.  If you use different
+keys across instances of the same log line, you will make your structured logs
+much harder to use.  `Sprintf()` is for values, not for keys!
+
+#### Why is this not a pure interface?
+
+The Logger type is implemented as a struct in order to allow the Go compiler to
+optimize things like high-V `Info` logs that are not triggered.  Not all of
+these implementations are implemented yet, but this structure was suggested as
+a way to ensure they *can* be implemented.  All of the real work is behind the
+`LogSink` interface.
+
+[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging

+ 24 - 0
vendor/github.com/go-logr/logr/discard.go

@@ -0,0 +1,24 @@
+/*
+Copyright 2020 The logr Authors.
+
+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.
+*/
+
+package logr
+
+// Discard returns a Logger that discards all messages logged to it.  It can be
+// used whenever the caller is not interested in the logs.  Logger instances
+// produced by this function always compare as equal.
+func Discard() Logger {
+	return New(nil)
+}

+ 804 - 0
vendor/github.com/go-logr/logr/funcr/funcr.go

@@ -0,0 +1,804 @@
+/*
+Copyright 2021 The logr Authors.
+
+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.
+*/
+
+// Package funcr implements formatting of structured log messages and
+// optionally captures the call site and timestamp.
+//
+// The simplest way to use it is via its implementation of a
+// github.com/go-logr/logr.LogSink with output through an arbitrary
+// "write" function.  See New and NewJSON for details.
+//
+// # Custom LogSinks
+//
+// For users who need more control, a funcr.Formatter can be embedded inside
+// your own custom LogSink implementation. This is useful when the LogSink
+// needs to implement additional methods, for example.
+//
+// # Formatting
+//
+// This will respect logr.Marshaler, fmt.Stringer, and error interfaces for
+// values which are being logged.  When rendering a struct, funcr will use Go's
+// standard JSON tags (all except "string").
+package funcr
+
+import (
+	"bytes"
+	"encoding"
+	"encoding/json"
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-logr/logr"
+)
+
+// New returns a logr.Logger which is implemented by an arbitrary function.
+func New(fn func(prefix, args string), opts Options) logr.Logger {
+	return logr.New(newSink(fn, NewFormatter(opts)))
+}
+
+// NewJSON returns a logr.Logger which is implemented by an arbitrary function
+// and produces JSON output.
+func NewJSON(fn func(obj string), opts Options) logr.Logger {
+	fnWrapper := func(_, obj string) {
+		fn(obj)
+	}
+	return logr.New(newSink(fnWrapper, NewFormatterJSON(opts)))
+}
+
+// Underlier exposes access to the underlying logging function. Since
+// callers only have a logr.Logger, they have to know which
+// implementation is in use, so this interface is less of an
+// abstraction and more of a way to test type conversion.
+type Underlier interface {
+	GetUnderlying() func(prefix, args string)
+}
+
+func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
+	l := &fnlogger{
+		Formatter: formatter,
+		write:     fn,
+	}
+	// For skipping fnlogger.Info and fnlogger.Error.
+	l.Formatter.AddCallDepth(1)
+	return l
+}
+
+// Options carries parameters which influence the way logs are generated.
+type Options struct {
+	// LogCaller tells funcr to add a "caller" key to some or all log lines.
+	// This has some overhead, so some users might not want it.
+	LogCaller MessageClass
+
+	// LogCallerFunc tells funcr to also log the calling function name.  This
+	// has no effect if caller logging is not enabled (see Options.LogCaller).
+	LogCallerFunc bool
+
+	// LogTimestamp tells funcr to add a "ts" key to log lines.  This has some
+	// overhead, so some users might not want it.
+	LogTimestamp bool
+
+	// TimestampFormat tells funcr how to render timestamps when LogTimestamp
+	// is enabled.  If not specified, a default format will be used.  For more
+	// details, see docs for Go's time.Layout.
+	TimestampFormat string
+
+	// Verbosity tells funcr which V logs to produce.  Higher values enable
+	// more logs.  Info logs at or below this level will be written, while logs
+	// above this level will be discarded.
+	Verbosity int
+
+	// RenderBuiltinsHook allows users to mutate the list of key-value pairs
+	// while a log line is being rendered.  The kvList argument follows logr
+	// conventions - each pair of slice elements is comprised of a string key
+	// and an arbitrary value (verified and sanitized before calling this
+	// hook).  The value returned must follow the same conventions.  This hook
+	// can be used to audit or modify logged data.  For example, you might want
+	// to prefix all of funcr's built-in keys with some string.  This hook is
+	// only called for built-in (provided by funcr itself) key-value pairs.
+	// Equivalent hooks are offered for key-value pairs saved via
+	// logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and
+	// for user-provided pairs (see RenderArgsHook).
+	RenderBuiltinsHook func(kvList []interface{}) []interface{}
+
+	// RenderValuesHook is the same as RenderBuiltinsHook, except that it is
+	// only called for key-value pairs saved via logr.Logger.WithValues.  See
+	// RenderBuiltinsHook for more details.
+	RenderValuesHook func(kvList []interface{}) []interface{}
+
+	// RenderArgsHook is the same as RenderBuiltinsHook, except that it is only
+	// called for key-value pairs passed directly to Info and Error.  See
+	// RenderBuiltinsHook for more details.
+	RenderArgsHook func(kvList []interface{}) []interface{}
+
+	// MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct
+	// that contains a struct, etc.) it may log.  Every time it finds a struct,
+	// slice, array, or map the depth is increased by one.  When the maximum is
+	// reached, the value will be converted to a string indicating that the max
+	// depth has been exceeded.  If this field is not specified, a default
+	// value will be used.
+	MaxLogDepth int
+}
+
+// MessageClass indicates which category or categories of messages to consider.
+type MessageClass int
+
+const (
+	// None ignores all message classes.
+	None MessageClass = iota
+	// All considers all message classes.
+	All
+	// Info only considers info messages.
+	Info
+	// Error only considers error messages.
+	Error
+)
+
+// fnlogger inherits some of its LogSink implementation from Formatter
+// and just needs to add some glue code.
+type fnlogger struct {
+	Formatter
+	write func(prefix, args string)
+}
+
+func (l fnlogger) WithName(name string) logr.LogSink {
+	l.Formatter.AddName(name)
+	return &l
+}
+
+func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
+	l.Formatter.AddValues(kvList)
+	return &l
+}
+
+func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
+	l.Formatter.AddCallDepth(depth)
+	return &l
+}
+
+func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
+	prefix, args := l.FormatInfo(level, msg, kvList)
+	l.write(prefix, args)
+}
+
+func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
+	prefix, args := l.FormatError(err, msg, kvList)
+	l.write(prefix, args)
+}
+
+func (l fnlogger) GetUnderlying() func(prefix, args string) {
+	return l.write
+}
+
+// Assert conformance to the interfaces.
+var _ logr.LogSink = &fnlogger{}
+var _ logr.CallDepthLogSink = &fnlogger{}
+var _ Underlier = &fnlogger{}
+
+// NewFormatter constructs a Formatter which emits a JSON-like key=value format.
+func NewFormatter(opts Options) Formatter {
+	return newFormatter(opts, outputKeyValue)
+}
+
+// NewFormatterJSON constructs a Formatter which emits strict JSON.
+func NewFormatterJSON(opts Options) Formatter {
+	return newFormatter(opts, outputJSON)
+}
+
+// Defaults for Options.
+const defaultTimestampFormat = "2006-01-02 15:04:05.000000"
+const defaultMaxLogDepth = 16
+
+func newFormatter(opts Options, outfmt outputFormat) Formatter {
+	if opts.TimestampFormat == "" {
+		opts.TimestampFormat = defaultTimestampFormat
+	}
+	if opts.MaxLogDepth == 0 {
+		opts.MaxLogDepth = defaultMaxLogDepth
+	}
+	f := Formatter{
+		outputFormat: outfmt,
+		prefix:       "",
+		values:       nil,
+		depth:        0,
+		opts:         &opts,
+	}
+	return f
+}
+
+// Formatter is an opaque struct which can be embedded in a LogSink
+// implementation. It should be constructed with NewFormatter. Some of
+// its methods directly implement logr.LogSink.
+type Formatter struct {
+	outputFormat outputFormat
+	prefix       string
+	values       []interface{}
+	valuesStr    string
+	depth        int
+	opts         *Options
+}
+
+// outputFormat indicates which outputFormat to use.
+type outputFormat int
+
+const (
+	// outputKeyValue emits a JSON-like key=value format, but not strict JSON.
+	outputKeyValue outputFormat = iota
+	// outputJSON emits strict JSON.
+	outputJSON
+)
+
+// PseudoStruct is a list of key-value pairs that gets logged as a struct.
+type PseudoStruct []interface{}
+
+// render produces a log line, ready to use.
+func (f Formatter) render(builtins, args []interface{}) string {
+	// Empirically bytes.Buffer is faster than strings.Builder for this.
+	buf := bytes.NewBuffer(make([]byte, 0, 1024))
+	if f.outputFormat == outputJSON {
+		buf.WriteByte('{')
+	}
+	vals := builtins
+	if hook := f.opts.RenderBuiltinsHook; hook != nil {
+		vals = hook(f.sanitize(vals))
+	}
+	f.flatten(buf, vals, false, false) // keys are ours, no need to escape
+	continuing := len(builtins) > 0
+	if len(f.valuesStr) > 0 {
+		if continuing {
+			if f.outputFormat == outputJSON {
+				buf.WriteByte(',')
+			} else {
+				buf.WriteByte(' ')
+			}
+		}
+		continuing = true
+		buf.WriteString(f.valuesStr)
+	}
+	vals = args
+	if hook := f.opts.RenderArgsHook; hook != nil {
+		vals = hook(f.sanitize(vals))
+	}
+	f.flatten(buf, vals, continuing, true) // escape user-provided keys
+	if f.outputFormat == outputJSON {
+		buf.WriteByte('}')
+	}
+	return buf.String()
+}
+
+// flatten renders a list of key-value pairs into a buffer.  If continuing is
+// true, it assumes that the buffer has previous values and will emit a
+// separator (which depends on the output format) before the first pair it
+// writes.  If escapeKeys is true, the keys are assumed to have
+// non-JSON-compatible characters in them and must be evaluated for escapes.
+//
+// This function returns a potentially modified version of kvList, which
+// ensures that there is a value for every key (adding a value if needed) and
+// that each key is a string (substituting a key if needed).
+func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} {
+	// This logic overlaps with sanitize() but saves one type-cast per key,
+	// which can be measurable.
+	if len(kvList)%2 != 0 {
+		kvList = append(kvList, noValue)
+	}
+	for i := 0; i < len(kvList); i += 2 {
+		k, ok := kvList[i].(string)
+		if !ok {
+			k = f.nonStringKey(kvList[i])
+			kvList[i] = k
+		}
+		v := kvList[i+1]
+
+		if i > 0 || continuing {
+			if f.outputFormat == outputJSON {
+				buf.WriteByte(',')
+			} else {
+				// In theory the format could be something we don't understand.  In
+				// practice, we control it, so it won't be.
+				buf.WriteByte(' ')
+			}
+		}
+
+		if escapeKeys {
+			buf.WriteString(prettyString(k))
+		} else {
+			// this is faster
+			buf.WriteByte('"')
+			buf.WriteString(k)
+			buf.WriteByte('"')
+		}
+		if f.outputFormat == outputJSON {
+			buf.WriteByte(':')
+		} else {
+			buf.WriteByte('=')
+		}
+		buf.WriteString(f.pretty(v))
+	}
+	return kvList
+}
+
+func (f Formatter) pretty(value interface{}) string {
+	return f.prettyWithFlags(value, 0, 0)
+}
+
+const (
+	flagRawStruct = 0x1 // do not print braces on structs
+)
+
+// TODO: This is not fast. Most of the overhead goes here.
+func (f Formatter) prettyWithFlags(value interface{}, flags uint32, depth int) string {
+	if depth > f.opts.MaxLogDepth {
+		return `"<max-log-depth-exceeded>"`
+	}
+
+	// Handle types that take full control of logging.
+	if v, ok := value.(logr.Marshaler); ok {
+		// Replace the value with what the type wants to get logged.
+		// That then gets handled below via reflection.
+		value = invokeMarshaler(v)
+	}
+
+	// Handle types that want to format themselves.
+	switch v := value.(type) {
+	case fmt.Stringer:
+		value = invokeStringer(v)
+	case error:
+		value = invokeError(v)
+	}
+
+	// Handling the most common types without reflect is a small perf win.
+	switch v := value.(type) {
+	case bool:
+		return strconv.FormatBool(v)
+	case string:
+		return prettyString(v)
+	case int:
+		return strconv.FormatInt(int64(v), 10)
+	case int8:
+		return strconv.FormatInt(int64(v), 10)
+	case int16:
+		return strconv.FormatInt(int64(v), 10)
+	case int32:
+		return strconv.FormatInt(int64(v), 10)
+	case int64:
+		return strconv.FormatInt(int64(v), 10)
+	case uint:
+		return strconv.FormatUint(uint64(v), 10)
+	case uint8:
+		return strconv.FormatUint(uint64(v), 10)
+	case uint16:
+		return strconv.FormatUint(uint64(v), 10)
+	case uint32:
+		return strconv.FormatUint(uint64(v), 10)
+	case uint64:
+		return strconv.FormatUint(v, 10)
+	case uintptr:
+		return strconv.FormatUint(uint64(v), 10)
+	case float32:
+		return strconv.FormatFloat(float64(v), 'f', -1, 32)
+	case float64:
+		return strconv.FormatFloat(v, 'f', -1, 64)
+	case complex64:
+		return `"` + strconv.FormatComplex(complex128(v), 'f', -1, 64) + `"`
+	case complex128:
+		return `"` + strconv.FormatComplex(v, 'f', -1, 128) + `"`
+	case PseudoStruct:
+		buf := bytes.NewBuffer(make([]byte, 0, 1024))
+		v = f.sanitize(v)
+		if flags&flagRawStruct == 0 {
+			buf.WriteByte('{')
+		}
+		for i := 0; i < len(v); i += 2 {
+			if i > 0 {
+				buf.WriteByte(',')
+			}
+			k, _ := v[i].(string) // sanitize() above means no need to check success
+			// arbitrary keys might need escaping
+			buf.WriteString(prettyString(k))
+			buf.WriteByte(':')
+			buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
+		}
+		if flags&flagRawStruct == 0 {
+			buf.WriteByte('}')
+		}
+		return buf.String()
+	}
+
+	buf := bytes.NewBuffer(make([]byte, 0, 256))
+	t := reflect.TypeOf(value)
+	if t == nil {
+		return "null"
+	}
+	v := reflect.ValueOf(value)
+	switch t.Kind() {
+	case reflect.Bool:
+		return strconv.FormatBool(v.Bool())
+	case reflect.String:
+		return prettyString(v.String())
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return strconv.FormatInt(int64(v.Int()), 10)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return strconv.FormatUint(uint64(v.Uint()), 10)
+	case reflect.Float32:
+		return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
+	case reflect.Float64:
+		return strconv.FormatFloat(v.Float(), 'f', -1, 64)
+	case reflect.Complex64:
+		return `"` + strconv.FormatComplex(complex128(v.Complex()), 'f', -1, 64) + `"`
+	case reflect.Complex128:
+		return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"`
+	case reflect.Struct:
+		if flags&flagRawStruct == 0 {
+			buf.WriteByte('{')
+		}
+		printComma := false // testing i>0 is not enough because of JSON omitted fields
+		for i := 0; i < t.NumField(); i++ {
+			fld := t.Field(i)
+			if fld.PkgPath != "" {
+				// reflect says this field is only defined for non-exported fields.
+				continue
+			}
+			if !v.Field(i).CanInterface() {
+				// reflect isn't clear exactly what this means, but we can't use it.
+				continue
+			}
+			name := ""
+			omitempty := false
+			if tag, found := fld.Tag.Lookup("json"); found {
+				if tag == "-" {
+					continue
+				}
+				if comma := strings.Index(tag, ","); comma != -1 {
+					if n := tag[:comma]; n != "" {
+						name = n
+					}
+					rest := tag[comma:]
+					if strings.Contains(rest, ",omitempty,") || strings.HasSuffix(rest, ",omitempty") {
+						omitempty = true
+					}
+				} else {
+					name = tag
+				}
+			}
+			if omitempty && isEmpty(v.Field(i)) {
+				continue
+			}
+			if printComma {
+				buf.WriteByte(',')
+			}
+			printComma = true // if we got here, we are rendering a field
+			if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
+				buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1))
+				continue
+			}
+			if name == "" {
+				name = fld.Name
+			}
+			// field names can't contain characters which need escaping
+			buf.WriteByte('"')
+			buf.WriteString(name)
+			buf.WriteByte('"')
+			buf.WriteByte(':')
+			buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
+		}
+		if flags&flagRawStruct == 0 {
+			buf.WriteByte('}')
+		}
+		return buf.String()
+	case reflect.Slice, reflect.Array:
+		// If this is outputing as JSON make sure this isn't really a json.RawMessage.
+		// If so just emit "as-is" and don't pretty it as that will just print
+		// it as [X,Y,Z,...] which isn't terribly useful vs the string form you really want.
+		if f.outputFormat == outputJSON {
+			if rm, ok := value.(json.RawMessage); ok {
+				// If it's empty make sure we emit an empty value as the array style would below.
+				if len(rm) > 0 {
+					buf.Write(rm)
+				} else {
+					buf.WriteString("null")
+				}
+				return buf.String()
+			}
+		}
+		buf.WriteByte('[')
+		for i := 0; i < v.Len(); i++ {
+			if i > 0 {
+				buf.WriteByte(',')
+			}
+			e := v.Index(i)
+			buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
+		}
+		buf.WriteByte(']')
+		return buf.String()
+	case reflect.Map:
+		buf.WriteByte('{')
+		// This does not sort the map keys, for best perf.
+		it := v.MapRange()
+		i := 0
+		for it.Next() {
+			if i > 0 {
+				buf.WriteByte(',')
+			}
+			// If a map key supports TextMarshaler, use it.
+			keystr := ""
+			if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok {
+				txt, err := m.MarshalText()
+				if err != nil {
+					keystr = fmt.Sprintf("<error-MarshalText: %s>", err.Error())
+				} else {
+					keystr = string(txt)
+				}
+				keystr = prettyString(keystr)
+			} else {
+				// prettyWithFlags will produce already-escaped values
+				keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1)
+				if t.Key().Kind() != reflect.String {
+					// JSON only does string keys.  Unlike Go's standard JSON, we'll
+					// convert just about anything to a string.
+					keystr = prettyString(keystr)
+				}
+			}
+			buf.WriteString(keystr)
+			buf.WriteByte(':')
+			buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
+			i++
+		}
+		buf.WriteByte('}')
+		return buf.String()
+	case reflect.Ptr, reflect.Interface:
+		if v.IsNil() {
+			return "null"
+		}
+		return f.prettyWithFlags(v.Elem().Interface(), 0, depth)
+	}
+	return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
+}
+
+func prettyString(s string) string {
+	// Avoid escaping (which does allocations) if we can.
+	if needsEscape(s) {
+		return strconv.Quote(s)
+	}
+	b := bytes.NewBuffer(make([]byte, 0, 1024))
+	b.WriteByte('"')
+	b.WriteString(s)
+	b.WriteByte('"')
+	return b.String()
+}
+
+// needsEscape determines whether the input string needs to be escaped or not,
+// without doing any allocations.
+func needsEscape(s string) bool {
+	for _, r := range s {
+		if !strconv.IsPrint(r) || r == '\\' || r == '"' {
+			return true
+		}
+	}
+	return false
+}
+
+func isEmpty(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+		return v.Len() == 0
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Complex64, reflect.Complex128:
+		return v.Complex() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+	return false
+}
+
+func invokeMarshaler(m logr.Marshaler) (ret interface{}) {
+	defer func() {
+		if r := recover(); r != nil {
+			ret = fmt.Sprintf("<panic: %s>", r)
+		}
+	}()
+	return m.MarshalLog()
+}
+
+func invokeStringer(s fmt.Stringer) (ret string) {
+	defer func() {
+		if r := recover(); r != nil {
+			ret = fmt.Sprintf("<panic: %s>", r)
+		}
+	}()
+	return s.String()
+}
+
+func invokeError(e error) (ret string) {
+	defer func() {
+		if r := recover(); r != nil {
+			ret = fmt.Sprintf("<panic: %s>", r)
+		}
+	}()
+	return e.Error()
+}
+
+// Caller represents the original call site for a log line, after considering
+// logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper.  The File and
+// Line fields will always be provided, while the Func field is optional.
+// Users can set the render hook fields in Options to examine logged key-value
+// pairs, one of which will be {"caller", Caller} if the Options.LogCaller
+// field is enabled for the given MessageClass.
+type Caller struct {
+	// File is the basename of the file for this call site.
+	File string `json:"file"`
+	// Line is the line number in the file for this call site.
+	Line int `json:"line"`
+	// Func is the function name for this call site, or empty if
+	// Options.LogCallerFunc is not enabled.
+	Func string `json:"function,omitempty"`
+}
+
+func (f Formatter) caller() Caller {
+	// +1 for this frame, +1 for Info/Error.
+	pc, file, line, ok := runtime.Caller(f.depth + 2)
+	if !ok {
+		return Caller{"<unknown>", 0, ""}
+	}
+	fn := ""
+	if f.opts.LogCallerFunc {
+		if fp := runtime.FuncForPC(pc); fp != nil {
+			fn = fp.Name()
+		}
+	}
+
+	return Caller{filepath.Base(file), line, fn}
+}
+
+const noValue = "<no-value>"
+
+func (f Formatter) nonStringKey(v interface{}) string {
+	return fmt.Sprintf("<non-string-key: %s>", f.snippet(v))
+}
+
+// snippet produces a short snippet string of an arbitrary value.
+func (f Formatter) snippet(v interface{}) string {
+	const snipLen = 16
+
+	snip := f.pretty(v)
+	if len(snip) > snipLen {
+		snip = snip[:snipLen]
+	}
+	return snip
+}
+
+// sanitize ensures that a list of key-value pairs has a value for every key
+// (adding a value if needed) and that each key is a string (substituting a key
+// if needed).
+func (f Formatter) sanitize(kvList []interface{}) []interface{} {
+	if len(kvList)%2 != 0 {
+		kvList = append(kvList, noValue)
+	}
+	for i := 0; i < len(kvList); i += 2 {
+		_, ok := kvList[i].(string)
+		if !ok {
+			kvList[i] = f.nonStringKey(kvList[i])
+		}
+	}
+	return kvList
+}
+
+// Init configures this Formatter from runtime info, such as the call depth
+// imposed by logr itself.
+// Note that this receiver is a pointer, so depth can be saved.
+func (f *Formatter) Init(info logr.RuntimeInfo) {
+	f.depth += info.CallDepth
+}
+
+// Enabled checks whether an info message at the given level should be logged.
+func (f Formatter) Enabled(level int) bool {
+	return level <= f.opts.Verbosity
+}
+
+// GetDepth returns the current depth of this Formatter.  This is useful for
+// implementations which do their own caller attribution.
+func (f Formatter) GetDepth() int {
+	return f.depth
+}
+
+// FormatInfo renders an Info log message into strings.  The prefix will be
+// empty when no names were set (via AddNames), or when the output is
+// configured for JSON.
+func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) {
+	args := make([]interface{}, 0, 64) // using a constant here impacts perf
+	prefix = f.prefix
+	if f.outputFormat == outputJSON {
+		args = append(args, "logger", prefix)
+		prefix = ""
+	}
+	if f.opts.LogTimestamp {
+		args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
+	}
+	if policy := f.opts.LogCaller; policy == All || policy == Info {
+		args = append(args, "caller", f.caller())
+	}
+	args = append(args, "level", level, "msg", msg)
+	return prefix, f.render(args, kvList)
+}
+
+// FormatError renders an Error log message into strings.  The prefix will be
+// empty when no names were set (via AddNames),  or when the output is
+// configured for JSON.
+func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) {
+	args := make([]interface{}, 0, 64) // using a constant here impacts perf
+	prefix = f.prefix
+	if f.outputFormat == outputJSON {
+		args = append(args, "logger", prefix)
+		prefix = ""
+	}
+	if f.opts.LogTimestamp {
+		args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
+	}
+	if policy := f.opts.LogCaller; policy == All || policy == Error {
+		args = append(args, "caller", f.caller())
+	}
+	args = append(args, "msg", msg)
+	var loggableErr interface{}
+	if err != nil {
+		loggableErr = err.Error()
+	}
+	args = append(args, "error", loggableErr)
+	return f.prefix, f.render(args, kvList)
+}
+
+// AddName appends the specified name.  funcr uses '/' characters to separate
+// name elements.  Callers should not pass '/' in the provided name string, but
+// this library does not actually enforce that.
+func (f *Formatter) AddName(name string) {
+	if len(f.prefix) > 0 {
+		f.prefix += "/"
+	}
+	f.prefix += name
+}
+
+// AddValues adds key-value pairs to the set of saved values to be logged with
+// each log line.
+func (f *Formatter) AddValues(kvList []interface{}) {
+	// Three slice args forces a copy.
+	n := len(f.values)
+	f.values = append(f.values[:n:n], kvList...)
+
+	vals := f.values
+	if hook := f.opts.RenderValuesHook; hook != nil {
+		vals = hook(f.sanitize(vals))
+	}
+
+	// Pre-render values, so we don't have to do it on each Info/Error call.
+	buf := bytes.NewBuffer(make([]byte, 0, 1024))
+	f.flatten(buf, vals, false, true) // escape user-provided keys
+	f.valuesStr = buf.String()
+}
+
+// AddCallDepth increases the number of stack-frames to skip when attributing
+// the log line to a file and line.
+func (f *Formatter) AddCallDepth(depth int) {
+	f.depth += depth
+}

+ 550 - 0
vendor/github.com/go-logr/logr/logr.go

@@ -0,0 +1,550 @@
+/*
+Copyright 2019 The logr Authors.
+
+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.
+*/
+
+// This design derives from Dave Cheney's blog:
+//     http://dave.cheney.net/2015/11/05/lets-talk-about-logging
+
+// Package logr defines a general-purpose logging API and abstract interfaces
+// to back that API.  Packages in the Go ecosystem can depend on this package,
+// while callers can implement logging with whatever backend is appropriate.
+//
+// # Usage
+//
+// Logging is done using a Logger instance.  Logger is a concrete type with
+// methods, which defers the actual logging to a LogSink interface.  The main
+// methods of Logger are Info() and Error().  Arguments to Info() and Error()
+// are key/value pairs rather than printf-style formatted strings, emphasizing
+// "structured logging".
+//
+// With Go's standard log package, we might write:
+//
+//	log.Printf("setting target value %s", targetValue)
+//
+// With logr's structured logging, we'd write:
+//
+//	logger.Info("setting target", "value", targetValue)
+//
+// Errors are much the same.  Instead of:
+//
+//	log.Printf("failed to open the pod bay door for user %s: %v", user, err)
+//
+// We'd write:
+//
+//	logger.Error(err, "failed to open the pod bay door", "user", user)
+//
+// Info() and Error() are very similar, but they are separate methods so that
+// LogSink implementations can choose to do things like attach additional
+// information (such as stack traces) on calls to Error(). Error() messages are
+// always logged, regardless of the current verbosity.  If there is no error
+// instance available, passing nil is valid.
+//
+// # Verbosity
+//
+// Often we want to log information only when the application in "verbose
+// mode".  To write log lines that are more verbose, Logger has a V() method.
+// The higher the V-level of a log line, the less critical it is considered.
+// Log-lines with V-levels that are not enabled (as per the LogSink) will not
+// be written.  Level V(0) is the default, and logger.V(0).Info() has the same
+// meaning as logger.Info().  Negative V-levels have the same meaning as V(0).
+// Error messages do not have a verbosity level and are always logged.
+//
+// Where we might have written:
+//
+//	if flVerbose >= 2 {
+//	    log.Printf("an unusual thing happened")
+//	}
+//
+// We can write:
+//
+//	logger.V(2).Info("an unusual thing happened")
+//
+// # Logger Names
+//
+// Logger instances can have name strings so that all messages logged through
+// that instance have additional context.  For example, you might want to add
+// a subsystem name:
+//
+//	logger.WithName("compactor").Info("started", "time", time.Now())
+//
+// The WithName() method returns a new Logger, which can be passed to
+// constructors or other functions for further use.  Repeated use of WithName()
+// will accumulate name "segments".  These name segments will be joined in some
+// way by the LogSink implementation.  It is strongly recommended that name
+// segments contain simple identifiers (letters, digits, and hyphen), and do
+// not contain characters that could muddle the log output or confuse the
+// joining operation (e.g. whitespace, commas, periods, slashes, brackets,
+// quotes, etc).
+//
+// # Saved Values
+//
+// Logger instances can store any number of key/value pairs, which will be
+// logged alongside all messages logged through that instance.  For example,
+// you might want to create a Logger instance per managed object:
+//
+// With the standard log package, we might write:
+//
+//	log.Printf("decided to set field foo to value %q for object %s/%s",
+//	    targetValue, object.Namespace, object.Name)
+//
+// With logr we'd write:
+//
+//	// Elsewhere: set up the logger to log the object name.
+//	obj.logger = mainLogger.WithValues(
+//	    "name", obj.name, "namespace", obj.namespace)
+//
+//	// later on...
+//	obj.logger.Info("setting foo", "value", targetValue)
+//
+// # Best Practices
+//
+// Logger has very few hard rules, with the goal that LogSink implementations
+// might have a lot of freedom to differentiate.  There are, however, some
+// things to consider.
+//
+// The log message consists of a constant message attached to the log line.
+// This should generally be a simple description of what's occurring, and should
+// never be a format string.  Variable information can then be attached using
+// named values.
+//
+// Keys are arbitrary strings, but should generally be constant values.  Values
+// may be any Go value, but how the value is formatted is determined by the
+// LogSink implementation.
+//
+// Logger instances are meant to be passed around by value. Code that receives
+// such a value can call its methods without having to check whether the
+// instance is ready for use.
+//
+// Calling methods with the null logger (Logger{}) as instance will crash
+// because it has no LogSink. Therefore this null logger should never be passed
+// around. For cases where passing a logger is optional, a pointer to Logger
+// should be used.
+//
+// # Key Naming Conventions
+//
+// Keys are not strictly required to conform to any specification or regex, but
+// it is recommended that they:
+//   - be human-readable and meaningful (not auto-generated or simple ordinals)
+//   - be constant (not dependent on input data)
+//   - contain only printable characters
+//   - not contain whitespace or punctuation
+//   - use lower case for simple keys and lowerCamelCase for more complex ones
+//
+// These guidelines help ensure that log data is processed properly regardless
+// of the log implementation.  For example, log implementations will try to
+// output JSON data or will store data for later database (e.g. SQL) queries.
+//
+// While users are generally free to use key names of their choice, it's
+// generally best to avoid using the following keys, as they're frequently used
+// by implementations:
+//   - "caller": the calling information (file/line) of a particular log line
+//   - "error": the underlying error value in the `Error` method
+//   - "level": the log level
+//   - "logger": the name of the associated logger
+//   - "msg": the log message
+//   - "stacktrace": the stack trace associated with a particular log line or
+//     error (often from the `Error` message)
+//   - "ts": the timestamp for a log line
+//
+// Implementations are encouraged to make use of these keys to represent the
+// above concepts, when necessary (for example, in a pure-JSON output form, it
+// would be necessary to represent at least message and timestamp as ordinary
+// named values).
+//
+// # Break Glass
+//
+// Implementations may choose to give callers access to the underlying
+// logging implementation.  The recommended pattern for this is:
+//
+//	// Underlier exposes access to the underlying logging implementation.
+//	// Since callers only have a logr.Logger, they have to know which
+//	// implementation is in use, so this interface is less of an abstraction
+//	// and more of way to test type conversion.
+//	type Underlier interface {
+//	    GetUnderlying() <underlying-type>
+//	}
+//
+// Logger grants access to the sink to enable type assertions like this:
+//
+//	func DoSomethingWithImpl(log logr.Logger) {
+//	    if underlier, ok := log.GetSink().(impl.Underlier); ok {
+//	       implLogger := underlier.GetUnderlying()
+//	       ...
+//	    }
+//	}
+//
+// Custom `With*` functions can be implemented by copying the complete
+// Logger struct and replacing the sink in the copy:
+//
+//	// WithFooBar changes the foobar parameter in the log sink and returns a
+//	// new logger with that modified sink.  It does nothing for loggers where
+//	// the sink doesn't support that parameter.
+//	func WithFoobar(log logr.Logger, foobar int) logr.Logger {
+//	   if foobarLogSink, ok := log.GetSink().(FoobarSink); ok {
+//	      log = log.WithSink(foobarLogSink.WithFooBar(foobar))
+//	   }
+//	   return log
+//	}
+//
+// Don't use New to construct a new Logger with a LogSink retrieved from an
+// existing Logger. Source code attribution might not work correctly and
+// unexported fields in Logger get lost.
+//
+// Beware that the same LogSink instance may be shared by different logger
+// instances. Calling functions that modify the LogSink will affect all of
+// those.
+package logr
+
+import (
+	"context"
+)
+
+// New returns a new Logger instance.  This is primarily used by libraries
+// implementing LogSink, rather than end users.  Passing a nil sink will create
+// a Logger which discards all log lines.
+func New(sink LogSink) Logger {
+	logger := Logger{}
+	logger.setSink(sink)
+	if sink != nil {
+		sink.Init(runtimeInfo)
+	}
+	return logger
+}
+
+// setSink stores the sink and updates any related fields. It mutates the
+// logger and thus is only safe to use for loggers that are not currently being
+// used concurrently.
+func (l *Logger) setSink(sink LogSink) {
+	l.sink = sink
+}
+
+// GetSink returns the stored sink.
+func (l Logger) GetSink() LogSink {
+	return l.sink
+}
+
+// WithSink returns a copy of the logger with the new sink.
+func (l Logger) WithSink(sink LogSink) Logger {
+	l.setSink(sink)
+	return l
+}
+
+// Logger is an interface to an abstract logging implementation.  This is a
+// concrete type for performance reasons, but all the real work is passed on to
+// a LogSink.  Implementations of LogSink should provide their own constructors
+// that return Logger, not LogSink.
+//
+// The underlying sink can be accessed through GetSink and be modified through
+// WithSink. This enables the implementation of custom extensions (see "Break
+// Glass" in the package documentation). Normally the sink should be used only
+// indirectly.
+type Logger struct {
+	sink  LogSink
+	level int
+}
+
+// Enabled tests whether this Logger is enabled.  For example, commandline
+// flags might be used to set the logging verbosity and disable some info logs.
+func (l Logger) Enabled() bool {
+	return l.sink != nil && l.sink.Enabled(l.level)
+}
+
+// Info logs a non-error message with the given key/value pairs as context.
+//
+// The msg argument should be used to add some constant description to the log
+// line.  The key/value pairs can then be used to add additional variable
+// information.  The key/value pairs must alternate string keys and arbitrary
+// values.
+func (l Logger) Info(msg string, keysAndValues ...interface{}) {
+	if l.sink == nil {
+		return
+	}
+	if l.Enabled() {
+		if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
+			withHelper.GetCallStackHelper()()
+		}
+		l.sink.Info(l.level, msg, keysAndValues...)
+	}
+}
+
+// Error logs an error, with the given message and key/value pairs as context.
+// It functions similarly to Info, but may have unique behavior, and should be
+// preferred for logging errors (see the package documentations for more
+// information). The log message will always be emitted, regardless of
+// verbosity level.
+//
+// The msg argument should be used to add context to any underlying error,
+// while the err argument should be used to attach the actual error that
+// triggered this log line, if present. The err parameter is optional
+// and nil may be passed instead of an error instance.
+func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
+	if l.sink == nil {
+		return
+	}
+	if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
+		withHelper.GetCallStackHelper()()
+	}
+	l.sink.Error(err, msg, keysAndValues...)
+}
+
+// V returns a new Logger instance for a specific verbosity level, relative to
+// this Logger.  In other words, V-levels are additive.  A higher verbosity
+// level means a log message is less important.  Negative V-levels are treated
+// as 0.
+func (l Logger) V(level int) Logger {
+	if l.sink == nil {
+		return l
+	}
+	if level < 0 {
+		level = 0
+	}
+	l.level += level
+	return l
+}
+
+// WithValues returns a new Logger instance with additional key/value pairs.
+// See Info for documentation on how key/value pairs work.
+func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
+	if l.sink == nil {
+		return l
+	}
+	l.setSink(l.sink.WithValues(keysAndValues...))
+	return l
+}
+
+// WithName returns a new Logger instance with the specified name element added
+// to the Logger's name.  Successive calls with WithName append additional
+// suffixes to the Logger's name.  It's strongly recommended that name segments
+// contain only letters, digits, and hyphens (see the package documentation for
+// more information).
+func (l Logger) WithName(name string) Logger {
+	if l.sink == nil {
+		return l
+	}
+	l.setSink(l.sink.WithName(name))
+	return l
+}
+
+// WithCallDepth returns a Logger instance that offsets the call stack by the
+// specified number of frames when logging call site information, if possible.
+// This is useful for users who have helper functions between the "real" call
+// site and the actual calls to Logger methods.  If depth is 0 the attribution
+// should be to the direct caller of this function.  If depth is 1 the
+// attribution should skip 1 call frame, and so on.  Successive calls to this
+// are additive.
+//
+// If the underlying log implementation supports a WithCallDepth(int) method,
+// it will be called and the result returned.  If the implementation does not
+// support CallDepthLogSink, the original Logger will be returned.
+//
+// To skip one level, WithCallStackHelper() should be used instead of
+// WithCallDepth(1) because it works with implementions that support the
+// CallDepthLogSink and/or CallStackHelperLogSink interfaces.
+func (l Logger) WithCallDepth(depth int) Logger {
+	if l.sink == nil {
+		return l
+	}
+	if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
+		l.setSink(withCallDepth.WithCallDepth(depth))
+	}
+	return l
+}
+
+// WithCallStackHelper returns a new Logger instance that skips the direct
+// caller when logging call site information, if possible.  This is useful for
+// users who have helper functions between the "real" call site and the actual
+// calls to Logger methods and want to support loggers which depend on marking
+// each individual helper function, like loggers based on testing.T.
+//
+// In addition to using that new logger instance, callers also must call the
+// returned function.
+//
+// If the underlying log implementation supports a WithCallDepth(int) method,
+// WithCallDepth(1) will be called to produce a new logger. If it supports a
+// WithCallStackHelper() method, that will be also called. If the
+// implementation does not support either of these, the original Logger will be
+// returned.
+func (l Logger) WithCallStackHelper() (func(), Logger) {
+	if l.sink == nil {
+		return func() {}, l
+	}
+	var helper func()
+	if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
+		l.setSink(withCallDepth.WithCallDepth(1))
+	}
+	if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
+		helper = withHelper.GetCallStackHelper()
+	} else {
+		helper = func() {}
+	}
+	return helper, l
+}
+
+// IsZero returns true if this logger is an uninitialized zero value
+func (l Logger) IsZero() bool {
+	return l.sink == nil
+}
+
+// contextKey is how we find Loggers in a context.Context.
+type contextKey struct{}
+
+// FromContext returns a Logger from ctx or an error if no Logger is found.
+func FromContext(ctx context.Context) (Logger, error) {
+	if v, ok := ctx.Value(contextKey{}).(Logger); ok {
+		return v, nil
+	}
+
+	return Logger{}, notFoundError{}
+}
+
+// notFoundError exists to carry an IsNotFound method.
+type notFoundError struct{}
+
+func (notFoundError) Error() string {
+	return "no logr.Logger was present"
+}
+
+func (notFoundError) IsNotFound() bool {
+	return true
+}
+
+// FromContextOrDiscard returns a Logger from ctx.  If no Logger is found, this
+// returns a Logger that discards all log messages.
+func FromContextOrDiscard(ctx context.Context) Logger {
+	if v, ok := ctx.Value(contextKey{}).(Logger); ok {
+		return v
+	}
+
+	return Discard()
+}
+
+// NewContext returns a new Context, derived from ctx, which carries the
+// provided Logger.
+func NewContext(ctx context.Context, logger Logger) context.Context {
+	return context.WithValue(ctx, contextKey{}, logger)
+}
+
+// RuntimeInfo holds information that the logr "core" library knows which
+// LogSinks might want to know.
+type RuntimeInfo struct {
+	// CallDepth is the number of call frames the logr library adds between the
+	// end-user and the LogSink.  LogSink implementations which choose to print
+	// the original logging site (e.g. file & line) should climb this many
+	// additional frames to find it.
+	CallDepth int
+}
+
+// runtimeInfo is a static global.  It must not be changed at run time.
+var runtimeInfo = RuntimeInfo{
+	CallDepth: 1,
+}
+
+// LogSink represents a logging implementation.  End-users will generally not
+// interact with this type.
+type LogSink interface {
+	// Init receives optional information about the logr library for LogSink
+	// implementations that need it.
+	Init(info RuntimeInfo)
+
+	// Enabled tests whether this LogSink is enabled at the specified V-level.
+	// For example, commandline flags might be used to set the logging
+	// verbosity and disable some info logs.
+	Enabled(level int) bool
+
+	// Info logs a non-error message with the given key/value pairs as context.
+	// The level argument is provided for optional logging.  This method will
+	// only be called when Enabled(level) is true. See Logger.Info for more
+	// details.
+	Info(level int, msg string, keysAndValues ...interface{})
+
+	// Error logs an error, with the given message and key/value pairs as
+	// context.  See Logger.Error for more details.
+	Error(err error, msg string, keysAndValues ...interface{})
+
+	// WithValues returns a new LogSink with additional key/value pairs.  See
+	// Logger.WithValues for more details.
+	WithValues(keysAndValues ...interface{}) LogSink
+
+	// WithName returns a new LogSink with the specified name appended.  See
+	// Logger.WithName for more details.
+	WithName(name string) LogSink
+}
+
+// CallDepthLogSink represents a LogSink that knows how to climb the call stack
+// to identify the original call site and can offset the depth by a specified
+// number of frames.  This is useful for users who have helper functions
+// between the "real" call site and the actual calls to Logger methods.
+// Implementations that log information about the call site (such as file,
+// function, or line) would otherwise log information about the intermediate
+// helper functions.
+//
+// This is an optional interface and implementations are not required to
+// support it.
+type CallDepthLogSink interface {
+	// WithCallDepth returns a LogSink that will offset the call
+	// stack by the specified number of frames when logging call
+	// site information.
+	//
+	// If depth is 0, the LogSink should skip exactly the number
+	// of call frames defined in RuntimeInfo.CallDepth when Info
+	// or Error are called, i.e. the attribution should be to the
+	// direct caller of Logger.Info or Logger.Error.
+	//
+	// If depth is 1 the attribution should skip 1 call frame, and so on.
+	// Successive calls to this are additive.
+	WithCallDepth(depth int) LogSink
+}
+
+// CallStackHelperLogSink represents a LogSink that knows how to climb
+// the call stack to identify the original call site and can skip
+// intermediate helper functions if they mark themselves as
+// helper. Go's testing package uses that approach.
+//
+// This is useful for users who have helper functions between the
+// "real" call site and the actual calls to Logger methods.
+// Implementations that log information about the call site (such as
+// file, function, or line) would otherwise log information about the
+// intermediate helper functions.
+//
+// This is an optional interface and implementations are not required
+// to support it. Implementations that choose to support this must not
+// simply implement it as WithCallDepth(1), because
+// Logger.WithCallStackHelper will call both methods if they are
+// present. This should only be implemented for LogSinks that actually
+// need it, as with testing.T.
+type CallStackHelperLogSink interface {
+	// GetCallStackHelper returns a function that must be called
+	// to mark the direct caller as helper function when logging
+	// call site information.
+	GetCallStackHelper() func()
+}
+
+// Marshaler is an optional interface that logged values may choose to
+// implement. Loggers with structured output, such as JSON, should
+// log the object return by the MarshalLog method instead of the
+// original value.
+type Marshaler interface {
+	// MarshalLog can be used to:
+	//   - ensure that structs are not logged as strings when the original
+	//     value has a String method: return a different type without a
+	//     String method
+	//   - select which fields of a complex type should get logged:
+	//     return a simpler struct with fewer fields
+	//   - log unexported fields: return a different struct
+	//     with exported fields
+	//
+	// It may return any value of any type.
+	MarshalLog() interface{}
+}

+ 201 - 0
vendor/github.com/go-logr/stdr/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.

+ 6 - 0
vendor/github.com/go-logr/stdr/README.md

@@ -0,0 +1,6 @@
+# Minimal Go logging using logr and Go's standard library
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/stdr.svg)](https://pkg.go.dev/github.com/go-logr/stdr)
+
+This package implements the [logr interface](https://github.com/go-logr/logr)
+in terms of Go's standard log package(https://pkg.go.dev/log).

+ 170 - 0
vendor/github.com/go-logr/stdr/stdr.go

@@ -0,0 +1,170 @@
+/*
+Copyright 2019 The logr Authors.
+
+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.
+*/
+
+// Package stdr implements github.com/go-logr/logr.Logger in terms of
+// Go's standard log package.
+package stdr
+
+import (
+	"log"
+	"os"
+
+	"github.com/go-logr/logr"
+	"github.com/go-logr/logr/funcr"
+)
+
+// The global verbosity level.  See SetVerbosity().
+var globalVerbosity int
+
+// SetVerbosity sets the global level against which all info logs will be
+// compared.  If this is greater than or equal to the "V" of the logger, the
+// message will be logged.  A higher value here means more logs will be written.
+// The previous verbosity value is returned.  This is not concurrent-safe -
+// callers must be sure to call it from only one goroutine.
+func SetVerbosity(v int) int {
+	old := globalVerbosity
+	globalVerbosity = v
+	return old
+}
+
+// New returns a logr.Logger which is implemented by Go's standard log package,
+// or something like it.  If std is nil, this will use a default logger
+// instead.
+//
+// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
+func New(std StdLogger) logr.Logger {
+	return NewWithOptions(std, Options{})
+}
+
+// NewWithOptions returns a logr.Logger which is implemented by Go's standard
+// log package, or something like it.  See New for details.
+func NewWithOptions(std StdLogger, opts Options) logr.Logger {
+	if std == nil {
+		// Go's log.Default() is only available in 1.16 and higher.
+		std = log.New(os.Stderr, "", log.LstdFlags)
+	}
+
+	if opts.Depth < 0 {
+		opts.Depth = 0
+	}
+
+	fopts := funcr.Options{
+		LogCaller: funcr.MessageClass(opts.LogCaller),
+	}
+
+	sl := &logger{
+		Formatter: funcr.NewFormatter(fopts),
+		std:       std,
+	}
+
+	// For skipping our own logger.Info/Error.
+	sl.Formatter.AddCallDepth(1 + opts.Depth)
+
+	return logr.New(sl)
+}
+
+// Options carries parameters which influence the way logs are generated.
+type Options struct {
+	// Depth biases the assumed number of call frames to the "true" caller.
+	// This is useful when the calling code calls a function which then calls
+	// stdr (e.g. a logging shim to another API).  Values less than zero will
+	// be treated as zero.
+	Depth int
+
+	// LogCaller tells stdr to add a "caller" key to some or all log lines.
+	// Go's log package has options to log this natively, too.
+	LogCaller MessageClass
+
+	// TODO: add an option to log the date/time
+}
+
+// MessageClass indicates which category or categories of messages to consider.
+type MessageClass int
+
+const (
+	// None ignores all message classes.
+	None MessageClass = iota
+	// All considers all message classes.
+	All
+	// Info only considers info messages.
+	Info
+	// Error only considers error messages.
+	Error
+)
+
+// StdLogger is the subset of the Go stdlib log.Logger API that is needed for
+// this adapter.
+type StdLogger interface {
+	// Output is the same as log.Output and log.Logger.Output.
+	Output(calldepth int, logline string) error
+}
+
+type logger struct {
+	funcr.Formatter
+	std StdLogger
+}
+
+var _ logr.LogSink = &logger{}
+var _ logr.CallDepthLogSink = &logger{}
+
+func (l logger) Enabled(level int) bool {
+	return globalVerbosity >= level
+}
+
+func (l logger) Info(level int, msg string, kvList ...interface{}) {
+	prefix, args := l.FormatInfo(level, msg, kvList)
+	if prefix != "" {
+		args = prefix + ": " + args
+	}
+	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
+}
+
+func (l logger) Error(err error, msg string, kvList ...interface{}) {
+	prefix, args := l.FormatError(err, msg, kvList)
+	if prefix != "" {
+		args = prefix + ": " + args
+	}
+	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
+}
+
+func (l logger) WithName(name string) logr.LogSink {
+	l.Formatter.AddName(name)
+	return &l
+}
+
+func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
+	l.Formatter.AddValues(kvList)
+	return &l
+}
+
+func (l logger) WithCallDepth(depth int) logr.LogSink {
+	l.Formatter.AddCallDepth(depth)
+	return &l
+}
+
+// Underlier exposes access to the underlying logging implementation.  Since
+// callers only have a logr.Logger, they have to know which implementation is
+// in use, so this interface is less of an abstraction and more of way to test
+// type conversion.
+type Underlier interface {
+	GetUnderlying() StdLogger
+}
+
+// GetUnderlying returns the StdLogger underneath this logger.  Since StdLogger
+// is itself an interface, the result may or may not be a Go log.Logger.
+func (l logger) GetUnderlying() StdLogger {
+	return l.std
+}

+ 21 - 0
vendor/github.com/gogf/gf/v2/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 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.

+ 8 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray.go

@@ -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 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_func.go

@@ -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)
+}

+ 870 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_normal_any.go

@@ -0,0 +1,870 @@
+// 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/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/text/gstr"
+	"github.com/gogf/gf/v2/util/gconv"
+	"github.com/gogf/gf/v2/util/grand"
+)
+
+// 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 an 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{}, 0)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice = append(slice, 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 `values` to the front of `index`.
+func (a *Array) InsertBefore(index int, values ...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], values...)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the `values` to the back of `index`.
+func (a *Array) InsertAfter(index int, values ...interface{}) error {
+	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], values...)
+	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 {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if i := a.doSearchWithoutLock(value); i != -1 {
+		a.doRemoveWithoutLock(i)
+		return true
+	}
+	return false
+}
+
+// RemoveValues removes multiple items by `values`.
+func (a *Array) RemoveValues(values ...interface{}) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i := a.doSearchWithoutLock(value); i != -1 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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()
+	return a.doSearchWithoutLock(value)
+}
+
+func (a *Array) doSearchWithoutLock(value interface{}) int {
+	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()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	var (
+		ok          bool
+		temp        interface{}
+		uniqueSet   = make(map[interface{}]struct{})
+		uniqueArray = make([]interface{}, 0, len(a.array))
+	)
+	for i := 0; i < len(a.array); i++ {
+		temp = a.array[i]
+		if _, ok = uniqueSet[temp]; ok {
+			continue
+		}
+		uniqueSet[temp] = struct{}{}
+		uniqueArray = append(uniqueArray, temp)
+	}
+	a.array = uniqueArray
+	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 {
+	if a == nil {
+		return ""
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	s := ""
+	for k, v := range a.array {
+		s = gconv.String(v)
+		if gstr.IsNumeric(s) {
+			buffer.WriteString(s)
+		} else {
+			buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
+		}
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a 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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *Array) Filter(filter func(index int, value interface{}) bool) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// 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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *Array) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]interface{}, len(a.array))
+	for i, v := range a.array {
+		newSlice[i] = deepcopy.Copy(v)
+	}
+	return NewArrayFrom(newSlice, a.mu.IsSafe())
+}

+ 846 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_normal_int.go

@@ -0,0 +1,846 @@
+// 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 an 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, 0)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice = append(slice, 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 `values` to the front of `index`.
+func (a *IntArray) InsertBefore(index int, values ...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], values...)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the `value` to the back of `index`.
+func (a *IntArray) InsertAfter(index int, values ...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], values...)
+	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 {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if i := a.doSearchWithoutLock(value); i != -1 {
+		a.doRemoveWithoutLock(i)
+		return true
+	}
+	return false
+}
+
+// RemoveValues removes multiple items by `values`.
+func (a *IntArray) RemoveValues(values ...int) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i := a.doSearchWithoutLock(value); i != -1 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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()
+	return a.doSearchWithoutLock(value)
+}
+
+func (a *IntArray) doSearchWithoutLock(value int) int {
+	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()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	var (
+		ok          bool
+		temp        int
+		uniqueSet   = make(map[int]struct{})
+		uniqueArray = make([]int, 0, len(a.array))
+	)
+	for i := 0; i < len(a.array); i++ {
+		temp = a.array[i]
+		if _, ok = uniqueSet[temp]; ok {
+			continue
+		}
+		uniqueSet[temp] = struct{}{}
+		uniqueArray = append(uniqueArray, temp)
+	}
+	a.array = uniqueArray
+	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 {
+	if a == nil {
+		return ""
+	}
+	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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *IntArray) Filter(filter func(index int, value int) bool) *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// FilterEmpty removes all 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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *IntArray) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]int, len(a.array))
+	copy(newSlice, a.array)
+	return NewIntArrayFrom(newSlice, a.mu.IsSafe())
+}

+ 857 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_normal_str.go

@@ -0,0 +1,857 @@
+// 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 `values` to the front of `index`.
+func (a *StrArray) InsertBefore(index int, values ...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], values...)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the `values` to the back of `index`.
+func (a *StrArray) InsertAfter(index int, values ...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], values...)
+	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
+}
+
+// RemoveValues removes multiple items by `values`.
+func (a *StrArray) RemoveValues(values ...string) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i := a.doSearchWithoutLock(value); i != -1 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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()
+	return a.doSearchWithoutLock(value)
+}
+
+func (a *StrArray) doSearchWithoutLock(value string) int {
+	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()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	var (
+		ok          bool
+		temp        string
+		uniqueSet   = make(map[string]struct{})
+		uniqueArray = make([]string, 0, len(a.array))
+	)
+	for i := 0; i < len(a.array); i++ {
+		temp = a.array[i]
+		if _, ok = uniqueSet[temp]; ok {
+			continue
+		}
+		uniqueSet[temp] = struct{}{}
+		uniqueArray = append(uniqueArray, temp)
+	}
+	a.array = uniqueArray
+	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 {
+	if a == nil {
+		return ""
+	}
+	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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *StrArray) Filter(filter func(index int, value string) bool) *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// FilterEmpty removes all empty 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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *StrArray) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]string, len(a.array))
+	copy(newSlice, a.array)
+	return NewStrArrayFrom(newSlice, a.mu.IsSafe())
+}

+ 842 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_any.go

@@ -0,0 +1,842 @@
+// 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/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/text/gstr"
+	"github.com/gogf/gf/v2/util/gconv"
+	"github.com/gogf/gf/v2/util/grand"
+	"github.com/gogf/gf/v2/util/gutil"
+)
+
+// SortedArray is a golang sorted array with rich features.
+// 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 `a` < `b`; the `a` will be inserted before `b`;
+// if it returns value = 0, means `a` = `b`; the `a` will be replaced by     `b`;
+// if it returns value > 0, means `a` > `b`; the `a` will be inserted after  `b`;
+func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	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 an array by a range from `start` to `end`
+// with step value `step`.
+func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	if step == 0 {
+		panic(fmt.Sprintf(`invalid step value: %d`, step))
+	}
+	slice := make([]interface{}, 0)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice = append(slice, 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 {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if i, r := a.binSearch(value, false); r == 0 {
+		_, res := a.doRemoveWithoutLock(i)
+		return res
+	}
+	return false
+}
+
+// RemoveValues removes an item by `values`.
+func (a *SortedArray) RemoveValues(values ...interface{}) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i, r := a.binSearch(value, false); r == 0 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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 does unique check, remove all repeated items.
+func (a *SortedArray) SetUnique(unique bool) *SortedArray {
+	oldUnique := a.unique
+	a.unique = unique
+	if unique && oldUnique != unique {
+		a.Unique()
+	}
+	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 {
+	if a == nil {
+		return ""
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	s := ""
+	for k, v := range a.array {
+		s = gconv.String(v)
+		if gstr.IsNumeric(s) {
+			buffer.WriteString(s)
+		} else {
+			buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
+		}
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a 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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *SortedArray) Filter(filter func(index int, value interface{}) bool) *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *SortedArray) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]interface{}, len(a.array))
+	for i, v := range a.array {
+		newSlice[i] = deepcopy.Copy(v)
+	}
+	return NewSortedArrayFrom(newSlice, a.comparator, a.mu.IsSafe())
+}

+ 787 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_int.go

@@ -0,0 +1,787 @@
+// 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 an 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, 0)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice = append(slice, 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 {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if i, r := a.binSearch(value, false); r == 0 {
+		_, res := a.doRemoveWithoutLock(i)
+		return res
+	}
+	return false
+}
+
+// RemoveValues removes an item by `values`.
+func (a *SortedIntArray) RemoveValues(values ...int) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i, r := a.binSearch(value, false); r == 0 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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 {
+	if a == nil {
+		return ""
+	}
+	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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *SortedIntArray) Filter(filter func(index int, value int) bool) *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// 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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *SortedIntArray) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]int, len(a.array))
+	copy(newSlice, a.array)
+	return NewSortedIntArrayFrom(newSlice, a.mu.IsSafe())
+}

+ 800 - 0
vendor/github.com/gogf/gf/v2/container/garray/garray_sorted_str.go

@@ -0,0 +1,800 @@
+// 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 {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if i, r := a.binSearch(value, false); r == 0 {
+		_, res := a.doRemoveWithoutLock(i)
+		return res
+	}
+	return false
+}
+
+// RemoveValues removes an item by `values`.
+func (a *SortedStrArray) RemoveValues(values ...string) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		if i, r := a.binSearch(value, false); r == 0 {
+			a.doRemoveWithoutLock(i)
+		}
+	}
+}
+
+// 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 {
+	if a == nil {
+		return ""
+	}
+	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
+}
+
+// Filter iterates array and filters elements using custom callback function.
+// It removes the element from array if callback function `filter` returns true,
+// it or else does nothing and continues iterating.
+func (a *SortedStrArray) Filter(filter func(index int, value string) bool) *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if filter(i, a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// 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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (a *SortedStrArray) DeepCopy() interface{} {
+	if a == nil {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	newSlice := make([]string, len(a.array))
+	copy(newSlice, a.array)
+	return NewSortedStrArrayFrom(newSlice, a.mu.IsSafe())
+}

+ 572 - 0
vendor/github.com/gogf/gf/v2/container/glist/glist.go

@@ -0,0 +1,572 @@
+// 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/deepcopy"
+	"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)
+	}
+}
+
+// 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 {
+	if l == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (l *List) DeepCopy() interface{} {
+	if l == nil {
+		return nil
+	}
+
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+
+	if l.list == nil {
+		return nil
+	}
+	var (
+		length = l.list.Len()
+		values = make([]interface{}, length)
+	)
+	if length > 0 {
+		for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
+			values[i] = deepcopy.Copy(e.Value)
+		}
+	}
+	return NewFrom(values, l.mu.IsSafe())
+}

+ 45 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap.go

@@ -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...)
+}

+ 563 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_any_any_map.go

@@ -0,0 +1,563 @@
+// 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/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+	"reflect"
+)
+
+// AnyAnyMap wraps map type `map[interface{}]interface{}` and provides more map features.
+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 shallow 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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *AnyAnyMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[interface{}]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = deepcopy.Copy(v)
+	}
+	return NewFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *AnyAnyMap) IsSubOf(other *AnyAnyMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *AnyAnyMap) Diff(other *AnyAnyMap) (addedKeys, removedKeys, updatedKeys []interface{}) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if !reflect.DeepEqual(m.data[key], other.data[key]) {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 564 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_any_map.go

@@ -0,0 +1,564 @@
+// 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/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+	"reflect"
+)
+
+// IntAnyMap implements map[int]interface{} with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *IntAnyMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[int]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = deepcopy.Copy(v)
+	}
+	return NewIntAnyMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *IntAnyMap) IsSubOf(other *IntAnyMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *IntAnyMap) Diff(other *IntAnyMap) (addedKeys, removedKeys, updatedKeys []int) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if !reflect.DeepEqual(m.data[key], other.data[key]) {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 533 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_int_map.go

@@ -0,0 +1,533 @@
+// 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"
+)
+
+// IntIntMap implements map[int]int with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *IntIntMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	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 NewIntIntMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *IntIntMap) IsSubOf(other *IntIntMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *IntIntMap) Diff(other *IntIntMap) (addedKeys, removedKeys, updatedKeys []int) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if m.data[key] != other.data[key] {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 533 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_int_str_map.go

@@ -0,0 +1,533 @@
+// 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"
+)
+
+// IntStrMap implements map[int]string with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *IntStrMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	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 NewIntStrMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *IntStrMap) IsSubOf(other *IntStrMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *IntStrMap) Diff(other *IntStrMap) (addedKeys, removedKeys, updatedKeys []int) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if m.data[key] != other.data[key] {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 551 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_any_map.go

@@ -0,0 +1,551 @@
+// 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 (
+	"reflect"
+
+	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/internal/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// StrAnyMap implements map[string]interface{} with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *StrAnyMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = deepcopy.Copy(v)
+	}
+	return NewStrAnyMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *StrAnyMap) IsSubOf(other *StrAnyMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *StrAnyMap) Diff(other *StrAnyMap) (addedKeys, removedKeys, updatedKeys []string) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if !reflect.DeepEqual(m.data[key], other.data[key]) {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 537 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_int_map.go

@@ -0,0 +1,537 @@
+// 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"
+)
+
+// StrIntMap implements map[string]int with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *StrIntMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	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 NewStrIntMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *StrIntMap) IsSubOf(other *StrIntMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *StrIntMap) Diff(other *StrIntMap) (addedKeys, removedKeys, updatedKeys []string) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if m.data[key] != other.data[key] {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 526 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_hash_str_str_map.go

@@ -0,0 +1,526 @@
+// 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"
+)
+
+// StrStrMap implements map[string]string with RWMutex that has switch.
+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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *StrStrMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	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 NewStrStrMapFrom(data, m.mu.IsSafe())
+}
+
+// IsSubOf checks whether the current map is a sub-map of `other`.
+func (m *StrStrMap) IsSubOf(other *StrStrMap) bool {
+	if m == other {
+		return true
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key, value := range m.data {
+		otherValue, ok := other.data[key]
+		if !ok {
+			return false
+		}
+		if otherValue != value {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff compares current map `m` with map `other` and returns their different keys.
+// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
+// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
+// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
+func (m *StrStrMap) Diff(other *StrStrMap) (addedKeys, removedKeys, updatedKeys []string) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+
+	for key := range m.data {
+		if _, ok := other.data[key]; !ok {
+			removedKeys = append(removedKeys, key)
+		} else if m.data[key] != other.data[key] {
+			updatedKeys = append(updatedKeys, key)
+		}
+	}
+	for key := range other.data {
+		if _, ok := m.data[key]; !ok {
+			addedKeys = append(addedKeys, key)
+		}
+	}
+	return
+}

+ 612 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_list_map.go

@@ -0,0 +1,612 @@
+// 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/deepcopy"
+	"github.com/gogf/gf/v2/internal/empty"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/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 {
+	if m == nil {
+		return ""
+	}
+	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
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (m *ListMap) DeepCopy() interface{} {
+	if m == nil {
+		return nil
+	}
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[interface{}]interface{}, len(m.data))
+	if m.list != nil {
+		var node *gListMapNode
+		m.list.IteratorAsc(func(e *glist.Element) bool {
+			node = e.Value.(*gListMapNode)
+			data[node.key] = deepcopy.Copy(node.value)
+			return true
+		})
+	}
+	return NewListMapFrom(data, m.mu.IsSafe())
+}

+ 30 - 0
vendor/github.com/gogf/gf/v2/container/gmap/gmap_tree_map.go

@@ -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...)
+}

+ 188 - 0
vendor/github.com/gogf/gf/v2/container/gpool/gpool.go

@@ -0,0 +1,188 @@
+// 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 gpool provides object-reusable concurrent-safe pool.
+package gpool
+
+import (
+	"context"
+	"time"
+
+	"github.com/gogf/gf/v2/container/glist"
+	"github.com/gogf/gf/v2/container/gtype"
+	"github.com/gogf/gf/v2/errors/gcode"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/os/gtime"
+	"github.com/gogf/gf/v2/os/gtimer"
+)
+
+// Pool is an Object-Reusable Pool.
+type Pool struct {
+	list    *glist.List                 // Available/idle items list.
+	closed  *gtype.Bool                 // Whether the pool is closed.
+	TTL     time.Duration               // Time To Live for pool items.
+	NewFunc func() (interface{}, error) // Callback function to create pool item.
+	// ExpireFunc is the for expired items destruction.
+	// This function needs to be defined when the pool items
+	// need to perform additional destruction operations.
+	// Eg: net.Conn, os.File, etc.
+	ExpireFunc func(interface{})
+}
+
+// Pool item.
+type poolItem struct {
+	value    interface{} // Item value.
+	expireAt int64       // Expire timestamp in milliseconds.
+}
+
+// NewFunc Creation function for object.
+type NewFunc func() (interface{}, error)
+
+// ExpireFunc Destruction function for object.
+type ExpireFunc func(interface{})
+
+// New creates and returns a new object pool.
+// To ensure execution efficiency, the expiration time cannot be modified once it is set.
+//
+// Note the expiration logic:
+// ttl = 0 : not expired;
+// ttl < 0 : immediate expired after use;
+// ttl > 0 : timeout expired;
+func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
+	r := &Pool{
+		list:    glist.New(true),
+		closed:  gtype.NewBool(),
+		TTL:     ttl,
+		NewFunc: newFunc,
+	}
+	if len(expireFunc) > 0 {
+		r.ExpireFunc = expireFunc[0]
+	}
+	gtimer.AddSingleton(context.Background(), time.Second, r.checkExpireItems)
+	return r
+}
+
+// Put puts an item to pool.
+func (p *Pool) Put(value interface{}) error {
+	if p.closed.Val() {
+		return gerror.NewCode(gcode.CodeInvalidOperation, "pool is closed")
+	}
+	item := &poolItem{
+		value: value,
+	}
+	if p.TTL == 0 {
+		item.expireAt = 0
+	} else {
+		// As for Golang version < 1.13, there's no method Milliseconds for time.Duration.
+		// So we need calculate the milliseconds using its nanoseconds value.
+		item.expireAt = gtime.TimestampMilli() + p.TTL.Nanoseconds()/1000000
+	}
+	p.list.PushBack(item)
+	return nil
+}
+
+// MustPut puts an item to pool, it panics if any error occurs.
+func (p *Pool) MustPut(value interface{}) {
+	if err := p.Put(value); err != nil {
+		panic(err)
+	}
+}
+
+// Clear clears pool, which means it will remove all items from pool.
+func (p *Pool) Clear() {
+	if p.ExpireFunc != nil {
+		for {
+			if r := p.list.PopFront(); r != nil {
+				p.ExpireFunc(r.(*poolItem).value)
+			} else {
+				break
+			}
+		}
+	} else {
+		p.list.RemoveAll()
+	}
+}
+
+// Get picks and returns an item from pool. If the pool is empty and NewFunc is defined,
+// it creates and returns one from NewFunc.
+func (p *Pool) Get() (interface{}, error) {
+	for !p.closed.Val() {
+		if r := p.list.PopFront(); r != nil {
+			f := r.(*poolItem)
+			if f.expireAt == 0 || f.expireAt > gtime.TimestampMilli() {
+				return f.value, nil
+			} else if p.ExpireFunc != nil {
+				// TODO: move expire function calling asynchronously out from `Get` operation.
+				p.ExpireFunc(f.value)
+			}
+		} else {
+			break
+		}
+	}
+	if p.NewFunc != nil {
+		return p.NewFunc()
+	}
+	return nil, gerror.NewCode(gcode.CodeInvalidOperation, "pool is empty")
+}
+
+// Size returns the count of available items of pool.
+func (p *Pool) Size() int {
+	return p.list.Len()
+}
+
+// Close closes the pool. If `p` has ExpireFunc,
+// then it automatically closes all items using this function before it's closed.
+// Commonly you do not need to call this function manually.
+func (p *Pool) Close() {
+	p.closed.Set(true)
+}
+
+// checkExpire removes expired items from pool in every second.
+func (p *Pool) checkExpireItems(ctx context.Context) {
+	if p.closed.Val() {
+		// If p has ExpireFunc,
+		// then it must close all items using this function.
+		if p.ExpireFunc != nil {
+			for {
+				if r := p.list.PopFront(); r != nil {
+					p.ExpireFunc(r.(*poolItem).value)
+				} else {
+					break
+				}
+			}
+		}
+		gtimer.Exit()
+	}
+	// All items do not expire.
+	if p.TTL == 0 {
+		return
+	}
+	// The latest item expire timestamp in milliseconds.
+	var latestExpire int64 = -1
+	// Retrieve the current timestamp in milliseconds, it expires the items
+	// by comparing with this timestamp. It is not accurate comparison for
+	// every item expired, but high performance.
+	var timestampMilli = gtime.TimestampMilli()
+	for {
+		if latestExpire > timestampMilli {
+			break
+		}
+		if r := p.list.PopFront(); r != nil {
+			item := r.(*poolItem)
+			latestExpire = item.expireAt
+			// TODO improve the auto-expiration mechanism of the pool.
+			if item.expireAt > timestampMilli {
+				p.list.PushFront(item)
+				break
+			}
+			if p.ExpireFunc != nil {
+				p.ExpireFunc(item.value)
+			}
+		} else {
+			break
+		}
+	}
+}

+ 144 - 0
vendor/github.com/gogf/gf/v2/container/gqueue/gqueue.go

@@ -0,0 +1,144 @@
+// 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 gqueue provides dynamic/static concurrent-safe queue.
+//
+// Features:
+//
+// 1. FIFO queue(data -> list -> chan);
+//
+// 2. Fast creation and initialization;
+//
+// 3. Support dynamic queue size(unlimited queue size);
+//
+// 4. Blocking when reading data from queue;
+package gqueue
+
+import (
+	"math"
+
+	"github.com/gogf/gf/v2/container/glist"
+	"github.com/gogf/gf/v2/container/gtype"
+)
+
+// Queue is a concurrent-safe queue built on doubly linked list and channel.
+type Queue struct {
+	limit  int              // Limit for queue size.
+	list   *glist.List      // Underlying list structure for data maintaining.
+	closed *gtype.Bool      // Whether queue is closed.
+	events chan struct{}    // Events for data writing.
+	C      chan interface{} // Underlying channel for data reading.
+}
+
+const (
+	defaultQueueSize = 10000 // Size for queue buffer.
+	defaultBatchSize = 10    // Max batch size per-fetching from list.
+)
+
+// New returns an empty queue object.
+// Optional parameter `limit` is used to limit the size of the queue, which is unlimited in default.
+// When `limit` is given, the queue will be static and high performance which is comparable with stdlib channel.
+func New(limit ...int) *Queue {
+	q := &Queue{
+		closed: gtype.NewBool(),
+	}
+	if len(limit) > 0 && limit[0] > 0 {
+		q.limit = limit[0]
+		q.C = make(chan interface{}, limit[0])
+	} else {
+		q.list = glist.New(true)
+		q.events = make(chan struct{}, math.MaxInt32)
+		q.C = make(chan interface{}, defaultQueueSize)
+		go q.asyncLoopFromListToChannel()
+	}
+	return q
+}
+
+// Push pushes the data `v` into the queue.
+// Note that it would panic if Push is called after the queue is closed.
+func (q *Queue) Push(v interface{}) {
+	if q.limit > 0 {
+		q.C <- v
+	} else {
+		q.list.PushBack(v)
+		if len(q.events) < defaultQueueSize {
+			q.events <- struct{}{}
+		}
+	}
+}
+
+// Pop pops an item from the queue in FIFO way.
+// Note that it would return nil immediately if Pop is called after the queue is closed.
+func (q *Queue) Pop() interface{} {
+	return <-q.C
+}
+
+// Close closes the queue.
+// Notice: It would notify all goroutines return immediately,
+// which are being blocked reading using Pop method.
+func (q *Queue) Close() {
+	if !q.closed.Cas(false, true) {
+		return
+	}
+	if q.events != nil {
+		close(q.events)
+	}
+	if q.limit > 0 {
+		close(q.C)
+	} else {
+		for i := 0; i < defaultBatchSize; i++ {
+			q.Pop()
+		}
+	}
+}
+
+// Len returns the length of the queue.
+// Note that the result might not be accurate if using unlimited queue size as there's an
+// asynchronous channel reading the list constantly.
+func (q *Queue) Len() (length int64) {
+	bufferedSize := int64(len(q.C))
+	if q.limit > 0 {
+		return bufferedSize
+	}
+	return int64(q.list.Size()) + bufferedSize
+}
+
+// Size is alias of Len.
+// Deprecated: use Len instead.
+func (q *Queue) Size() int64 {
+	return q.Len()
+}
+
+// asyncLoopFromListToChannel starts an asynchronous goroutine,
+// which handles the data synchronization from list `q.list` to channel `q.C`.
+func (q *Queue) asyncLoopFromListToChannel() {
+	defer func() {
+		if q.closed.Val() {
+			_ = recover()
+		}
+	}()
+	for !q.closed.Val() {
+		<-q.events
+		for !q.closed.Val() {
+			if bufferLength := q.list.Len(); bufferLength > 0 {
+				// When q.C is closed, it will panic here, especially q.C is being blocked for writing.
+				// If any error occurs here, it will be caught by recover and be ignored.
+				for i := 0; i < bufferLength; i++ {
+					q.C <- q.list.PopFront()
+				}
+			} else {
+				break
+			}
+		}
+		// Clear q.events to remain just one event to do the next synchronization check.
+		for i := 0; i < len(q.events)-1; i++ {
+			<-q.events
+		}
+	}
+	// It should be here to close `q.C` if `q` is unlimited size.
+	// It's the sender's responsibility to close channel when it should be closed.
+	close(q.C)
+}

+ 526 - 0
vendor/github.com/gogf/gf/v2/container/gset/gset_any_set.go

@@ -0,0 +1,526 @@
+// 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 gset provides kinds of concurrent-safe/unsafe sets.
+package gset
+
+import (
+	"bytes"
+
+	"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"
+)
+
+type Set struct {
+	mu   rwmutex.RWMutex
+	data map[interface{}]struct{}
+}
+
+// New create and returns a new set, which contains un-repeated items.
+// The parameter `safe` is used to specify whether using set in concurrent-safety,
+// which is false in default.
+func New(safe ...bool) *Set {
+	return NewSet(safe...)
+}
+
+// NewSet create and returns a new set, which contains un-repeated items.
+// Also see New.
+func NewSet(safe ...bool) *Set {
+	return &Set{
+		data: make(map[interface{}]struct{}),
+		mu:   rwmutex.Create(safe...),
+	}
+}
+
+// NewFrom returns a new set from `items`.
+// Parameter `items` can be either a variable of any type, or a slice.
+func NewFrom(items interface{}, safe ...bool) *Set {
+	m := make(map[interface{}]struct{})
+	for _, v := range gconv.Interfaces(items) {
+		m[v] = struct{}{}
+	}
+	return &Set{
+		data: m,
+		mu:   rwmutex.Create(safe...),
+	}
+}
+
+// Iterator iterates the set readonly with given callback function `f`,
+// if `f` returns true then continue iterating; or false to stop.
+func (set *Set) Iterator(f func(v interface{}) bool) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		if !f(k) {
+			break
+		}
+	}
+}
+
+// Add adds one or multiple items to the set.
+func (set *Set) Add(items ...interface{}) {
+	set.mu.Lock()
+	if set.data == nil {
+		set.data = make(map[interface{}]struct{})
+	}
+	for _, v := range items {
+		set.data[v] = struct{}{}
+	}
+	set.mu.Unlock()
+}
+
+// AddIfNotExist checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set,
+// or else it does nothing and returns false.
+//
+// Note that, if `item` is nil, it does nothing and returns false.
+func (set *Set) AddIfNotExist(item interface{}) bool {
+	if item == nil {
+		return false
+	}
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[interface{}]struct{})
+		}
+		if _, ok := set.data[item]; !ok {
+			set.data[item] = struct{}{}
+			return true
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exist in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, if `item` is nil, it does nothing and returns false. The function `f`
+// is executed without writing lock.
+func (set *Set) AddIfNotExistFunc(item interface{}, f func() bool) bool {
+	if item == nil {
+		return false
+	}
+	if !set.Contains(item) {
+		if f() {
+			set.mu.Lock()
+			defer set.mu.Unlock()
+			if set.data == nil {
+				set.data = make(map[interface{}]struct{})
+			}
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFuncLock checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, if `item` is nil, it does nothing and returns false. The function `f`
+// is executed within writing lock.
+func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() bool) bool {
+	if item == nil {
+		return false
+	}
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[interface{}]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains `item`.
+func (set *Set) Contains(item interface{}) bool {
+	var ok bool
+	set.mu.RLock()
+	if set.data != nil {
+		_, ok = set.data[item]
+	}
+	set.mu.RUnlock()
+	return ok
+}
+
+// Remove deletes `item` from set.
+func (set *Set) Remove(item interface{}) {
+	set.mu.Lock()
+	if set.data != nil {
+		delete(set.data, item)
+	}
+	set.mu.Unlock()
+}
+
+// Size returns the size of the set.
+func (set *Set) Size() int {
+	set.mu.RLock()
+	l := len(set.data)
+	set.mu.RUnlock()
+	return l
+}
+
+// Clear deletes all items of the set.
+func (set *Set) Clear() {
+	set.mu.Lock()
+	set.data = make(map[interface{}]struct{})
+	set.mu.Unlock()
+}
+
+// Slice returns all items of the set as slice.
+func (set *Set) Slice() []interface{} {
+	set.mu.RLock()
+	var (
+		i   = 0
+		ret = make([]interface{}, len(set.data))
+	)
+	for item := range set.data {
+		ret[i] = item
+		i++
+	}
+	set.mu.RUnlock()
+	return ret
+}
+
+// Join joins items with a string `glue`.
+func (set *Set) Join(glue string) string {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if len(set.data) == 0 {
+		return ""
+	}
+	var (
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	for k := range set.data {
+		buffer.WriteString(gconv.String(k))
+		if i != l-1 {
+			buffer.WriteString(glue)
+		}
+		i++
+	}
+	return buffer.String()
+}
+
+// String returns items as a string, which implements like json.Marshal does.
+func (set *Set) String() string {
+	if set == nil {
+		return ""
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		s      string
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	buffer.WriteByte('[')
+	for k := range set.data {
+		s = gconv.String(k)
+		if gstr.IsNumeric(s) {
+			buffer.WriteString(s)
+		} else {
+			buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
+		}
+		if i != l-1 {
+			buffer.WriteByte(',')
+		}
+		i++
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// LockFunc locks writing with callback function `f`.
+func (set *Set) LockFunc(f func(m map[interface{}]struct{})) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	f(set.data)
+}
+
+// RLockFunc locks reading with callback function `f`.
+func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	f(set.data)
+}
+
+// Equal checks whether the two sets equal.
+func (set *Set) Equal(other *Set) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	if len(set.data) != len(other.data) {
+		return false
+	}
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// IsSubsetOf checks whether the current set is a sub-set of `other`.
+func (set *Set) IsSubsetOf(other *Set) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// Union returns a new set which is the union of `set` and `others`.
+// Which means, all the items in `newSet` are in `set` or in `others`.
+func (set *Set) Union(others ...*Set) (newSet *Set) {
+	newSet = NewSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			newSet.data[k] = v
+		}
+		if set != other {
+			for k, v := range other.data {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+
+	return
+}
+
+// Diff returns a new set which is the difference set from `set` to `others`.
+// Which means, all the items in `newSet` are in `set` but not in `others`.
+func (set *Set) Diff(others ...*Set) (newSet *Set) {
+	newSet = NewSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set == other {
+			continue
+		}
+		other.mu.RLock()
+		for k, v := range set.data {
+			if _, ok := other.data[k]; !ok {
+				newSet.data[k] = v
+			}
+		}
+		other.mu.RUnlock()
+	}
+	return
+}
+
+// Intersect returns a new set which is the intersection from `set` to `others`.
+// Which means, all the items in `newSet` are in `set` and also in `others`.
+func (set *Set) Intersect(others ...*Set) (newSet *Set) {
+	newSet = NewSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			if _, ok := other.data[k]; ok {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return
+}
+
+// Complement returns a new set which is the complement from `set` to `full`.
+// Which means, all the items in `newSet` are in `full` and not in `set`.
+//
+// It returns the difference between `full` and `set`
+// if the given set `full` is not the full set of `set`.
+func (set *Set) Complement(full *Set) (newSet *Set) {
+	newSet = NewSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if set != full {
+		full.mu.RLock()
+		defer full.mu.RUnlock()
+	}
+	for k, v := range full.data {
+		if _, ok := set.data[k]; !ok {
+			newSet.data[k] = v
+		}
+	}
+	return
+}
+
+// Merge adds items from `others` sets into `set`.
+func (set *Set) Merge(others ...*Set) *Set {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range other.data {
+			set.data[k] = v
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return set
+}
+
+// Sum sums items.
+// Note: The items should be converted to int type,
+// or you'd get a result that you unexpected.
+func (set *Set) Sum() (sum int) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		sum += gconv.Int(k)
+	}
+	return
+}
+
+// Pop randomly pops an item from set.
+func (set *Set) Pop() interface{} {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for k := range set.data {
+		delete(set.data, k)
+		return k
+	}
+	return nil
+}
+
+// Pops randomly pops `size` items from set.
+// It returns all items if size == -1.
+func (set *Set) Pops(size int) []interface{} {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if size > len(set.data) || size == -1 {
+		size = len(set.data)
+	}
+	if size <= 0 {
+		return nil
+	}
+	index := 0
+	array := make([]interface{}, size)
+	for k := range set.data {
+		delete(set.data, k)
+		array[index] = k
+		index++
+		if index == size {
+			break
+		}
+	}
+	return array
+}
+
+// Walk applies a user supplied function `f` to every item of set.
+func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	m := make(map[interface{}]struct{}, len(set.data))
+	for k, v := range set.data {
+		m[f(k)] = v
+	}
+	set.data = m
+	return set
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (set Set) MarshalJSON() ([]byte, error) {
+	return json.Marshal(set.Slice())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (set *Set) UnmarshalJSON(b []byte) error {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[interface{}]struct{})
+	}
+	var array []interface{}
+	if err := json.UnmarshalUseNumber(b, &array); err != nil {
+		return err
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for set.
+func (set *Set) UnmarshalValue(value interface{}) (err error) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[interface{}]struct{})
+	}
+	var array []interface{}
+	switch value.(type) {
+	case string, []byte:
+		err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceAny(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (set *Set) DeepCopy() interface{} {
+	if set == nil {
+		return nil
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	data := make([]interface{}, 0)
+	for k := range set.data {
+		data = append(data, k)
+	}
+	return NewFrom(data, set.mu.IsSafe())
+}

+ 489 - 0
vendor/github.com/gogf/gf/v2/container/gset/gset_int_set.go

@@ -0,0 +1,489 @@
+// 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 gset
+
+import (
+	"bytes"
+
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+type IntSet struct {
+	mu   rwmutex.RWMutex
+	data map[int]struct{}
+}
+
+// NewIntSet create and returns a new set, which contains un-repeated items.
+// The parameter `safe` is used to specify whether using set in concurrent-safety,
+// which is false in default.
+func NewIntSet(safe ...bool) *IntSet {
+	return &IntSet{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[int]struct{}),
+	}
+}
+
+// NewIntSetFrom returns a new set from `items`.
+func NewIntSetFrom(items []int, safe ...bool) *IntSet {
+	m := make(map[int]struct{})
+	for _, v := range items {
+		m[v] = struct{}{}
+	}
+	return &IntSet{
+		mu:   rwmutex.Create(safe...),
+		data: m,
+	}
+}
+
+// Iterator iterates the set readonly with given callback function `f`,
+// if `f` returns true then continue iterating; or false to stop.
+func (set *IntSet) Iterator(f func(v int) bool) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		if !f(k) {
+			break
+		}
+	}
+}
+
+// Add adds one or multiple items to the set.
+func (set *IntSet) Add(item ...int) {
+	set.mu.Lock()
+	if set.data == nil {
+		set.data = make(map[int]struct{})
+	}
+	for _, v := range item {
+		set.data[v] = struct{}{}
+	}
+	set.mu.Unlock()
+}
+
+// AddIfNotExist checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set,
+// or else it does nothing and returns false.
+//
+// Note that, if `item` is nil, it does nothing and returns false.
+func (set *IntSet) AddIfNotExist(item int) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[int]struct{})
+		}
+		if _, ok := set.data[item]; !ok {
+			set.data[item] = struct{}{}
+			return true
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, the function `f` is executed without writing lock.
+func (set *IntSet) AddIfNotExistFunc(item int, f func() bool) bool {
+	if !set.Contains(item) {
+		if f() {
+			set.mu.Lock()
+			defer set.mu.Unlock()
+			if set.data == nil {
+				set.data = make(map[int]struct{})
+			}
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFuncLock checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, the function `f` is executed without writing lock.
+func (set *IntSet) AddIfNotExistFuncLock(item int, f func() bool) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[int]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains `item`.
+func (set *IntSet) Contains(item int) bool {
+	var ok bool
+	set.mu.RLock()
+	if set.data != nil {
+		_, ok = set.data[item]
+	}
+	set.mu.RUnlock()
+	return ok
+}
+
+// Remove deletes `item` from set.
+func (set *IntSet) Remove(item int) {
+	set.mu.Lock()
+	if set.data != nil {
+		delete(set.data, item)
+	}
+	set.mu.Unlock()
+}
+
+// Size returns the size of the set.
+func (set *IntSet) Size() int {
+	set.mu.RLock()
+	l := len(set.data)
+	set.mu.RUnlock()
+	return l
+}
+
+// Clear deletes all items of the set.
+func (set *IntSet) Clear() {
+	set.mu.Lock()
+	set.data = make(map[int]struct{})
+	set.mu.Unlock()
+}
+
+// Slice returns the an of items of the set as slice.
+func (set *IntSet) Slice() []int {
+	set.mu.RLock()
+	var (
+		i   = 0
+		ret = make([]int, len(set.data))
+	)
+	for k := range set.data {
+		ret[i] = k
+		i++
+	}
+	set.mu.RUnlock()
+	return ret
+}
+
+// Join joins items with a string `glue`.
+func (set *IntSet) Join(glue string) string {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if len(set.data) == 0 {
+		return ""
+	}
+	var (
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	for k := range set.data {
+		buffer.WriteString(gconv.String(k))
+		if i != l-1 {
+			buffer.WriteString(glue)
+		}
+		i++
+	}
+	return buffer.String()
+}
+
+// String returns items as a string, which implements like json.Marshal does.
+func (set *IntSet) String() string {
+	if set == nil {
+		return ""
+	}
+	return "[" + set.Join(",") + "]"
+}
+
+// LockFunc locks writing with callback function `f`.
+func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	f(set.data)
+}
+
+// RLockFunc locks reading with callback function `f`.
+func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	f(set.data)
+}
+
+// Equal checks whether the two sets equal.
+func (set *IntSet) Equal(other *IntSet) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	if len(set.data) != len(other.data) {
+		return false
+	}
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// IsSubsetOf checks whether the current set is a sub-set of `other`.
+func (set *IntSet) IsSubsetOf(other *IntSet) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// Union returns a new set which is the union of `set` and `other`.
+// Which means, all the items in `newSet` are in `set` or in `other`.
+func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
+	newSet = NewIntSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			newSet.data[k] = v
+		}
+		if set != other {
+			for k, v := range other.data {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+
+	return
+}
+
+// Diff returns a new set which is the difference set from `set` to `other`.
+// Which means, all the items in `newSet` are in `set` but not in `other`.
+func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
+	newSet = NewIntSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set == other {
+			continue
+		}
+		other.mu.RLock()
+		for k, v := range set.data {
+			if _, ok := other.data[k]; !ok {
+				newSet.data[k] = v
+			}
+		}
+		other.mu.RUnlock()
+	}
+	return
+}
+
+// Intersect returns a new set which is the intersection from `set` to `other`.
+// Which means, all the items in `newSet` are in `set` and also in `other`.
+func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
+	newSet = NewIntSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			if _, ok := other.data[k]; ok {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return
+}
+
+// Complement returns a new set which is the complement from `set` to `full`.
+// Which means, all the items in `newSet` are in `full` and not in `set`.
+//
+// It returns the difference between `full` and `set`
+// if the given set `full` is not the full set of `set`.
+func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
+	newSet = NewIntSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if set != full {
+		full.mu.RLock()
+		defer full.mu.RUnlock()
+	}
+	for k, v := range full.data {
+		if _, ok := set.data[k]; !ok {
+			newSet.data[k] = v
+		}
+	}
+	return
+}
+
+// Merge adds items from `others` sets into `set`.
+func (set *IntSet) Merge(others ...*IntSet) *IntSet {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range other.data {
+			set.data[k] = v
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return set
+}
+
+// Sum sums items.
+// Note: The items should be converted to int type,
+// or you'd get a result that you unexpected.
+func (set *IntSet) Sum() (sum int) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		sum += k
+	}
+	return
+}
+
+// Pop randomly pops an item from set.
+func (set *IntSet) Pop() int {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for k := range set.data {
+		delete(set.data, k)
+		return k
+	}
+	return 0
+}
+
+// Pops randomly pops `size` items from set.
+// It returns all items if size == -1.
+func (set *IntSet) Pops(size int) []int {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if size > len(set.data) || size == -1 {
+		size = len(set.data)
+	}
+	if size <= 0 {
+		return nil
+	}
+	index := 0
+	array := make([]int, size)
+	for k := range set.data {
+		delete(set.data, k)
+		array[index] = k
+		index++
+		if index == size {
+			break
+		}
+	}
+	return array
+}
+
+// Walk applies a user supplied function `f` to every item of set.
+func (set *IntSet) Walk(f func(item int) int) *IntSet {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	m := make(map[int]struct{}, len(set.data))
+	for k, v := range set.data {
+		m[f(k)] = v
+	}
+	set.data = m
+	return set
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (set IntSet) MarshalJSON() ([]byte, error) {
+	return json.Marshal(set.Slice())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (set *IntSet) UnmarshalJSON(b []byte) error {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[int]struct{})
+	}
+	var array []int
+	if err := json.UnmarshalUseNumber(b, &array); err != nil {
+		return err
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for set.
+func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[int]struct{})
+	}
+	var array []int
+	switch value.(type) {
+	case string, []byte:
+		err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceInt(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (set *IntSet) DeepCopy() interface{} {
+	if set == nil {
+		return nil
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		slice = make([]int, len(set.data))
+		index = 0
+	)
+	for k := range set.data {
+		slice[index] = k
+		index++
+	}
+	return NewIntSetFrom(slice, set.mu.IsSafe())
+}

+ 519 - 0
vendor/github.com/gogf/gf/v2/container/gset/gset_str_set.go

@@ -0,0 +1,519 @@
+// 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 gset
+
+import (
+	"bytes"
+	"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"
+)
+
+type StrSet struct {
+	mu   rwmutex.RWMutex
+	data map[string]struct{}
+}
+
+// NewStrSet create and returns a new set, which contains un-repeated items.
+// The parameter `safe` is used to specify whether using set in concurrent-safety,
+// which is false in default.
+func NewStrSet(safe ...bool) *StrSet {
+	return &StrSet{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[string]struct{}),
+	}
+}
+
+// NewStrSetFrom returns a new set from `items`.
+func NewStrSetFrom(items []string, safe ...bool) *StrSet {
+	m := make(map[string]struct{})
+	for _, v := range items {
+		m[v] = struct{}{}
+	}
+	return &StrSet{
+		mu:   rwmutex.Create(safe...),
+		data: m,
+	}
+}
+
+// Iterator iterates the set readonly with given callback function `f`,
+// if `f` returns true then continue iterating; or false to stop.
+func (set *StrSet) Iterator(f func(v string) bool) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		if !f(k) {
+			break
+		}
+	}
+}
+
+// Add adds one or multiple items to the set.
+func (set *StrSet) Add(item ...string) {
+	set.mu.Lock()
+	if set.data == nil {
+		set.data = make(map[string]struct{})
+	}
+	for _, v := range item {
+		set.data[v] = struct{}{}
+	}
+	set.mu.Unlock()
+}
+
+// AddIfNotExist checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exist in the set,
+// or else it does nothing and returns false.
+func (set *StrSet) AddIfNotExist(item string) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[string]struct{})
+		}
+		if _, ok := set.data[item]; !ok {
+			set.data[item] = struct{}{}
+			return true
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, the function `f` is executed without writing lock.
+func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool {
+	if !set.Contains(item) {
+		if f() {
+			set.mu.Lock()
+			defer set.mu.Unlock()
+			if set.data == nil {
+				set.data = make(map[string]struct{})
+			}
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFuncLock checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function `f` returns true, or else it does nothing and returns false.
+//
+// Note that, the function `f` is executed without writing lock.
+func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[string]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains `item`.
+func (set *StrSet) Contains(item string) bool {
+	var ok bool
+	set.mu.RLock()
+	if set.data != nil {
+		_, ok = set.data[item]
+	}
+	set.mu.RUnlock()
+	return ok
+}
+
+// ContainsI checks whether a value exists in the set with case-insensitively.
+// Note that it internally iterates the whole set to do the comparison with case-insensitively.
+func (set *StrSet) ContainsI(item string) bool {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		if strings.EqualFold(k, item) {
+			return true
+		}
+	}
+	return false
+}
+
+// Remove deletes `item` from set.
+func (set *StrSet) Remove(item string) {
+	set.mu.Lock()
+	if set.data != nil {
+		delete(set.data, item)
+	}
+	set.mu.Unlock()
+}
+
+// Size returns the size of the set.
+func (set *StrSet) Size() int {
+	set.mu.RLock()
+	l := len(set.data)
+	set.mu.RUnlock()
+	return l
+}
+
+// Clear deletes all items of the set.
+func (set *StrSet) Clear() {
+	set.mu.Lock()
+	set.data = make(map[string]struct{})
+	set.mu.Unlock()
+}
+
+// Slice returns the an of items of the set as slice.
+func (set *StrSet) Slice() []string {
+	set.mu.RLock()
+	var (
+		i   = 0
+		ret = make([]string, len(set.data))
+	)
+	for item := range set.data {
+		ret[i] = item
+		i++
+	}
+
+	set.mu.RUnlock()
+	return ret
+}
+
+// Join joins items with a string `glue`.
+func (set *StrSet) Join(glue string) string {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if len(set.data) == 0 {
+		return ""
+	}
+	var (
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	for k := range set.data {
+		buffer.WriteString(k)
+		if i != l-1 {
+			buffer.WriteString(glue)
+		}
+		i++
+	}
+	return buffer.String()
+}
+
+// String returns items as a string, which implements like json.Marshal does.
+func (set *StrSet) String() string {
+	if set == nil {
+		return ""
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	buffer.WriteByte('[')
+	for k := range set.data {
+		buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
+		if i != l-1 {
+			buffer.WriteByte(',')
+		}
+		i++
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// LockFunc locks writing with callback function `f`.
+func (set *StrSet) LockFunc(f func(m map[string]struct{})) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	f(set.data)
+}
+
+// RLockFunc locks reading with callback function `f`.
+func (set *StrSet) RLockFunc(f func(m map[string]struct{})) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	f(set.data)
+}
+
+// Equal checks whether the two sets equal.
+func (set *StrSet) Equal(other *StrSet) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	if len(set.data) != len(other.data) {
+		return false
+	}
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// IsSubsetOf checks whether the current set is a sub-set of `other`.
+func (set *StrSet) IsSubsetOf(other *StrSet) bool {
+	if set == other {
+		return true
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	other.mu.RLock()
+	defer other.mu.RUnlock()
+	for key := range set.data {
+		if _, ok := other.data[key]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// Union returns a new set which is the union of `set` and `other`.
+// Which means, all the items in `newSet` are in `set` or in `other`.
+func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
+	newSet = NewStrSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			newSet.data[k] = v
+		}
+		if set != other {
+			for k, v := range other.data {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+
+	return
+}
+
+// Diff returns a new set which is the difference set from `set` to `other`.
+// Which means, all the items in `newSet` are in `set` but not in `other`.
+func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
+	newSet = NewStrSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set == other {
+			continue
+		}
+		other.mu.RLock()
+		for k, v := range set.data {
+			if _, ok := other.data[k]; !ok {
+				newSet.data[k] = v
+			}
+		}
+		other.mu.RUnlock()
+	}
+	return
+}
+
+// Intersect returns a new set which is the intersection from `set` to `other`.
+// Which means, all the items in `newSet` are in `set` and also in `other`.
+func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
+	newSet = NewStrSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range set.data {
+			if _, ok := other.data[k]; ok {
+				newSet.data[k] = v
+			}
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return
+}
+
+// Complement returns a new set which is the complement from `set` to `full`.
+// Which means, all the items in `newSet` are in `full` and not in `set`.
+//
+// It returns the difference between `full` and `set`
+// if the given set `full` is not the full set of `set`.
+func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) {
+	newSet = NewStrSet()
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	if set != full {
+		full.mu.RLock()
+		defer full.mu.RUnlock()
+	}
+	for k, v := range full.data {
+		if _, ok := set.data[k]; !ok {
+			newSet.data[k] = v
+		}
+	}
+	return
+}
+
+// Merge adds items from `others` sets into `set`.
+func (set *StrSet) Merge(others ...*StrSet) *StrSet {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for _, other := range others {
+		if set != other {
+			other.mu.RLock()
+		}
+		for k, v := range other.data {
+			set.data[k] = v
+		}
+		if set != other {
+			other.mu.RUnlock()
+		}
+	}
+	return set
+}
+
+// Sum sums items.
+// Note: The items should be converted to int type,
+// or you'd get a result that you unexpected.
+func (set *StrSet) Sum() (sum int) {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	for k := range set.data {
+		sum += gconv.Int(k)
+	}
+	return
+}
+
+// Pop randomly pops an item from set.
+func (set *StrSet) Pop() string {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	for k := range set.data {
+		delete(set.data, k)
+		return k
+	}
+	return ""
+}
+
+// Pops randomly pops `size` items from set.
+// It returns all items if size == -1.
+func (set *StrSet) Pops(size int) []string {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if size > len(set.data) || size == -1 {
+		size = len(set.data)
+	}
+	if size <= 0 {
+		return nil
+	}
+	index := 0
+	array := make([]string, size)
+	for k := range set.data {
+		delete(set.data, k)
+		array[index] = k
+		index++
+		if index == size {
+			break
+		}
+	}
+	return array
+}
+
+// Walk applies a user supplied function `f` to every item of set.
+func (set *StrSet) Walk(f func(item string) string) *StrSet {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	m := make(map[string]struct{}, len(set.data))
+	for k, v := range set.data {
+		m[f(k)] = v
+	}
+	set.data = m
+	return set
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (set StrSet) MarshalJSON() ([]byte, error) {
+	return json.Marshal(set.Slice())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (set *StrSet) UnmarshalJSON(b []byte) error {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[string]struct{})
+	}
+	var array []string
+	if err := json.UnmarshalUseNumber(b, &array); err != nil {
+		return err
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for set.
+func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
+	set.mu.Lock()
+	defer set.mu.Unlock()
+	if set.data == nil {
+		set.data = make(map[string]struct{})
+	}
+	var array []string
+	switch value.(type) {
+	case string, []byte:
+		err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceStr(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (set *StrSet) DeepCopy() interface{} {
+	if set == nil {
+		return nil
+	}
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		slice = make([]string, len(set.data))
+		index = 0
+	)
+	for k := range set.data {
+		slice[index] = k
+		index++
+	}
+	return NewStrSetFrom(slice, set.mu.IsSafe())
+}

+ 10 - 0
vendor/github.com/gogf/gf/v2/container/gtree/gtree.go

@@ -0,0 +1,10 @@
+// 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 gtree provides concurrent-safe/unsafe tree containers.
+//
+// Some implements are from: https://github.com/emirpasic/gods
+package gtree

+ 816 - 0
vendor/github.com/gogf/gf/v2/container/gtree/gtree_avltree.go

@@ -0,0 +1,816 @@
+// 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 gtree
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// AVLTree holds elements of the AVL tree.
+type AVLTree struct {
+	mu         rwmutex.RWMutex
+	root       *AVLTreeNode
+	comparator func(v1, v2 interface{}) int
+	size       int
+}
+
+// AVLTreeNode is a single element within the tree.
+type AVLTreeNode struct {
+	Key      interface{}
+	Value    interface{}
+	parent   *AVLTreeNode
+	children [2]*AVLTreeNode
+	b        int8
+}
+
+// NewAVLTree instantiates an AVL tree with the custom key comparator.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+func NewAVLTree(comparator func(v1, v2 interface{}) int, safe ...bool) *AVLTree {
+	return &AVLTree{
+		mu:         rwmutex.Create(safe...),
+		comparator: comparator,
+	}
+}
+
+// NewAVLTreeFrom instantiates an AVL tree with the custom key comparator and data map.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *AVLTree {
+	tree := NewAVLTree(comparator, safe...)
+	for k, v := range data {
+		tree.put(k, v, nil, &tree.root)
+	}
+	return tree
+}
+
+// Clone returns a new tree with a copy of current tree.
+func (tree *AVLTree) Clone() *AVLTree {
+	newTree := NewAVLTree(tree.comparator, tree.mu.IsSafe())
+	newTree.Sets(tree.Map())
+	return newTree
+}
+
+// Set inserts node into the tree.
+func (tree *AVLTree) Set(key interface{}, value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.put(key, value, nil, &tree.root)
+}
+
+// Sets batch sets key-values to the tree.
+func (tree *AVLTree) Sets(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for key, value := range data {
+		tree.put(key, value, nil, &tree.root)
+	}
+}
+
+// Search searches the tree with given `key`.
+// Second return parameter `found` is true if key was found, otherwise false.
+func (tree *AVLTree) Search(key interface{}) (value interface{}, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	if node, found := tree.doSearch(key); found {
+		return node.Value, true
+	}
+	return nil, false
+}
+
+// doSearch searches the tree with given `key`.
+// Second return parameter `found` is true if key was found, otherwise false.
+func (tree *AVLTree) doSearch(key interface{}) (node *AVLTreeNode, found bool) {
+	node = tree.root
+	for node != nil {
+		cmp := tree.getComparator()(key, node.Key)
+		switch {
+		case cmp == 0:
+			return node, true
+		case cmp < 0:
+			node = node.children[0]
+		case cmp > 0:
+			node = node.children[1]
+		}
+	}
+	return nil, false
+}
+
+// Get searches the node in the tree by `key` and returns its value or nil if key is not found in tree.
+func (tree *AVLTree) Get(key interface{}) (value interface{}) {
+	value, _ = tree.Search(key)
+	return
+}
+
+// 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 (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	if node, found := tree.doSearch(key); found {
+		return node.Value
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		tree.put(key, value, nil, &tree.root)
+	}
+	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 (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *AVLTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a gvar.Var with the value by given `key`.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *AVLTree) GetVar(key interface{}) *gvar.Var {
+	return gvar.New(tree.Get(key))
+}
+
+// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *AVLTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *AVLTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *AVLTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.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 (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *AVLTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Contains checks whether `key` exists in the tree.
+func (tree *AVLTree) Contains(key interface{}) bool {
+	_, ok := tree.Search(key)
+	return ok
+}
+
+// Remove removes the node from the tree by key.
+// Key should adhere to the comparator's type assertion, otherwise method panics.
+func (tree *AVLTree) Remove(key interface{}) (value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	value, _ = tree.remove(key, &tree.root)
+	return
+}
+
+// Removes batch deletes values of the tree by `keys`.
+func (tree *AVLTree) Removes(keys []interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for _, key := range keys {
+		tree.remove(key, &tree.root)
+	}
+}
+
+// IsEmpty returns true if tree does not contain any nodes.
+func (tree *AVLTree) IsEmpty() bool {
+	return tree.Size() == 0
+}
+
+// Size returns number of nodes in the tree.
+func (tree *AVLTree) Size() int {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	return tree.size
+}
+
+// Keys returns all keys in asc order.
+func (tree *AVLTree) Keys() []interface{} {
+	keys := make([]interface{}, tree.Size())
+	index := 0
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		keys[index] = key
+		index++
+		return true
+	})
+	return keys
+}
+
+// Values returns all values in asc order based on the key.
+func (tree *AVLTree) Values() []interface{} {
+	values := make([]interface{}, tree.Size())
+	index := 0
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		values[index] = value
+		index++
+		return true
+	})
+	return values
+}
+
+// Left returns the minimum element of the AVL tree
+// or nil if the tree is empty.
+func (tree *AVLTree) Left() *AVLTreeNode {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.bottom(0)
+	if tree.mu.IsSafe() {
+		return &AVLTreeNode{
+			Key:   node.Key,
+			Value: node.Value,
+		}
+	}
+	return node
+}
+
+// Right returns the maximum element of the AVL tree
+// or nil if the tree is empty.
+func (tree *AVLTree) Right() *AVLTreeNode {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.bottom(1)
+	if tree.mu.IsSafe() {
+		return &AVLTreeNode{
+			Key:   node.Key,
+			Value: node.Value,
+		}
+	}
+	return node
+}
+
+// Floor Finds floor node of the input key, return the floor node or nil if no floor node is found.
+// Second return parameter is true if floor was found, otherwise false.
+//
+// Floor node is defined as the largest node that is smaller than or equal to the given node.
+// A floor node may not be found, either because the tree is empty, or because
+// all nodes in the tree is larger than the given node.
+//
+// Key should adhere to the comparator's type assertion, otherwise method panics.
+func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	n := tree.root
+	for n != nil {
+		c := tree.getComparator()(key, n.Key)
+		switch {
+		case c == 0:
+			return n, true
+		case c < 0:
+			n = n.children[0]
+		case c > 0:
+			floor, found = n, true
+			n = n.children[1]
+		}
+	}
+	if found {
+		return
+	}
+	return nil, false
+}
+
+// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling node is found.
+// Second return parameter is true if ceiling was found, otherwise false.
+//
+// Ceiling node is defined as the smallest node that is larger than or equal to the given node.
+// A ceiling node may not be found, either because the tree is empty, or because
+// all nodes in the tree is smaller than the given node.
+//
+// Key should adhere to the comparator's type assertion, otherwise method panics.
+func (tree *AVLTree) Ceiling(key interface{}) (ceiling *AVLTreeNode, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	n := tree.root
+	for n != nil {
+		c := tree.getComparator()(key, n.Key)
+		switch {
+		case c == 0:
+			return n, true
+		case c > 0:
+			n = n.children[1]
+		case c < 0:
+			ceiling, found = n, true
+			n = n.children[0]
+		}
+	}
+	if found {
+		return
+	}
+	return nil, false
+}
+
+// Clear removes all nodes from the tree.
+func (tree *AVLTree) Clear() {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+}
+
+// Replace the data of the tree with given `data`.
+func (tree *AVLTree) Replace(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+	for key, value := range data {
+		tree.put(key, value, nil, &tree.root)
+	}
+}
+
+// String returns a string representation of container
+func (tree *AVLTree) String() string {
+	if tree == nil {
+		return ""
+	}
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	str := ""
+	if tree.size != 0 {
+		output(tree.root, "", true, &str)
+	}
+	return str
+}
+
+// Print prints the tree to stdout.
+func (tree *AVLTree) Print() {
+	fmt.Println(tree.String())
+}
+
+// Map returns all key-value items as map.
+func (tree *AVLTree) Map() map[interface{}]interface{} {
+	m := make(map[interface{}]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[key] = value
+		return true
+	})
+	return m
+}
+
+// MapStrAny returns all key-value items as map[string]interface{}.
+func (tree *AVLTree) MapStrAny() map[string]interface{} {
+	m := make(map[string]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[gconv.String(key)] = value
+		return true
+	})
+	return m
+}
+
+// Flip exchanges key-value of the tree to value-key.
+// Note that you should guarantee the value is the same type as key,
+// or else the comparator would panic.
+//
+// If the type of value is different with key, you pass the new `comparator`.
+func (tree *AVLTree) Flip(comparator ...func(v1, v2 interface{}) int) {
+	t := (*AVLTree)(nil)
+	if len(comparator) > 0 {
+		t = NewAVLTree(comparator[0], tree.mu.IsSafe())
+	} else {
+		t = NewAVLTree(tree.comparator, tree.mu.IsSafe())
+	}
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		t.put(value, key, nil, &t.root)
+		return true
+	})
+	tree.mu.Lock()
+	tree.root = t.root
+	tree.size = t.size
+	tree.mu.Unlock()
+}
+
+// Iterator is alias of IteratorAsc.
+func (tree *AVLTree) Iterator(f func(key, value interface{}) bool) {
+	tree.IteratorAsc(f)
+}
+
+// IteratorFrom is alias of IteratorAscFrom.
+func (tree *AVLTree) IteratorFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.IteratorAscFrom(key, match, f)
+}
+
+// IteratorAsc iterates the tree readonly in ascending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *AVLTree) IteratorAsc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	tree.doIteratorAsc(tree.bottom(0), f)
+}
+
+// IteratorAscFrom iterates the tree readonly in ascending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *AVLTree) IteratorAscFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, found := tree.doSearch(key)
+	if match {
+		if found {
+			tree.doIteratorAsc(node, f)
+		}
+	} else {
+		tree.doIteratorAsc(node, f)
+	}
+}
+
+func (tree *AVLTree) doIteratorAsc(node *AVLTreeNode, f func(key, value interface{}) bool) {
+	for node != nil {
+		if !f(node.Key, node.Value) {
+			return
+		}
+		node = node.Next()
+	}
+}
+
+// IteratorDesc iterates the tree readonly in descending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *AVLTree) IteratorDesc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	tree.doIteratorDesc(tree.bottom(1), f)
+}
+
+// IteratorDescFrom iterates the tree readonly in descending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *AVLTree) IteratorDescFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, found := tree.doSearch(key)
+	if match {
+		if found {
+			tree.doIteratorDesc(node, f)
+		}
+	} else {
+		tree.doIteratorDesc(node, f)
+	}
+}
+
+func (tree *AVLTree) doIteratorDesc(node *AVLTreeNode, f func(key, value interface{}) bool) {
+	for node != nil {
+		if !f(node.Key, node.Value) {
+			return
+		}
+		node = node.Prev()
+	}
+}
+
+func (tree *AVLTree) put(key interface{}, value interface{}, p *AVLTreeNode, qp **AVLTreeNode) bool {
+	q := *qp
+	if q == nil {
+		tree.size++
+		*qp = &AVLTreeNode{Key: key, Value: value, parent: p}
+		return true
+	}
+
+	c := tree.getComparator()(key, q.Key)
+	if c == 0 {
+		q.Key = key
+		q.Value = value
+		return false
+	}
+
+	if c < 0 {
+		c = -1
+	} else {
+		c = 1
+	}
+	a := (c + 1) / 2
+	if tree.put(key, value, q, &q.children[a]) {
+		return putFix(int8(c), qp)
+	}
+	return false
+}
+
+func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{}, fix bool) {
+	q := *qp
+	if q == nil {
+		return nil, false
+	}
+
+	c := tree.getComparator()(key, q.Key)
+	if c == 0 {
+		tree.size--
+		value = q.Value
+		fix = true
+		if q.children[1] == nil {
+			if q.children[0] != nil {
+				q.children[0].parent = q.parent
+			}
+			*qp = q.children[0]
+			return
+		}
+		if removeMin(&q.children[1], &q.Key, &q.Value) {
+			return value, removeFix(-1, qp)
+		}
+		return
+	}
+
+	if c < 0 {
+		c = -1
+	} else {
+		c = 1
+	}
+	a := (c + 1) / 2
+	value, fix = tree.remove(key, &q.children[a])
+	if fix {
+		return value, removeFix(int8(-c), qp)
+	}
+	return value, false
+}
+
+func removeMin(qp **AVLTreeNode, minKey *interface{}, minVal *interface{}) bool {
+	q := *qp
+	if q.children[0] == nil {
+		*minKey = q.Key
+		*minVal = q.Value
+		if q.children[1] != nil {
+			q.children[1].parent = q.parent
+		}
+		*qp = q.children[1]
+		return true
+	}
+	fix := removeMin(&q.children[0], minKey, minVal)
+	if fix {
+		return removeFix(1, qp)
+	}
+	return false
+}
+
+func putFix(c int8, t **AVLTreeNode) bool {
+	s := *t
+	if s.b == 0 {
+		s.b = c
+		return true
+	}
+
+	if s.b == -c {
+		s.b = 0
+		return false
+	}
+
+	if s.children[(c+1)/2].b == c {
+		s = singleRotate(c, s)
+	} else {
+		s = doubleRotate(c, s)
+	}
+	*t = s
+	return false
+}
+
+func removeFix(c int8, t **AVLTreeNode) bool {
+	s := *t
+	if s.b == 0 {
+		s.b = c
+		return false
+	}
+
+	if s.b == -c {
+		s.b = 0
+		return true
+	}
+
+	a := (c + 1) / 2
+	if s.children[a].b == 0 {
+		s = rotate(c, s)
+		s.b = -c
+		*t = s
+		return false
+	}
+
+	if s.children[a].b == c {
+		s = singleRotate(c, s)
+	} else {
+		s = doubleRotate(c, s)
+	}
+	*t = s
+	return true
+}
+
+func singleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
+	s.b = 0
+	s = rotate(c, s)
+	s.b = 0
+	return s
+}
+
+func doubleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
+	a := (c + 1) / 2
+	r := s.children[a]
+	s.children[a] = rotate(-c, s.children[a])
+	p := rotate(c, s)
+
+	switch {
+	default:
+		s.b = 0
+		r.b = 0
+	case p.b == c:
+		s.b = -c
+		r.b = 0
+	case p.b == -c:
+		s.b = 0
+		r.b = c
+	}
+
+	p.b = 0
+	return p
+}
+
+func rotate(c int8, s *AVLTreeNode) *AVLTreeNode {
+	a := (c + 1) / 2
+	r := s.children[a]
+	s.children[a] = r.children[a^1]
+	if s.children[a] != nil {
+		s.children[a].parent = s
+	}
+	r.children[a^1] = s
+	r.parent = s.parent
+	s.parent = r
+	return r
+}
+
+func (tree *AVLTree) bottom(d int) *AVLTreeNode {
+	n := tree.root
+	if n == nil {
+		return nil
+	}
+
+	for c := n.children[d]; c != nil; c = n.children[d] {
+		n = c
+	}
+	return n
+}
+
+// Prev returns the previous element in an inorder
+// walk of the AVL tree.
+func (node *AVLTreeNode) Prev() *AVLTreeNode {
+	return node.walk1(0)
+}
+
+// Next returns the next element in an inorder
+// walk of the AVL tree.
+func (node *AVLTreeNode) Next() *AVLTreeNode {
+	return node.walk1(1)
+}
+
+func (node *AVLTreeNode) walk1(a int) *AVLTreeNode {
+	if node == nil {
+		return nil
+	}
+	n := node
+	if n.children[a] != nil {
+		n = n.children[a]
+		for n.children[a^1] != nil {
+			n = n.children[a^1]
+		}
+		return n
+	}
+
+	p := n.parent
+	for p != nil && p.children[a] == n {
+		n = p
+		p = p.parent
+	}
+	return p
+}
+
+func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
+	if node.children[1] != nil {
+		newPrefix := prefix
+		if isTail {
+			newPrefix += "│   "
+		} else {
+			newPrefix += "    "
+		}
+		output(node.children[1], newPrefix, false, str)
+	}
+	*str += prefix
+	if isTail {
+		*str += "└── "
+	} else {
+		*str += "┌── "
+	}
+	*str += fmt.Sprintf("%v\n", node.Key)
+	if node.children[0] != nil {
+		newPrefix := prefix
+		if isTail {
+			newPrefix += "    "
+		} else {
+			newPrefix += "│   "
+		}
+		output(node.children[0], newPrefix, true, str)
+	}
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (tree AVLTree) MarshalJSON() (jsonBytes []byte, err error) {
+	if tree.root == nil {
+		return []byte("null"), nil
+	}
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('{')
+	tree.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
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it panics.
+func (tree *AVLTree) getComparator() func(a, b interface{}) int {
+	if tree.comparator == nil {
+		panic("comparator is missing for tree")
+	}
+	return tree.comparator
+}

+ 979 - 0
vendor/github.com/gogf/gf/v2/container/gtree/gtree_btree.go

@@ -0,0 +1,979 @@
+// 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 gtree
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/internal/intlog"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/internal/rwmutex"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// BTree holds elements of the B-tree.
+type BTree struct {
+	mu         rwmutex.RWMutex
+	root       *BTreeNode
+	comparator func(v1, v2 interface{}) int
+	size       int // Total number of keys in the tree
+	m          int // order (maximum number of children)
+}
+
+// BTreeNode is a single element within the tree.
+type BTreeNode struct {
+	Parent   *BTreeNode
+	Entries  []*BTreeEntry // Contained keys in node
+	Children []*BTreeNode  // Children nodes
+}
+
+// BTreeEntry represents the key-value pair contained within nodes.
+type BTreeEntry struct {
+	Key   interface{}
+	Value interface{}
+}
+
+// NewBTree instantiates a B-tree with `m` (maximum number of children) and a custom key comparator.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+// Note that the `m` must be greater or equal than 3, or else it panics.
+func NewBTree(m int, comparator func(v1, v2 interface{}) int, safe ...bool) *BTree {
+	if m < 3 {
+		panic("Invalid order, should be at least 3")
+	}
+	return &BTree{
+		comparator: comparator,
+		mu:         rwmutex.Create(safe...),
+		m:          m,
+	}
+}
+
+// NewBTreeFrom instantiates a B-tree with `m` (maximum number of children), a custom key comparator and data map.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *BTree {
+	tree := NewBTree(m, comparator, safe...)
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+	return tree
+}
+
+// Clone returns a new tree with a copy of current tree.
+func (tree *BTree) Clone() *BTree {
+	newTree := NewBTree(tree.m, tree.comparator, tree.mu.IsSafe())
+	newTree.Sets(tree.Map())
+	return newTree
+}
+
+// Set inserts key-value item into the tree.
+func (tree *BTree) Set(key interface{}, value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.doSet(key, value)
+}
+
+// doSet inserts key-value pair node into the tree.
+// If key already exists, then its value is updated with the new value.
+func (tree *BTree) doSet(key interface{}, value interface{}) {
+	entry := &BTreeEntry{Key: key, Value: value}
+	if tree.root == nil {
+		tree.root = &BTreeNode{Entries: []*BTreeEntry{entry}, Children: []*BTreeNode{}}
+		tree.size++
+		return
+	}
+
+	if tree.insert(tree.root, entry) {
+		tree.size++
+	}
+}
+
+// Sets batch sets key-values to the tree.
+func (tree *BTree) Sets(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+}
+
+// Get searches the node in the tree by `key` and returns its value or nil if key is not found in tree.
+func (tree *BTree) Get(key interface{}) (value interface{}) {
+	value, _ = tree.Search(key)
+	return
+}
+
+// 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 (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	if entry := tree.doSearch(key); entry != nil {
+		return entry.Value
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		tree.doSet(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 (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *BTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a gvar.Var with the value by given `key`.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *BTree) GetVar(key interface{}) *gvar.Var {
+	return gvar.New(tree.Get(key))
+}
+
+// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *BTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *BTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *BTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.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 (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *BTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Contains checks whether `key` exists in the tree.
+func (tree *BTree) Contains(key interface{}) bool {
+	_, ok := tree.Search(key)
+	return ok
+}
+
+// doRemove removes the node from the tree by key.
+// Key should adhere to the comparator's type assertion, otherwise method panics.
+func (tree *BTree) doRemove(key interface{}) (value interface{}) {
+	node, index, found := tree.searchRecursively(tree.root, key)
+	if found {
+		value = node.Entries[index].Value
+		tree.delete(node, index)
+		tree.size--
+	}
+	return
+}
+
+// Remove removes the node from the tree by `key`.
+func (tree *BTree) Remove(key interface{}) (value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	return tree.doRemove(key)
+}
+
+// Removes batch deletes values of the tree by `keys`.
+func (tree *BTree) Removes(keys []interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for _, key := range keys {
+		tree.doRemove(key)
+	}
+}
+
+// IsEmpty returns true if tree does not contain any nodes
+func (tree *BTree) IsEmpty() bool {
+	return tree.Size() == 0
+}
+
+// Size returns number of nodes in the tree.
+func (tree *BTree) Size() int {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	return tree.size
+}
+
+// Keys returns all keys in asc order.
+func (tree *BTree) Keys() []interface{} {
+	keys := make([]interface{}, tree.Size())
+	index := 0
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		keys[index] = key
+		index++
+		return true
+	})
+	return keys
+}
+
+// Values returns all values in asc order based on the key.
+func (tree *BTree) Values() []interface{} {
+	values := make([]interface{}, tree.Size())
+	index := 0
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		values[index] = value
+		index++
+		return true
+	})
+	return values
+}
+
+// Map returns all key-value items as map.
+func (tree *BTree) Map() map[interface{}]interface{} {
+	m := make(map[interface{}]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[key] = value
+		return true
+	})
+	return m
+}
+
+// MapStrAny returns all key-value items as map[string]interface{}.
+func (tree *BTree) MapStrAny() map[string]interface{} {
+	m := make(map[string]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[gconv.String(key)] = value
+		return true
+	})
+	return m
+}
+
+// Clear removes all nodes from the tree.
+func (tree *BTree) Clear() {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+}
+
+// Replace the data of the tree with given `data`.
+func (tree *BTree) Replace(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+}
+
+// Height returns the height of the tree.
+func (tree *BTree) Height() int {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	return tree.root.height()
+}
+
+// Left returns the left-most (min) entry or nil if tree is empty.
+func (tree *BTree) Left() *BTreeEntry {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.left(tree.root)
+	if node != nil {
+		return node.Entries[0]
+	}
+	return nil
+}
+
+// Right returns the right-most (max) entry or nil if tree is empty.
+func (tree *BTree) Right() *BTreeEntry {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.right(tree.root)
+	if node != nil {
+		return node.Entries[len(node.Entries)-1]
+	}
+	return nil
+}
+
+// String returns a string representation of container (for debugging purposes)
+func (tree *BTree) String() string {
+	if tree == nil {
+		return ""
+	}
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	var buffer bytes.Buffer
+	if tree.size != 0 {
+		tree.output(&buffer, tree.root, 0, true)
+	}
+	return buffer.String()
+}
+
+// Search searches the tree with given `key`.
+// Second return parameter `found` is true if key was found, otherwise false.
+func (tree *BTree) Search(key interface{}) (value interface{}, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, index, found := tree.searchRecursively(tree.root, key)
+	if found {
+		return node.Entries[index].Value, true
+	}
+	return nil, false
+}
+
+// Search searches the tree with given `key` without mutex.
+// It returns the entry if found or otherwise nil.
+func (tree *BTree) doSearch(key interface{}) *BTreeEntry {
+	node, index, found := tree.searchRecursively(tree.root, key)
+	if found {
+		return node.Entries[index]
+	}
+	return nil
+}
+
+// Print prints the tree to stdout.
+func (tree *BTree) Print() {
+	fmt.Println(tree.String())
+}
+
+// Iterator is alias of IteratorAsc.
+func (tree *BTree) Iterator(f func(key, value interface{}) bool) {
+	tree.IteratorAsc(f)
+}
+
+// IteratorFrom is alias of IteratorAscFrom.
+func (tree *BTree) IteratorFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.IteratorAscFrom(key, match, f)
+}
+
+// IteratorAsc iterates the tree readonly in ascending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *BTree) IteratorAsc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.left(tree.root)
+	if node == nil {
+		return
+	}
+	tree.doIteratorAsc(node, node.Entries[0], 0, f)
+}
+
+// IteratorAscFrom iterates the tree readonly in ascending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *BTree) IteratorAscFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, index, found := tree.searchRecursively(tree.root, key)
+	if match {
+		if found {
+			tree.doIteratorAsc(node, node.Entries[index], index, f)
+		}
+	} else {
+		if index >= 0 && index < len(node.Entries) {
+			tree.doIteratorAsc(node, node.Entries[index], index, f)
+		}
+	}
+}
+
+func (tree *BTree) doIteratorAsc(node *BTreeNode, entry *BTreeEntry, index int, f func(key, value interface{}) bool) {
+	first := true
+loop:
+	if entry == nil {
+		return
+	}
+	if !f(entry.Key, entry.Value) {
+		return
+	}
+	// Find current entry position in current node
+	if !first {
+		index, _ = tree.search(node, entry.Key)
+	} else {
+		first = false
+	}
+	// Try to go down to the child right of the current entry
+	if index+1 < len(node.Children) {
+		node = node.Children[index+1]
+		// Try to go down to the child left of the current node
+		for len(node.Children) > 0 {
+			node = node.Children[0]
+		}
+		// Return the left-most entry
+		entry = node.Entries[0]
+		goto loop
+	}
+	// Above assures that we have reached a leaf node, so return the next entry in current node (if any)
+	if index+1 < len(node.Entries) {
+		entry = node.Entries[index+1]
+		goto loop
+	}
+	// Reached leaf node and there are no entries to the right of the current entry, so go up to the parent
+	for node.Parent != nil {
+		node = node.Parent
+		// Find next entry position in current node (note: search returns the first equal or bigger than entry)
+		index, _ = tree.search(node, entry.Key)
+		// Check that there is a next entry position in current node
+		if index < len(node.Entries) {
+			entry = node.Entries[index]
+			goto loop
+		}
+	}
+}
+
+// IteratorDesc iterates the tree readonly in descending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *BTree) IteratorDesc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.right(tree.root)
+	if node == nil {
+		return
+	}
+	index := len(node.Entries) - 1
+	entry := node.Entries[index]
+	tree.doIteratorDesc(node, entry, index, f)
+}
+
+// IteratorDescFrom iterates the tree readonly in descending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *BTree) IteratorDescFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, index, found := tree.searchRecursively(tree.root, key)
+	if match {
+		if found {
+			tree.doIteratorDesc(node, node.Entries[index], index, f)
+		}
+	} else {
+		if index >= 0 && index < len(node.Entries) {
+			tree.doIteratorDesc(node, node.Entries[index], index, f)
+		}
+	}
+}
+
+// IteratorDesc iterates the tree readonly in descending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *BTree) doIteratorDesc(node *BTreeNode, entry *BTreeEntry, index int, f func(key, value interface{}) bool) {
+	first := true
+loop:
+	if entry == nil {
+		return
+	}
+	if !f(entry.Key, entry.Value) {
+		return
+	}
+	// Find current entry position in current node
+	if !first {
+		index, _ = tree.search(node, entry.Key)
+	} else {
+		first = false
+	}
+	// Try to go down to the child left of the current entry
+	if index < len(node.Children) {
+		node = node.Children[index]
+		// Try to go down to the child right of the current node
+		for len(node.Children) > 0 {
+			node = node.Children[len(node.Children)-1]
+		}
+		// Return the right-most entry
+		entry = node.Entries[len(node.Entries)-1]
+		goto loop
+	}
+	// Above assures that we have reached a leaf node, so return the previous entry in current node (if any)
+	if index-1 >= 0 {
+		entry = node.Entries[index-1]
+		goto loop
+	}
+
+	// Reached leaf node and there are no entries to the left of the current entry, so go up to the parent
+	for node.Parent != nil {
+		node = node.Parent
+		// Find previous entry position in current node (note: search returns the first equal or bigger than entry)
+		index, _ = tree.search(node, entry.Key)
+		// Check that there is a previous entry position in current node
+		if index-1 >= 0 {
+			entry = node.Entries[index-1]
+			goto loop
+		}
+	}
+}
+
+func (tree *BTree) output(buffer *bytes.Buffer, node *BTreeNode, level int, isTail bool) {
+	for e := 0; e < len(node.Entries)+1; e++ {
+		if e < len(node.Children) {
+			tree.output(buffer, node.Children[e], level+1, true)
+		}
+		if e < len(node.Entries) {
+			if _, err := buffer.WriteString(strings.Repeat("    ", level)); err != nil {
+				intlog.Errorf(context.TODO(), `%+v`, err)
+			}
+			if _, err := buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n"); err != nil {
+				intlog.Errorf(context.TODO(), `%+v`, err)
+			}
+		}
+	}
+}
+
+func (node *BTreeNode) height() int {
+	h := 0
+	n := node
+	for ; n != nil; n = n.Children[0] {
+		h++
+		if len(n.Children) == 0 {
+			break
+		}
+	}
+	return h
+}
+
+func (tree *BTree) isLeaf(node *BTreeNode) bool {
+	return len(node.Children) == 0
+}
+
+// func (tree *BTree) isFull(node *BTreeNode) bool {
+//	return len(node.Entries) == tree.maxEntries()
+// }
+
+func (tree *BTree) shouldSplit(node *BTreeNode) bool {
+	return len(node.Entries) > tree.maxEntries()
+}
+
+func (tree *BTree) maxChildren() int {
+	return tree.m
+}
+
+func (tree *BTree) minChildren() int {
+	return (tree.m + 1) / 2 // ceil(m/2)
+}
+
+func (tree *BTree) maxEntries() int {
+	return tree.maxChildren() - 1
+}
+
+func (tree *BTree) minEntries() int {
+	return tree.minChildren() - 1
+}
+
+func (tree *BTree) middle() int {
+	// "-1" to favor right nodes to have more keys when splitting
+	return (tree.m - 1) / 2
+}
+
+// search does search only within the single node among its entries
+func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bool) {
+	low, mid, high := 0, 0, len(node.Entries)-1
+	for low <= high {
+		mid = low + (high-low)/2
+		compare := tree.getComparator()(key, node.Entries[mid].Key)
+		switch {
+		case compare > 0:
+			low = mid + 1
+		case compare < 0:
+			high = mid - 1
+		case compare == 0:
+			return mid, true
+		}
+	}
+	return low, false
+}
+
+// searchRecursively searches recursively down the tree starting at the startNode
+func (tree *BTree) searchRecursively(startNode *BTreeNode, key interface{}) (node *BTreeNode, index int, found bool) {
+	if tree.size == 0 {
+		return nil, -1, false
+	}
+	node = startNode
+	for {
+		index, found = tree.search(node, key)
+		if found {
+			return node, index, true
+		}
+		if tree.isLeaf(node) {
+			return node, index, false
+		}
+		node = node.Children[index]
+	}
+}
+
+func (tree *BTree) insert(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
+	if tree.isLeaf(node) {
+		return tree.insertIntoLeaf(node, entry)
+	}
+	return tree.insertIntoInternal(node, entry)
+}
+
+func (tree *BTree) insertIntoLeaf(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
+	insertPosition, found := tree.search(node, entry.Key)
+	if found {
+		node.Entries[insertPosition] = entry
+		return false
+	}
+	// Insert entry's key in the middle of the node
+	node.Entries = append(node.Entries, nil)
+	copy(node.Entries[insertPosition+1:], node.Entries[insertPosition:])
+	node.Entries[insertPosition] = entry
+	tree.split(node)
+	return true
+}
+
+func (tree *BTree) insertIntoInternal(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
+	insertPosition, found := tree.search(node, entry.Key)
+	if found {
+		node.Entries[insertPosition] = entry
+		return false
+	}
+	return tree.insert(node.Children[insertPosition], entry)
+}
+
+func (tree *BTree) split(node *BTreeNode) {
+	if !tree.shouldSplit(node) {
+		return
+	}
+
+	if node == tree.root {
+		tree.splitRoot()
+		return
+	}
+
+	tree.splitNonRoot(node)
+}
+
+func (tree *BTree) splitNonRoot(node *BTreeNode) {
+	middle := tree.middle()
+	parent := node.Parent
+
+	left := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[:middle]...), Parent: parent}
+	right := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[middle+1:]...), Parent: parent}
+
+	// Move children from the node to be split into left and right nodes
+	if !tree.isLeaf(node) {
+		left.Children = append([]*BTreeNode(nil), node.Children[:middle+1]...)
+		right.Children = append([]*BTreeNode(nil), node.Children[middle+1:]...)
+		setParent(left.Children, left)
+		setParent(right.Children, right)
+	}
+
+	insertPosition, _ := tree.search(parent, node.Entries[middle].Key)
+
+	// Insert middle key into parent
+	parent.Entries = append(parent.Entries, nil)
+	copy(parent.Entries[insertPosition+1:], parent.Entries[insertPosition:])
+	parent.Entries[insertPosition] = node.Entries[middle]
+
+	// Set child left of inserted key in parent to the created left node
+	parent.Children[insertPosition] = left
+
+	// Set child right of inserted key in parent to the created right node
+	parent.Children = append(parent.Children, nil)
+	copy(parent.Children[insertPosition+2:], parent.Children[insertPosition+1:])
+	parent.Children[insertPosition+1] = right
+
+	tree.split(parent)
+}
+
+func (tree *BTree) splitRoot() {
+	middle := tree.middle()
+	left := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[:middle]...)}
+	right := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[middle+1:]...)}
+
+	// Move children from the node to be split into left and right nodes
+	if !tree.isLeaf(tree.root) {
+		left.Children = append([]*BTreeNode(nil), tree.root.Children[:middle+1]...)
+		right.Children = append([]*BTreeNode(nil), tree.root.Children[middle+1:]...)
+		setParent(left.Children, left)
+		setParent(right.Children, right)
+	}
+
+	// Root is a node with one entry and two children (left and right)
+	newRoot := &BTreeNode{
+		Entries:  []*BTreeEntry{tree.root.Entries[middle]},
+		Children: []*BTreeNode{left, right},
+	}
+
+	left.Parent = newRoot
+	right.Parent = newRoot
+	tree.root = newRoot
+}
+
+func setParent(nodes []*BTreeNode, parent *BTreeNode) {
+	for _, node := range nodes {
+		node.Parent = parent
+	}
+}
+
+func (tree *BTree) left(node *BTreeNode) *BTreeNode {
+	if tree.size == 0 {
+		return nil
+	}
+	current := node
+	for {
+		if tree.isLeaf(current) {
+			return current
+		}
+		current = current.Children[0]
+	}
+}
+
+func (tree *BTree) right(node *BTreeNode) *BTreeNode {
+	if tree.size == 0 {
+		return nil
+	}
+	current := node
+	for {
+		if tree.isLeaf(current) {
+			return current
+		}
+		current = current.Children[len(current.Children)-1]
+	}
+}
+
+// leftSibling returns the node's left sibling and child index (in parent) if it exists, otherwise (nil,-1)
+// key is any of keys in node (could even be deleted).
+func (tree *BTree) leftSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
+	if node.Parent != nil {
+		index, _ := tree.search(node.Parent, key)
+		index--
+		if index >= 0 && index < len(node.Parent.Children) {
+			return node.Parent.Children[index], index
+		}
+	}
+	return nil, -1
+}
+
+// rightSibling returns the node's right sibling and child index (in parent) if it exists, otherwise (nil,-1)
+// key is any of keys in node (could even be deleted).
+func (tree *BTree) rightSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
+	if node.Parent != nil {
+		index, _ := tree.search(node.Parent, key)
+		index++
+		if index < len(node.Parent.Children) {
+			return node.Parent.Children[index], index
+		}
+	}
+	return nil, -1
+}
+
+// delete deletes an entry in node at entries' index
+// ref.: https://en.wikipedia.org/wiki/B-tree#Deletion
+func (tree *BTree) delete(node *BTreeNode, index int) {
+	// deleting from a leaf node
+	if tree.isLeaf(node) {
+		deletedKey := node.Entries[index].Key
+		tree.deleteEntry(node, index)
+		tree.reBalance(node, deletedKey)
+		if len(tree.root.Entries) == 0 {
+			tree.root = nil
+		}
+		return
+	}
+
+	// deleting from an internal node
+	leftLargestNode := tree.right(node.Children[index]) // largest node in the left sub-tree (assumed to exist)
+	leftLargestEntryIndex := len(leftLargestNode.Entries) - 1
+	node.Entries[index] = leftLargestNode.Entries[leftLargestEntryIndex]
+	deletedKey := leftLargestNode.Entries[leftLargestEntryIndex].Key
+	tree.deleteEntry(leftLargestNode, leftLargestEntryIndex)
+	tree.reBalance(leftLargestNode, deletedKey)
+}
+
+// reBalance reBalances the tree after deletion if necessary and returns true, otherwise false.
+// Note that we first delete the entry and then call reBalance, thus the passed deleted key as reference.
+func (tree *BTree) reBalance(node *BTreeNode, deletedKey interface{}) {
+	// check if re-balancing is needed
+	if node == nil || len(node.Entries) >= tree.minEntries() {
+		return
+	}
+
+	// try to borrow from left sibling
+	leftSibling, leftSiblingIndex := tree.leftSibling(node, deletedKey)
+	if leftSibling != nil && len(leftSibling.Entries) > tree.minEntries() {
+		// rotate right
+		node.Entries = append([]*BTreeEntry{node.Parent.Entries[leftSiblingIndex]}, node.Entries...) // prepend parent's separator entry to node's entries
+		node.Parent.Entries[leftSiblingIndex] = leftSibling.Entries[len(leftSibling.Entries)-1]
+		tree.deleteEntry(leftSibling, len(leftSibling.Entries)-1)
+		if !tree.isLeaf(leftSibling) {
+			leftSiblingRightMostChild := leftSibling.Children[len(leftSibling.Children)-1]
+			leftSiblingRightMostChild.Parent = node
+			node.Children = append([]*BTreeNode{leftSiblingRightMostChild}, node.Children...)
+			tree.deleteChild(leftSibling, len(leftSibling.Children)-1)
+		}
+		return
+	}
+
+	// try to borrow from right sibling
+	rightSibling, rightSiblingIndex := tree.rightSibling(node, deletedKey)
+	if rightSibling != nil && len(rightSibling.Entries) > tree.minEntries() {
+		// rotate left
+		node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1]) // append parent's separator entry to node's entries
+		node.Parent.Entries[rightSiblingIndex-1] = rightSibling.Entries[0]
+		tree.deleteEntry(rightSibling, 0)
+		if !tree.isLeaf(rightSibling) {
+			rightSiblingLeftMostChild := rightSibling.Children[0]
+			rightSiblingLeftMostChild.Parent = node
+			node.Children = append(node.Children, rightSiblingLeftMostChild)
+			tree.deleteChild(rightSibling, 0)
+		}
+		return
+	}
+
+	// merge with siblings
+	if rightSibling != nil {
+		// merge with right sibling
+		node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1])
+		node.Entries = append(node.Entries, rightSibling.Entries...)
+		deletedKey = node.Parent.Entries[rightSiblingIndex-1].Key
+		tree.deleteEntry(node.Parent, rightSiblingIndex-1)
+		tree.appendChildren(node.Parent.Children[rightSiblingIndex], node)
+		tree.deleteChild(node.Parent, rightSiblingIndex)
+	} else if leftSibling != nil {
+		// merge with left sibling
+		entries := append([]*BTreeEntry(nil), leftSibling.Entries...)
+		entries = append(entries, node.Parent.Entries[leftSiblingIndex])
+		node.Entries = append(entries, node.Entries...)
+		deletedKey = node.Parent.Entries[leftSiblingIndex].Key
+		tree.deleteEntry(node.Parent, leftSiblingIndex)
+		tree.prependChildren(node.Parent.Children[leftSiblingIndex], node)
+		tree.deleteChild(node.Parent, leftSiblingIndex)
+	}
+
+	// make the merged node the root if its parent was the root and the root is empty
+	if node.Parent == tree.root && len(tree.root.Entries) == 0 {
+		tree.root = node
+		node.Parent = nil
+		return
+	}
+
+	// parent might be underflow, so try to reBalance if necessary
+	tree.reBalance(node.Parent, deletedKey)
+}
+
+func (tree *BTree) prependChildren(fromNode *BTreeNode, toNode *BTreeNode) {
+	children := append([]*BTreeNode(nil), fromNode.Children...)
+	toNode.Children = append(children, toNode.Children...)
+	setParent(fromNode.Children, toNode)
+}
+
+func (tree *BTree) appendChildren(fromNode *BTreeNode, toNode *BTreeNode) {
+	toNode.Children = append(toNode.Children, fromNode.Children...)
+	setParent(fromNode.Children, toNode)
+}
+
+func (tree *BTree) deleteEntry(node *BTreeNode, index int) {
+	copy(node.Entries[index:], node.Entries[index+1:])
+	node.Entries[len(node.Entries)-1] = nil
+	node.Entries = node.Entries[:len(node.Entries)-1]
+}
+
+func (tree *BTree) deleteChild(node *BTreeNode, index int) {
+	if index >= len(node.Children) {
+		return
+	}
+	copy(node.Children[index:], node.Children[index+1:])
+	node.Children[len(node.Children)-1] = nil
+	node.Children = node.Children[:len(node.Children)-1]
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (tree BTree) MarshalJSON() (jsonBytes []byte, err error) {
+	if tree.root == nil {
+		return []byte("null"), nil
+	}
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('{')
+	tree.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
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it panics.
+func (tree *BTree) getComparator() func(a, b interface{}) int {
+	if tree.comparator == nil {
+		panic("comparator is missing for tree")
+	}
+	return tree.comparator
+}

+ 991 - 0
vendor/github.com/gogf/gf/v2/container/gtree/gtree_redblacktree.go

@@ -0,0 +1,991 @@
+// 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 gtree
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/gogf/gf/v2/container/gvar"
+	"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/gutil"
+)
+
+type color bool
+
+const (
+	black, red color = true, false
+)
+
+// RedBlackTree holds elements of the red-black tree.
+type RedBlackTree struct {
+	mu         rwmutex.RWMutex
+	root       *RedBlackTreeNode
+	size       int
+	comparator func(v1, v2 interface{}) int
+}
+
+// RedBlackTreeNode is a single element within the tree.
+type RedBlackTreeNode struct {
+	Key    interface{}
+	Value  interface{}
+	color  color
+	left   *RedBlackTreeNode
+	right  *RedBlackTreeNode
+	parent *RedBlackTreeNode
+}
+
+// NewRedBlackTree instantiates a red-black tree with the custom key comparator.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+func NewRedBlackTree(comparator func(v1, v2 interface{}) int, safe ...bool) *RedBlackTree {
+	return &RedBlackTree{
+		mu:         rwmutex.Create(safe...),
+		comparator: comparator,
+	}
+}
+
+// NewRedBlackTreeFrom instantiates a red-black tree with the custom key comparator and `data` map.
+// The parameter `safe` is used to specify whether using tree in concurrent-safety,
+// which is false in default.
+func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *RedBlackTree {
+	tree := NewRedBlackTree(comparator, safe...)
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+	return tree
+}
+
+// SetComparator sets/changes the comparator for sorting.
+func (tree *RedBlackTree) SetComparator(comparator func(a, b interface{}) int) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.comparator = comparator
+	if tree.size > 0 {
+		data := make(map[interface{}]interface{}, tree.size)
+		tree.doIteratorAsc(tree.leftNode(), func(key, value interface{}) bool {
+			data[key] = value
+			return true
+		})
+		// Resort the tree if comparator is changed.
+		tree.root = nil
+		tree.size = 0
+		for k, v := range data {
+			tree.doSet(k, v)
+		}
+	}
+}
+
+// Clone returns a new tree with a copy of current tree.
+func (tree *RedBlackTree) Clone() *RedBlackTree {
+	newTree := NewRedBlackTree(tree.comparator, tree.mu.IsSafe())
+	newTree.Sets(tree.Map())
+	return newTree
+}
+
+// Set inserts key-value item into the tree.
+func (tree *RedBlackTree) Set(key interface{}, value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.doSet(key, value)
+}
+
+// Sets batch sets key-values to the tree.
+func (tree *RedBlackTree) Sets(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+}
+
+// doSet inserts key-value item into the tree without mutex.
+func (tree *RedBlackTree) doSet(key interface{}, value interface{}) {
+	insertedNode := (*RedBlackTreeNode)(nil)
+	if tree.root == nil {
+		// Assert key is of comparator's type for initial tree
+		tree.getComparator()(key, key)
+		tree.root = &RedBlackTreeNode{Key: key, Value: value, color: red}
+		insertedNode = tree.root
+	} else {
+		node := tree.root
+		loop := true
+		for loop {
+			compare := tree.getComparator()(key, node.Key)
+			switch {
+			case compare == 0:
+				// node.Key   = key
+				node.Value = value
+				return
+			case compare < 0:
+				if node.left == nil {
+					node.left = &RedBlackTreeNode{Key: key, Value: value, color: red}
+					insertedNode = node.left
+					loop = false
+				} else {
+					node = node.left
+				}
+			case compare > 0:
+				if node.right == nil {
+					node.right = &RedBlackTreeNode{Key: key, Value: value, color: red}
+					insertedNode = node.right
+					loop = false
+				} else {
+					node = node.right
+				}
+			}
+		}
+		insertedNode.parent = node
+	}
+	tree.insertCase1(insertedNode)
+	tree.size++
+}
+
+// Get searches the node in the tree by `key` and returns its value or nil if key is not found in tree.
+func (tree *RedBlackTree) Get(key interface{}) (value interface{}) {
+	value, _ = tree.Search(key)
+	return
+}
+
+// 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 (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	if node, found := tree.doSearch(key); found {
+		return node.Value
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		tree.doSet(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 (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.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 (tree *RedBlackTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
+	if v, ok := tree.Search(key); !ok {
+		return tree.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a gvar.Var with the value by given `key`.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *RedBlackTree) GetVar(key interface{}) *gvar.Var {
+	return gvar.New(tree.Get(key))
+}
+
+// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *RedBlackTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *RedBlackTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
+// The returned gvar.Var is un-concurrent safe.
+func (tree *RedBlackTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(tree.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 (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.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 (tree *RedBlackTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
+	if !tree.Contains(key) {
+		tree.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Contains checks whether `key` exists in the tree.
+func (tree *RedBlackTree) Contains(key interface{}) bool {
+	_, ok := tree.Search(key)
+	return ok
+}
+
+// doRemove removes the node from the tree by `key` without mutex.
+func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) {
+	child := (*RedBlackTreeNode)(nil)
+	node, found := tree.doSearch(key)
+	if !found {
+		return
+	}
+	value = node.Value
+	if node.left != nil && node.right != nil {
+		p := node.left.maximumNode()
+		node.Key = p.Key
+		node.Value = p.Value
+		node = p
+	}
+	if node.left == nil || node.right == nil {
+		if node.right == nil {
+			child = node.left
+		} else {
+			child = node.right
+		}
+		if node.color == black {
+			node.color = tree.nodeColor(child)
+			tree.deleteCase1(node)
+		}
+		tree.replaceNode(node, child)
+		if node.parent == nil && child != nil {
+			child.color = black
+		}
+	}
+	tree.size--
+	return
+}
+
+// Remove removes the node from the tree by `key`.
+func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	return tree.doRemove(key)
+}
+
+// Removes batch deletes values of the tree by `keys`.
+func (tree *RedBlackTree) Removes(keys []interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	for _, key := range keys {
+		tree.doRemove(key)
+	}
+}
+
+// IsEmpty returns true if tree does not contain any nodes.
+func (tree *RedBlackTree) IsEmpty() bool {
+	return tree.Size() == 0
+}
+
+// Size returns number of nodes in the tree.
+func (tree *RedBlackTree) Size() int {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	return tree.size
+}
+
+// Keys returns all keys in asc order.
+func (tree *RedBlackTree) Keys() []interface{} {
+	var (
+		keys  = make([]interface{}, tree.Size())
+		index = 0
+	)
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		keys[index] = key
+		index++
+		return true
+	})
+	return keys
+}
+
+// Values returns all values in asc order based on the key.
+func (tree *RedBlackTree) Values() []interface{} {
+	var (
+		values = make([]interface{}, tree.Size())
+		index  = 0
+	)
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		values[index] = value
+		index++
+		return true
+	})
+	return values
+}
+
+// Map returns all key-value items as map.
+func (tree *RedBlackTree) Map() map[interface{}]interface{} {
+	m := make(map[interface{}]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[key] = value
+		return true
+	})
+	return m
+}
+
+// MapStrAny returns all key-value items as map[string]interface{}.
+func (tree *RedBlackTree) MapStrAny() map[string]interface{} {
+	m := make(map[string]interface{}, tree.Size())
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		m[gconv.String(key)] = value
+		return true
+	})
+	return m
+}
+
+// Left returns the left-most (min) node or nil if tree is empty.
+func (tree *RedBlackTree) Left() *RedBlackTreeNode {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.leftNode()
+	if tree.mu.IsSafe() {
+		return &RedBlackTreeNode{
+			Key:   node.Key,
+			Value: node.Value,
+		}
+	}
+	return node
+}
+
+// Right returns the right-most (max) node or nil if tree is empty.
+func (tree *RedBlackTree) Right() *RedBlackTreeNode {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node := tree.rightNode()
+	if tree.mu.IsSafe() {
+		return &RedBlackTreeNode{
+			Key:   node.Key,
+			Value: node.Value,
+		}
+	}
+	return node
+}
+
+// leftNode returns the left-most (min) node or nil if tree is empty.
+func (tree *RedBlackTree) leftNode() *RedBlackTreeNode {
+	p := (*RedBlackTreeNode)(nil)
+	n := tree.root
+	for n != nil {
+		p = n
+		n = n.left
+	}
+	return p
+}
+
+// rightNode returns the right-most (max) node or nil if tree is empty.
+func (tree *RedBlackTree) rightNode() *RedBlackTreeNode {
+	p := (*RedBlackTreeNode)(nil)
+	n := tree.root
+	for n != nil {
+		p = n
+		n = n.right
+	}
+	return p
+}
+
+// Floor Finds floor node of the input key, return the floor node or nil if no floor node is found.
+// Second return parameter is true if floor was found, otherwise false.
+//
+// Floor node is defined as the largest node that its key is smaller than or equal to the given `key`.
+// A floor node may not be found, either because the tree is empty, or because
+// all nodes in the tree are larger than the given node.
+func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	n := tree.root
+	for n != nil {
+		compare := tree.getComparator()(key, n.Key)
+		switch {
+		case compare == 0:
+			return n, true
+		case compare < 0:
+			n = n.left
+		case compare > 0:
+			floor, found = n, true
+			n = n.right
+		}
+	}
+	if found {
+		return
+	}
+	return nil, false
+}
+
+// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling node is found.
+// Second return parameter is true if ceiling was found, otherwise false.
+//
+// Ceiling node is defined as the smallest node that its key is larger than or equal to the given `key`.
+// A ceiling node may not be found, either because the tree is empty, or because
+// all nodes in the tree are smaller than the given node.
+func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	n := tree.root
+	for n != nil {
+		compare := tree.getComparator()(key, n.Key)
+		switch {
+		case compare == 0:
+			return n, true
+		case compare > 0:
+			n = n.right
+		case compare < 0:
+			ceiling, found = n, true
+			n = n.left
+		}
+	}
+	if found {
+		return
+	}
+	return nil, false
+}
+
+// Iterator is alias of IteratorAsc.
+func (tree *RedBlackTree) Iterator(f func(key, value interface{}) bool) {
+	tree.IteratorAsc(f)
+}
+
+// IteratorFrom is alias of IteratorAscFrom.
+func (tree *RedBlackTree) IteratorFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.IteratorAscFrom(key, match, f)
+}
+
+// IteratorAsc iterates the tree readonly in ascending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *RedBlackTree) IteratorAsc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	tree.doIteratorAsc(tree.leftNode(), f)
+}
+
+// IteratorAscFrom iterates the tree readonly in ascending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *RedBlackTree) IteratorAscFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, found := tree.doSearch(key)
+	if match {
+		if found {
+			tree.doIteratorAsc(node, f)
+		}
+	} else {
+		tree.doIteratorAsc(node, f)
+	}
+}
+
+func (tree *RedBlackTree) doIteratorAsc(node *RedBlackTreeNode, f func(key, value interface{}) bool) {
+loop:
+	if node == nil {
+		return
+	}
+	if !f(node.Key, node.Value) {
+		return
+	}
+	if node.right != nil {
+		node = node.right
+		for node.left != nil {
+			node = node.left
+		}
+		goto loop
+	}
+	if node.parent != nil {
+		old := node
+		for node.parent != nil {
+			node = node.parent
+			if tree.getComparator()(old.Key, node.Key) <= 0 {
+				goto loop
+			}
+		}
+	}
+}
+
+// IteratorDesc iterates the tree readonly in descending order with given callback function `f`.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *RedBlackTree) IteratorDesc(f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	tree.doIteratorDesc(tree.rightNode(), f)
+}
+
+// IteratorDescFrom iterates the tree readonly in descending order with given callback function `f`.
+// The parameter `key` specifies the start entry for iterating. The `match` specifies whether
+// starting iterating if the `key` is fully matched, or else using index searching iterating.
+// If `f` returns true, then it continues iterating; or false to stop.
+func (tree *RedBlackTree) IteratorDescFrom(key interface{}, match bool, f func(key, value interface{}) bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, found := tree.doSearch(key)
+	if match {
+		if found {
+			tree.doIteratorDesc(node, f)
+		}
+	} else {
+		tree.doIteratorDesc(node, f)
+	}
+}
+
+func (tree *RedBlackTree) doIteratorDesc(node *RedBlackTreeNode, f func(key, value interface{}) bool) {
+loop:
+	if node == nil {
+		return
+	}
+	if !f(node.Key, node.Value) {
+		return
+	}
+	if node.left != nil {
+		node = node.left
+		for node.right != nil {
+			node = node.right
+		}
+		goto loop
+	}
+	if node.parent != nil {
+		old := node
+		for node.parent != nil {
+			node = node.parent
+			if tree.getComparator()(old.Key, node.Key) >= 0 {
+				goto loop
+			}
+		}
+	}
+}
+
+// Clear removes all nodes from the tree.
+func (tree *RedBlackTree) Clear() {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+}
+
+// Replace the data of the tree with given `data`.
+func (tree *RedBlackTree) Replace(data map[interface{}]interface{}) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	tree.root = nil
+	tree.size = 0
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+}
+
+// String returns a string representation of container.
+func (tree *RedBlackTree) String() string {
+	if tree == nil {
+		return ""
+	}
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	str := ""
+	if tree.size != 0 {
+		tree.output(tree.root, "", true, &str)
+	}
+	return str
+}
+
+// Print prints the tree to stdout.
+func (tree *RedBlackTree) Print() {
+	fmt.Println(tree.String())
+}
+
+// Search searches the tree with given `key`.
+// Second return parameter `found` is true if key was found, otherwise false.
+func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool) {
+	tree.mu.RLock()
+	defer tree.mu.RUnlock()
+	node, found := tree.doSearch(key)
+	if found {
+		return node.Value, true
+	}
+	return nil, false
+}
+
+// Flip exchanges key-value of the tree to value-key.
+// Note that you should guarantee the value is the same type as key,
+// or else the comparator would panic.
+//
+// If the type of value is different with key, you pass the new `comparator`.
+func (tree *RedBlackTree) Flip(comparator ...func(v1, v2 interface{}) int) {
+	t := (*RedBlackTree)(nil)
+	if len(comparator) > 0 {
+		t = NewRedBlackTree(comparator[0], tree.mu.IsSafe())
+	} else {
+		t = NewRedBlackTree(tree.comparator, tree.mu.IsSafe())
+	}
+	tree.IteratorAsc(func(key, value interface{}) bool {
+		t.doSet(value, key)
+		return true
+	})
+	tree.mu.Lock()
+	tree.root = t.root
+	tree.size = t.size
+	tree.mu.Unlock()
+}
+
+func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail bool, str *string) {
+	if node.right != nil {
+		newPrefix := prefix
+		if isTail {
+			newPrefix += "│   "
+		} else {
+			newPrefix += "    "
+		}
+		tree.output(node.right, newPrefix, false, str)
+	}
+	*str += prefix
+	if isTail {
+		*str += "└── "
+	} else {
+		*str += "┌── "
+	}
+	*str += fmt.Sprintf("%v\n", node.Key)
+	if node.left != nil {
+		newPrefix := prefix
+		if isTail {
+			newPrefix += "    "
+		} else {
+			newPrefix += "│   "
+		}
+		tree.output(node.left, newPrefix, true, str)
+	}
+}
+
+// doSearch searches the tree with given `key` without mutex.
+// It returns the node if found or otherwise nil.
+func (tree *RedBlackTree) doSearch(key interface{}) (node *RedBlackTreeNode, found bool) {
+	node = tree.root
+	for node != nil {
+		compare := tree.getComparator()(key, node.Key)
+		switch {
+		case compare == 0:
+			return node, true
+		case compare < 0:
+			node = node.left
+		case compare > 0:
+			node = node.right
+		}
+	}
+	return node, false
+}
+
+func (node *RedBlackTreeNode) grandparent() *RedBlackTreeNode {
+	if node != nil && node.parent != nil {
+		return node.parent.parent
+	}
+	return nil
+}
+
+func (node *RedBlackTreeNode) uncle() *RedBlackTreeNode {
+	if node == nil || node.parent == nil || node.parent.parent == nil {
+		return nil
+	}
+	return node.parent.sibling()
+}
+
+func (node *RedBlackTreeNode) sibling() *RedBlackTreeNode {
+	if node == nil || node.parent == nil {
+		return nil
+	}
+	if node == node.parent.left {
+		return node.parent.right
+	}
+	return node.parent.left
+}
+
+func (tree *RedBlackTree) rotateLeft(node *RedBlackTreeNode) {
+	right := node.right
+	tree.replaceNode(node, right)
+	node.right = right.left
+	if right.left != nil {
+		right.left.parent = node
+	}
+	right.left = node
+	node.parent = right
+}
+
+func (tree *RedBlackTree) rotateRight(node *RedBlackTreeNode) {
+	left := node.left
+	tree.replaceNode(node, left)
+	node.left = left.right
+	if left.right != nil {
+		left.right.parent = node
+	}
+	left.right = node
+	node.parent = left
+}
+
+func (tree *RedBlackTree) replaceNode(old *RedBlackTreeNode, new *RedBlackTreeNode) {
+	if old.parent == nil {
+		tree.root = new
+	} else {
+		if old == old.parent.left {
+			old.parent.left = new
+		} else {
+			old.parent.right = new
+		}
+	}
+	if new != nil {
+		new.parent = old.parent
+	}
+}
+
+func (tree *RedBlackTree) insertCase1(node *RedBlackTreeNode) {
+	if node.parent == nil {
+		node.color = black
+	} else {
+		tree.insertCase2(node)
+	}
+}
+
+func (tree *RedBlackTree) insertCase2(node *RedBlackTreeNode) {
+	if tree.nodeColor(node.parent) == black {
+		return
+	}
+	tree.insertCase3(node)
+}
+
+func (tree *RedBlackTree) insertCase3(node *RedBlackTreeNode) {
+	uncle := node.uncle()
+	if tree.nodeColor(uncle) == red {
+		node.parent.color = black
+		uncle.color = black
+		node.grandparent().color = red
+		tree.insertCase1(node.grandparent())
+	} else {
+		tree.insertCase4(node)
+	}
+}
+
+func (tree *RedBlackTree) insertCase4(node *RedBlackTreeNode) {
+	grandparent := node.grandparent()
+	if node == node.parent.right && node.parent == grandparent.left {
+		tree.rotateLeft(node.parent)
+		node = node.left
+	} else if node == node.parent.left && node.parent == grandparent.right {
+		tree.rotateRight(node.parent)
+		node = node.right
+	}
+	tree.insertCase5(node)
+}
+
+func (tree *RedBlackTree) insertCase5(node *RedBlackTreeNode) {
+	node.parent.color = black
+	grandparent := node.grandparent()
+	grandparent.color = red
+	if node == node.parent.left && node.parent == grandparent.left {
+		tree.rotateRight(grandparent)
+	} else if node == node.parent.right && node.parent == grandparent.right {
+		tree.rotateLeft(grandparent)
+	}
+}
+
+func (node *RedBlackTreeNode) maximumNode() *RedBlackTreeNode {
+	if node == nil {
+		return nil
+	}
+	for node.right != nil {
+		return node.right
+	}
+	return node
+}
+
+func (tree *RedBlackTree) deleteCase1(node *RedBlackTreeNode) {
+	if node.parent == nil {
+		return
+	}
+	tree.deleteCase2(node)
+}
+
+func (tree *RedBlackTree) deleteCase2(node *RedBlackTreeNode) {
+	sibling := node.sibling()
+	if tree.nodeColor(sibling) == red {
+		node.parent.color = red
+		sibling.color = black
+		if node == node.parent.left {
+			tree.rotateLeft(node.parent)
+		} else {
+			tree.rotateRight(node.parent)
+		}
+	}
+	tree.deleteCase3(node)
+}
+
+func (tree *RedBlackTree) deleteCase3(node *RedBlackTreeNode) {
+	sibling := node.sibling()
+	if tree.nodeColor(node.parent) == black &&
+		tree.nodeColor(sibling) == black &&
+		tree.nodeColor(sibling.left) == black &&
+		tree.nodeColor(sibling.right) == black {
+		sibling.color = red
+		tree.deleteCase1(node.parent)
+	} else {
+		tree.deleteCase4(node)
+	}
+}
+
+func (tree *RedBlackTree) deleteCase4(node *RedBlackTreeNode) {
+	sibling := node.sibling()
+	if tree.nodeColor(node.parent) == red &&
+		tree.nodeColor(sibling) == black &&
+		tree.nodeColor(sibling.left) == black &&
+		tree.nodeColor(sibling.right) == black {
+		sibling.color = red
+		node.parent.color = black
+	} else {
+		tree.deleteCase5(node)
+	}
+}
+
+func (tree *RedBlackTree) deleteCase5(node *RedBlackTreeNode) {
+	sibling := node.sibling()
+	if node == node.parent.left &&
+		tree.nodeColor(sibling) == black &&
+		tree.nodeColor(sibling.left) == red &&
+		tree.nodeColor(sibling.right) == black {
+		sibling.color = red
+		sibling.left.color = black
+		tree.rotateRight(sibling)
+	} else if node == node.parent.right &&
+		tree.nodeColor(sibling) == black &&
+		tree.nodeColor(sibling.right) == red &&
+		tree.nodeColor(sibling.left) == black {
+		sibling.color = red
+		sibling.right.color = black
+		tree.rotateLeft(sibling)
+	}
+	tree.deleteCase6(node)
+}
+
+func (tree *RedBlackTree) deleteCase6(node *RedBlackTreeNode) {
+	sibling := node.sibling()
+	sibling.color = tree.nodeColor(node.parent)
+	node.parent.color = black
+	if node == node.parent.left && tree.nodeColor(sibling.right) == red {
+		sibling.right.color = black
+		tree.rotateLeft(node.parent)
+	} else if tree.nodeColor(sibling.left) == red {
+		sibling.left.color = black
+		tree.rotateRight(node.parent)
+	}
+}
+
+func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
+	if node == nil {
+		return black
+	}
+	return node.color
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (tree RedBlackTree) MarshalJSON() (jsonBytes []byte, err error) {
+	if tree.root == nil {
+		return []byte("null"), nil
+	}
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('{')
+	tree.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 (tree *RedBlackTree) UnmarshalJSON(b []byte) error {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	if tree.comparator == nil {
+		tree.comparator = gutil.ComparatorString
+	}
+	var data map[string]interface{}
+	if err := json.UnmarshalUseNumber(b, &data); err != nil {
+		return err
+	}
+	for k, v := range data {
+		tree.doSet(k, v)
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (tree *RedBlackTree) UnmarshalValue(value interface{}) (err error) {
+	tree.mu.Lock()
+	defer tree.mu.Unlock()
+	if tree.comparator == nil {
+		tree.comparator = gutil.ComparatorString
+	}
+	for k, v := range gconv.Map(value) {
+		tree.doSet(k, v)
+	}
+	return
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it panics.
+func (tree *RedBlackTree) getComparator() func(a, b interface{}) int {
+	if tree.comparator == nil {
+		panic("comparator is missing for tree")
+	}
+	return tree.comparator
+}

+ 14 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype.go

@@ -0,0 +1,14 @@
+// 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 gtype provides high performance and concurrent-safe basic variable types.
+package gtype
+
+// New is alias of NewInterface.
+// See NewInterface.
+func New(value ...interface{}) *Interface {
+	return NewInterface(value...)
+}

+ 106 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_bool.go

@@ -0,0 +1,106 @@
+// 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 gtype
+
+import (
+	"bytes"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Bool is a struct for concurrent-safe operation for type bool.
+type Bool struct {
+	value int32
+}
+
+var (
+	bytesTrue  = []byte("true")
+	bytesFalse = []byte("false")
+)
+
+// NewBool creates and returns a concurrent-safe object for bool type,
+// with given initial value `value`.
+func NewBool(value ...bool) *Bool {
+	t := &Bool{}
+	if len(value) > 0 {
+		if value[0] {
+			t.value = 1
+		} else {
+			t.value = 0
+		}
+	}
+	return t
+}
+
+// Clone clones and returns a new concurrent-safe object for bool type.
+func (v *Bool) Clone() *Bool {
+	return NewBool(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Bool) Set(value bool) (old bool) {
+	if value {
+		old = atomic.SwapInt32(&v.value, 1) == 1
+	} else {
+		old = atomic.SwapInt32(&v.value, 0) == 1
+	}
+	return
+}
+
+// Val atomically loads and returns t.value.
+func (v *Bool) Val() bool {
+	return atomic.LoadInt32(&v.value) > 0
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Bool) Cas(old, new bool) (swapped bool) {
+	var oldInt32, newInt32 int32
+	if old {
+		oldInt32 = 1
+	}
+	if new {
+		newInt32 = 1
+	}
+	return atomic.CompareAndSwapInt32(&v.value, oldInt32, newInt32)
+}
+
+// String implements String interface for string printing.
+func (v *Bool) String() string {
+	if v.Val() {
+		return "true"
+	}
+	return "false"
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Bool) MarshalJSON() ([]byte, error) {
+	if v.Val() {
+		return bytesTrue, nil
+	}
+	return bytesFalse, nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Bool) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Bool(bytes.Trim(b, `"`)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Bool) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Bool(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Bool) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewBool(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_byte.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Byte is a struct for concurrent-safe operation for type byte.
+type Byte struct {
+	value int32
+}
+
+// NewByte creates and returns a concurrent-safe object for byte type,
+// with given initial value `value`.
+func NewByte(value ...byte) *Byte {
+	if len(value) > 0 {
+		return &Byte{
+			value: int32(value[0]),
+		}
+	}
+	return &Byte{}
+}
+
+// Clone clones and returns a new concurrent-safe object for byte type.
+func (v *Byte) Clone() *Byte {
+	return NewByte(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Byte) Set(value byte) (old byte) {
+	return byte(atomic.SwapInt32(&v.value, int32(value)))
+}
+
+// Val atomically loads and returns t.value.
+func (v *Byte) Val() byte {
+	return byte(atomic.LoadInt32(&v.value))
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Byte) Add(delta byte) (new byte) {
+	return byte(atomic.AddInt32(&v.value, int32(delta)))
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Byte) Cas(old, new byte) (swapped bool) {
+	return atomic.CompareAndSwapInt32(&v.value, int32(old), int32(new))
+}
+
+// String implements String interface for string printing.
+func (v *Byte) String() string {
+	return strconv.FormatUint(uint64(v.Val()), 10)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Byte) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Byte) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Uint8(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Byte) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Byte(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Byte) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewByte(v.Val())
+}

+ 96 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_bytes.go

@@ -0,0 +1,96 @@
+// 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 gtype
+
+import (
+	"bytes"
+	"encoding/base64"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Bytes is a struct for concurrent-safe operation for type []byte.
+type Bytes struct {
+	value atomic.Value
+}
+
+// NewBytes creates and returns a concurrent-safe object for []byte type,
+// with given initial value `value`.
+func NewBytes(value ...[]byte) *Bytes {
+	t := &Bytes{}
+	if len(value) > 0 {
+		t.value.Store(value[0])
+	}
+	return t
+}
+
+// Clone clones and returns a new shallow copy object for []byte type.
+func (v *Bytes) Clone() *Bytes {
+	return NewBytes(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+// Note: The parameter `value` cannot be nil.
+func (v *Bytes) Set(value []byte) (old []byte) {
+	old = v.Val()
+	v.value.Store(value)
+	return
+}
+
+// Val atomically loads and returns t.value.
+func (v *Bytes) Val() []byte {
+	if s := v.value.Load(); s != nil {
+		return s.([]byte)
+	}
+	return nil
+}
+
+// String implements String interface for string printing.
+func (v *Bytes) String() string {
+	return string(v.Val())
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Bytes) MarshalJSON() ([]byte, error) {
+	val := v.Val()
+	dst := make([]byte, base64.StdEncoding.EncodedLen(len(val)))
+	base64.StdEncoding.Encode(dst, val)
+	return []byte(`"` + string(dst) + `"`), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Bytes) UnmarshalJSON(b []byte) error {
+	var (
+		src    = make([]byte, base64.StdEncoding.DecodedLen(len(b)))
+		n, err = base64.StdEncoding.Decode(src, bytes.Trim(b, `"`))
+	)
+	if err != nil {
+		err = gerror.Wrap(err, `base64.StdEncoding.Decode failed`)
+		return err
+	}
+	v.Set(src[:n])
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Bytes) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Bytes(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Bytes) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	oldBytes := v.Val()
+	newBytes := make([]byte, len(oldBytes))
+	copy(newBytes, oldBytes)
+	return NewBytes(newBytes)
+}

+ 97 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_float32.go

@@ -0,0 +1,97 @@
+// 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 gtype
+
+import (
+	"math"
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Float32 is a struct for concurrent-safe operation for type float32.
+type Float32 struct {
+	value uint32
+}
+
+// NewFloat32 creates and returns a concurrent-safe object for float32 type,
+// with given initial value `value`.
+func NewFloat32(value ...float32) *Float32 {
+	if len(value) > 0 {
+		return &Float32{
+			value: math.Float32bits(value[0]),
+		}
+	}
+	return &Float32{}
+}
+
+// Clone clones and returns a new concurrent-safe object for float32 type.
+func (v *Float32) Clone() *Float32 {
+	return NewFloat32(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Float32) Set(value float32) (old float32) {
+	return math.Float32frombits(atomic.SwapUint32(&v.value, math.Float32bits(value)))
+}
+
+// Val atomically loads and returns t.value.
+func (v *Float32) Val() float32 {
+	return math.Float32frombits(atomic.LoadUint32(&v.value))
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Float32) Add(delta float32) (new float32) {
+	for {
+		old := math.Float32frombits(v.value)
+		new = old + delta
+		if atomic.CompareAndSwapUint32(
+			&v.value,
+			math.Float32bits(old),
+			math.Float32bits(new),
+		) {
+			break
+		}
+	}
+	return
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Float32) Cas(old, new float32) (swapped bool) {
+	return atomic.CompareAndSwapUint32(&v.value, math.Float32bits(old), math.Float32bits(new))
+}
+
+// String implements String interface for string printing.
+func (v *Float32) String() string {
+	return strconv.FormatFloat(float64(v.Val()), 'g', -1, 32)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Float32) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatFloat(float64(v.Val()), 'g', -1, 32)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Float32) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Float32(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Float32) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Float32(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Float32) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewFloat32(v.Val())
+}

+ 97 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_float64.go

@@ -0,0 +1,97 @@
+// 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 gtype
+
+import (
+	"math"
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Float64 is a struct for concurrent-safe operation for type float64.
+type Float64 struct {
+	value uint64
+}
+
+// NewFloat64 creates and returns a concurrent-safe object for float64 type,
+// with given initial value `value`.
+func NewFloat64(value ...float64) *Float64 {
+	if len(value) > 0 {
+		return &Float64{
+			value: math.Float64bits(value[0]),
+		}
+	}
+	return &Float64{}
+}
+
+// Clone clones and returns a new concurrent-safe object for float64 type.
+func (v *Float64) Clone() *Float64 {
+	return NewFloat64(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Float64) Set(value float64) (old float64) {
+	return math.Float64frombits(atomic.SwapUint64(&v.value, math.Float64bits(value)))
+}
+
+// Val atomically loads and returns t.value.
+func (v *Float64) Val() float64 {
+	return math.Float64frombits(atomic.LoadUint64(&v.value))
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Float64) Add(delta float64) (new float64) {
+	for {
+		old := math.Float64frombits(v.value)
+		new = old + delta
+		if atomic.CompareAndSwapUint64(
+			&v.value,
+			math.Float64bits(old),
+			math.Float64bits(new),
+		) {
+			break
+		}
+	}
+	return
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Float64) Cas(old, new float64) (swapped bool) {
+	return atomic.CompareAndSwapUint64(&v.value, math.Float64bits(old), math.Float64bits(new))
+}
+
+// String implements String interface for string printing.
+func (v *Float64) String() string {
+	return strconv.FormatFloat(v.Val(), 'g', -1, 64)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Float64) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatFloat(v.Val(), 'g', -1, 64)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Float64) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Float64(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Float64) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Float64(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Float64) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewFloat64(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_int.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Int is a struct for concurrent-safe operation for type int.
+type Int struct {
+	value int64
+}
+
+// NewInt creates and returns a concurrent-safe object for int type,
+// with given initial value `value`.
+func NewInt(value ...int) *Int {
+	if len(value) > 0 {
+		return &Int{
+			value: int64(value[0]),
+		}
+	}
+	return &Int{}
+}
+
+// Clone clones and returns a new concurrent-safe object for int type.
+func (v *Int) Clone() *Int {
+	return NewInt(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Int) Set(value int) (old int) {
+	return int(atomic.SwapInt64(&v.value, int64(value)))
+}
+
+// Val atomically loads and returns t.value.
+func (v *Int) Val() int {
+	return int(atomic.LoadInt64(&v.value))
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Int) Add(delta int) (new int) {
+	return int(atomic.AddInt64(&v.value, int64(delta)))
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Int) Cas(old, new int) (swapped bool) {
+	return atomic.CompareAndSwapInt64(&v.value, int64(old), int64(new))
+}
+
+// String implements String interface for string printing.
+func (v *Int) String() string {
+	return strconv.Itoa(v.Val())
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Int) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.Itoa(v.Val())), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Int) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Int(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Int) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Int(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Int) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewInt(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_int32.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Int32 is a struct for concurrent-safe operation for type int32.
+type Int32 struct {
+	value int32
+}
+
+// NewInt32 creates and returns a concurrent-safe object for int32 type,
+// with given initial value `value`.
+func NewInt32(value ...int32) *Int32 {
+	if len(value) > 0 {
+		return &Int32{
+			value: value[0],
+		}
+	}
+	return &Int32{}
+}
+
+// Clone clones and returns a new concurrent-safe object for int32 type.
+func (v *Int32) Clone() *Int32 {
+	return NewInt32(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Int32) Set(value int32) (old int32) {
+	return atomic.SwapInt32(&v.value, value)
+}
+
+// Val atomically loads and returns t.value.
+func (v *Int32) Val() int32 {
+	return atomic.LoadInt32(&v.value)
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Int32) Add(delta int32) (new int32) {
+	return atomic.AddInt32(&v.value, delta)
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Int32) Cas(old, new int32) (swapped bool) {
+	return atomic.CompareAndSwapInt32(&v.value, old, new)
+}
+
+// String implements String interface for string printing.
+func (v *Int32) String() string {
+	return strconv.Itoa(int(v.Val()))
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Int32) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.Itoa(int(v.Val()))), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Int32) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Int32(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Int32) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Int32(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Int32) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewInt32(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_int64.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Int64 is a struct for concurrent-safe operation for type int64.
+type Int64 struct {
+	value int64
+}
+
+// NewInt64 creates and returns a concurrent-safe object for int64 type,
+// with given initial value `value`.
+func NewInt64(value ...int64) *Int64 {
+	if len(value) > 0 {
+		return &Int64{
+			value: value[0],
+		}
+	}
+	return &Int64{}
+}
+
+// Clone clones and returns a new concurrent-safe object for int64 type.
+func (v *Int64) Clone() *Int64 {
+	return NewInt64(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Int64) Set(value int64) (old int64) {
+	return atomic.SwapInt64(&v.value, value)
+}
+
+// Val atomically loads and returns t.value.
+func (v *Int64) Val() int64 {
+	return atomic.LoadInt64(&v.value)
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Int64) Add(delta int64) (new int64) {
+	return atomic.AddInt64(&v.value, delta)
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Int64) Cas(old, new int64) (swapped bool) {
+	return atomic.CompareAndSwapInt64(&v.value, old, new)
+}
+
+// String implements String interface for string printing.
+func (v *Int64) String() string {
+	return strconv.FormatInt(v.Val(), 10)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Int64) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatInt(v.Val(), 10)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Int64) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Int64(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Int64) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Int64(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Int64) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewInt64(v.Val())
+}

+ 82 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_interface.go

@@ -0,0 +1,82 @@
+// 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 gtype
+
+import (
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/internal/deepcopy"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Interface is a struct for concurrent-safe operation for type interface{}.
+type Interface struct {
+	value atomic.Value
+}
+
+// NewInterface creates and returns a concurrent-safe object for interface{} type,
+// with given initial value `value`.
+func NewInterface(value ...interface{}) *Interface {
+	t := &Interface{}
+	if len(value) > 0 && value[0] != nil {
+		t.value.Store(value[0])
+	}
+	return t
+}
+
+// Clone clones and returns a new concurrent-safe object for interface{} type.
+func (v *Interface) Clone() *Interface {
+	return NewInterface(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+// Note: The parameter `value` cannot be nil.
+func (v *Interface) Set(value interface{}) (old interface{}) {
+	old = v.Val()
+	v.value.Store(value)
+	return
+}
+
+// Val atomically loads and returns t.value.
+func (v *Interface) Val() interface{} {
+	return v.value.Load()
+}
+
+// String implements String interface for string printing.
+func (v *Interface) String() string {
+	return gconv.String(v.Val())
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Interface) MarshalJSON() ([]byte, error) {
+	return json.Marshal(v.Val())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Interface) UnmarshalJSON(b []byte) error {
+	var i interface{}
+	if err := json.UnmarshalUseNumber(b, &i); err != nil {
+		return err
+	}
+	v.Set(i)
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Interface) UnmarshalValue(value interface{}) error {
+	v.Set(value)
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Interface) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewInterface(deepcopy.Copy(v.Val()))
+}

+ 80 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_string.go

@@ -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 this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gtype
+
+import (
+	"bytes"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// String is a struct for concurrent-safe operation for type string.
+type String struct {
+	value atomic.Value
+}
+
+// NewString creates and returns a concurrent-safe object for string type,
+// with given initial value `value`.
+func NewString(value ...string) *String {
+	t := &String{}
+	if len(value) > 0 {
+		t.value.Store(value[0])
+	}
+	return t
+}
+
+// Clone clones and returns a new concurrent-safe object for string type.
+func (v *String) Clone() *String {
+	return NewString(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *String) Set(value string) (old string) {
+	old = v.Val()
+	v.value.Store(value)
+	return
+}
+
+// Val atomically loads and returns t.value.
+func (v *String) Val() string {
+	s := v.value.Load()
+	if s != nil {
+		return s.(string)
+	}
+	return ""
+}
+
+// String implements String interface for string printing.
+func (v *String) String() string {
+	return v.Val()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v String) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + v.Val() + `"`), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *String) UnmarshalJSON(b []byte) error {
+	v.Set(string(bytes.Trim(b, `"`)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *String) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.String(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *String) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewString(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Uint is a struct for concurrent-safe operation for type uint.
+type Uint struct {
+	value uint64
+}
+
+// NewUint creates and returns a concurrent-safe object for uint type,
+// with given initial value `value`.
+func NewUint(value ...uint) *Uint {
+	if len(value) > 0 {
+		return &Uint{
+			value: uint64(value[0]),
+		}
+	}
+	return &Uint{}
+}
+
+// Clone clones and returns a new concurrent-safe object for uint type.
+func (v *Uint) Clone() *Uint {
+	return NewUint(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Uint) Set(value uint) (old uint) {
+	return uint(atomic.SwapUint64(&v.value, uint64(value)))
+}
+
+// Val atomically loads and returns t.value.
+func (v *Uint) Val() uint {
+	return uint(atomic.LoadUint64(&v.value))
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Uint) Add(delta uint) (new uint) {
+	return uint(atomic.AddUint64(&v.value, uint64(delta)))
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Uint) Cas(old, new uint) (swapped bool) {
+	return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new))
+}
+
+// String implements String interface for string printing.
+func (v *Uint) String() string {
+	return strconv.FormatUint(uint64(v.Val()), 10)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Uint) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Uint) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Uint(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Uint) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Uint(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Uint) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewUint(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint32.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Uint32 is a struct for concurrent-safe operation for type uint32.
+type Uint32 struct {
+	value uint32
+}
+
+// NewUint32 creates and returns a concurrent-safe object for uint32 type,
+// with given initial value `value`.
+func NewUint32(value ...uint32) *Uint32 {
+	if len(value) > 0 {
+		return &Uint32{
+			value: value[0],
+		}
+	}
+	return &Uint32{}
+}
+
+// Clone clones and returns a new concurrent-safe object for uint32 type.
+func (v *Uint32) Clone() *Uint32 {
+	return NewUint32(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Uint32) Set(value uint32) (old uint32) {
+	return atomic.SwapUint32(&v.value, value)
+}
+
+// Val atomically loads and returns t.value.
+func (v *Uint32) Val() uint32 {
+	return atomic.LoadUint32(&v.value)
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Uint32) Add(delta uint32) (new uint32) {
+	return atomic.AddUint32(&v.value, delta)
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Uint32) Cas(old, new uint32) (swapped bool) {
+	return atomic.CompareAndSwapUint32(&v.value, old, new)
+}
+
+// String implements String interface for string printing.
+func (v *Uint32) String() string {
+	return strconv.FormatUint(uint64(v.Val()), 10)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Uint32) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Uint32) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Uint32(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Uint32) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Uint32(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Uint32) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewUint32(v.Val())
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/container/gtype/gtype_uint64.go

@@ -0,0 +1,85 @@
+// 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 gtype
+
+import (
+	"strconv"
+	"sync/atomic"
+
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Uint64 is a struct for concurrent-safe operation for type uint64.
+type Uint64 struct {
+	value uint64
+}
+
+// NewUint64 creates and returns a concurrent-safe object for uint64 type,
+// with given initial value `value`.
+func NewUint64(value ...uint64) *Uint64 {
+	if len(value) > 0 {
+		return &Uint64{
+			value: value[0],
+		}
+	}
+	return &Uint64{}
+}
+
+// Clone clones and returns a new concurrent-safe object for uint64 type.
+func (v *Uint64) Clone() *Uint64 {
+	return NewUint64(v.Val())
+}
+
+// Set atomically stores `value` into t.value and returns the previous value of t.value.
+func (v *Uint64) Set(value uint64) (old uint64) {
+	return atomic.SwapUint64(&v.value, value)
+}
+
+// Val atomically loads and returns t.value.
+func (v *Uint64) Val() uint64 {
+	return atomic.LoadUint64(&v.value)
+}
+
+// Add atomically adds `delta` to t.value and returns the new value.
+func (v *Uint64) Add(delta uint64) (new uint64) {
+	return atomic.AddUint64(&v.value, delta)
+}
+
+// Cas executes the compare-and-swap operation for value.
+func (v *Uint64) Cas(old, new uint64) (swapped bool) {
+	return atomic.CompareAndSwapUint64(&v.value, old, new)
+}
+
+// String implements String interface for string printing.
+func (v *Uint64) String() string {
+	return strconv.FormatUint(v.Val(), 10)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Uint64) MarshalJSON() ([]byte, error) {
+	return []byte(strconv.FormatUint(v.Val(), 10)), nil
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Uint64) UnmarshalJSON(b []byte) error {
+	v.Set(gconv.Uint64(string(b)))
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for `v`.
+func (v *Uint64) UnmarshalValue(value interface{}) error {
+	v.Set(gconv.Uint64(value))
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Uint64) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return NewUint64(v.Val())
+}

+ 205 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar.go

@@ -0,0 +1,205 @@
+// 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 gvar provides an universal variable type, like generics.
+package gvar
+
+import (
+	"time"
+
+	"github.com/gogf/gf/v2/container/gtype"
+	"github.com/gogf/gf/v2/internal/deepcopy"
+	"github.com/gogf/gf/v2/internal/json"
+	"github.com/gogf/gf/v2/os/gtime"
+	"github.com/gogf/gf/v2/util/gconv"
+	"github.com/gogf/gf/v2/util/gutil"
+)
+
+// Var is an universal variable type implementer.
+type Var struct {
+	value interface{} // Underlying value.
+	safe  bool        // Concurrent safe or not.
+}
+
+// New creates and returns a new Var with given `value`.
+// The optional parameter `safe` specifies whether Var is used in concurrent-safety,
+// which is false in default.
+func New(value interface{}, safe ...bool) *Var {
+	if len(safe) > 0 && safe[0] {
+		return &Var{
+			value: gtype.NewInterface(value),
+			safe:  true,
+		}
+	}
+	return &Var{
+		value: value,
+	}
+}
+
+// Copy does a deep copy of current Var and returns a pointer to this Var.
+func (v *Var) Copy() *Var {
+	return New(gutil.Copy(v.Val()), v.safe)
+}
+
+// Clone does a shallow copy of current Var and returns a pointer to this Var.
+func (v *Var) Clone() *Var {
+	return New(v.Val(), v.safe)
+}
+
+// Set sets `value` to `v`, and returns the old value.
+func (v *Var) Set(value interface{}) (old interface{}) {
+	if v.safe {
+		if t, ok := v.value.(*gtype.Interface); ok {
+			old = t.Set(value)
+			return
+		}
+	}
+	old = v.value
+	v.value = value
+	return
+}
+
+// Val returns the current value of `v`.
+func (v *Var) Val() interface{} {
+	if v == nil {
+		return nil
+	}
+	if v.safe {
+		if t, ok := v.value.(*gtype.Interface); ok {
+			return t.Val()
+		}
+	}
+	return v.value
+}
+
+// Interface is alias of Val.
+func (v *Var) Interface() interface{} {
+	return v.Val()
+}
+
+// Bytes converts and returns `v` as []byte.
+func (v *Var) Bytes() []byte {
+	return gconv.Bytes(v.Val())
+}
+
+// String converts and returns `v` as string.
+func (v *Var) String() string {
+	return gconv.String(v.Val())
+}
+
+// Bool converts and returns `v` as bool.
+func (v *Var) Bool() bool {
+	return gconv.Bool(v.Val())
+}
+
+// Int converts and returns `v` as int.
+func (v *Var) Int() int {
+	return gconv.Int(v.Val())
+}
+
+// Int8 converts and returns `v` as int8.
+func (v *Var) Int8() int8 {
+	return gconv.Int8(v.Val())
+}
+
+// Int16 converts and returns `v` as int16.
+func (v *Var) Int16() int16 {
+	return gconv.Int16(v.Val())
+}
+
+// Int32 converts and returns `v` as int32.
+func (v *Var) Int32() int32 {
+	return gconv.Int32(v.Val())
+}
+
+// Int64 converts and returns `v` as int64.
+func (v *Var) Int64() int64 {
+	return gconv.Int64(v.Val())
+}
+
+// Uint converts and returns `v` as uint.
+func (v *Var) Uint() uint {
+	return gconv.Uint(v.Val())
+}
+
+// Uint8 converts and returns `v` as uint8.
+func (v *Var) Uint8() uint8 {
+	return gconv.Uint8(v.Val())
+}
+
+// Uint16 converts and returns `v` as uint16.
+func (v *Var) Uint16() uint16 {
+	return gconv.Uint16(v.Val())
+}
+
+// Uint32 converts and returns `v` as uint32.
+func (v *Var) Uint32() uint32 {
+	return gconv.Uint32(v.Val())
+}
+
+// Uint64 converts and returns `v` as uint64.
+func (v *Var) Uint64() uint64 {
+	return gconv.Uint64(v.Val())
+}
+
+// Float32 converts and returns `v` as float32.
+func (v *Var) Float32() float32 {
+	return gconv.Float32(v.Val())
+}
+
+// Float64 converts and returns `v` as float64.
+func (v *Var) Float64() float64 {
+	return gconv.Float64(v.Val())
+}
+
+// Time converts and returns `v` as time.Time.
+// The parameter `format` specifies the format of the time string using gtime,
+// eg: Y-m-d H:i:s.
+func (v *Var) Time(format ...string) time.Time {
+	return gconv.Time(v.Val(), format...)
+}
+
+// Duration converts and returns `v` as time.Duration.
+// If value of `v` is string, then it uses time.ParseDuration for conversion.
+func (v *Var) Duration() time.Duration {
+	return gconv.Duration(v.Val())
+}
+
+// GTime converts and returns `v` as *gtime.Time.
+// The parameter `format` specifies the format of the time string using gtime,
+// eg: Y-m-d H:i:s.
+func (v *Var) GTime(format ...string) *gtime.Time {
+	return gconv.GTime(v.Val(), format...)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (v Var) MarshalJSON() ([]byte, error) {
+	return json.Marshal(v.Val())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (v *Var) UnmarshalJSON(b []byte) error {
+	var i interface{}
+	if err := json.UnmarshalUseNumber(b, &i); err != nil {
+		return err
+	}
+	v.Set(i)
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for Var.
+func (v *Var) UnmarshalValue(value interface{}) error {
+	v.Set(value)
+	return nil
+}
+
+// DeepCopy implements interface for deep copy of current type.
+func (v *Var) DeepCopy() interface{} {
+	if v == nil {
+		return nil
+	}
+	return New(deepcopy.Copy(v.Val()), v.safe)
+}

+ 51 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_is.go

@@ -0,0 +1,51 @@
+// 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 gvar
+
+import (
+	"github.com/gogf/gf/v2/internal/utils"
+)
+
+// IsNil checks whether `v` is nil.
+func (v *Var) IsNil() bool {
+	return utils.IsNil(v.Val())
+}
+
+// IsEmpty checks whether `v` is empty.
+func (v *Var) IsEmpty() bool {
+	return utils.IsEmpty(v.Val())
+}
+
+// IsInt checks whether `v` is type of int.
+func (v *Var) IsInt() bool {
+	return utils.IsInt(v.Val())
+}
+
+// IsUint checks whether `v` is type of uint.
+func (v *Var) IsUint() bool {
+	return utils.IsUint(v.Val())
+}
+
+// IsFloat checks whether `v` is type of float.
+func (v *Var) IsFloat() bool {
+	return utils.IsFloat(v.Val())
+}
+
+// IsSlice checks whether `v` is type of slice.
+func (v *Var) IsSlice() bool {
+	return utils.IsSlice(v.Val())
+}
+
+// IsMap checks whether `v` is type of map.
+func (v *Var) IsMap() bool {
+	return utils.IsMap(v.Val())
+}
+
+// IsStruct checks whether `v` is type of struct.
+func (v *Var) IsStruct() bool {
+	return utils.IsStruct(v.Val())
+}

+ 25 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_list.go

@@ -0,0 +1,25 @@
+// 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 gvar
+
+import (
+	"github.com/gogf/gf/v2/util/gutil"
+)
+
+// ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
+// Note that the parameter `list` should be type of slice which contains elements of map or struct,
+// or else it returns an empty slice.
+func (v *Var) ListItemValues(key interface{}) (values []interface{}) {
+	return gutil.ListItemValues(v.Val(), key)
+}
+
+// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
+// Note that the parameter `list` should be type of slice which contains elements of map or struct,
+// or else it returns an empty slice.
+func (v *Var) ListItemValuesUnique(key string) []interface{} {
+	return gutil.ListItemValuesUnique(v.Val(), key)
+}

+ 97 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_map.go

@@ -0,0 +1,97 @@
+// 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 gvar
+
+import "github.com/gogf/gf/v2/util/gconv"
+
+// MapOption specifies the option for map converting.
+type MapOption = gconv.MapOption
+
+// Map converts and returns `v` as map[string]interface{}.
+func (v *Var) Map(option ...MapOption) map[string]interface{} {
+	return gconv.Map(v.Val(), option...)
+}
+
+// MapStrAny is like function Map, but implements the interface of MapStrAny.
+func (v *Var) MapStrAny(option ...MapOption) map[string]interface{} {
+	return v.Map(option...)
+}
+
+// MapStrStr converts and returns `v` as map[string]string.
+func (v *Var) MapStrStr(option ...MapOption) map[string]string {
+	return gconv.MapStrStr(v.Val(), option...)
+}
+
+// MapStrVar converts and returns `v` as map[string]Var.
+func (v *Var) MapStrVar(option ...MapOption) map[string]*Var {
+	m := v.Map(option...)
+	if len(m) > 0 {
+		vMap := make(map[string]*Var, len(m))
+		for k, v := range m {
+			vMap[k] = New(v)
+		}
+		return vMap
+	}
+	return nil
+}
+
+// MapDeep converts and returns `v` as map[string]interface{} recursively.
+// Deprecated: used Map instead.
+func (v *Var) MapDeep(tags ...string) map[string]interface{} {
+	return gconv.MapDeep(v.Val(), tags...)
+}
+
+// MapStrStrDeep converts and returns `v` as map[string]string recursively.
+// Deprecated: used MapStrStr instead.
+func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
+	return gconv.MapStrStrDeep(v.Val(), tags...)
+}
+
+// MapStrVarDeep converts and returns `v` as map[string]*Var recursively.
+// Deprecated: used MapStrVar instead.
+func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
+	m := v.MapDeep(tags...)
+	if len(m) > 0 {
+		vMap := make(map[string]*Var, len(m))
+		for k, v := range m {
+			vMap[k] = New(v)
+		}
+		return vMap
+	}
+	return nil
+}
+
+// Maps converts and returns `v` as map[string]string.
+// See gconv.Maps.
+func (v *Var) Maps(option ...MapOption) []map[string]interface{} {
+	return gconv.Maps(v.Val(), option...)
+}
+
+// MapsDeep converts `value` to []map[string]interface{} recursively.
+// Deprecated: used Maps instead.
+func (v *Var) MapsDeep(tags ...string) []map[string]interface{} {
+	return gconv.MapsDeep(v.Val(), tags...)
+}
+
+// MapToMap converts any map type variable `params` to another map type variable `pointer`.
+// See gconv.MapToMap.
+func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) {
+	return gconv.MapToMap(v.Val(), pointer, mapping...)
+}
+
+// MapToMaps converts any map type variable `params` to another map type variable `pointer`.
+// See gconv.MapToMaps.
+func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
+	return gconv.MapToMaps(v.Val(), pointer, mapping...)
+}
+
+// MapToMapsDeep converts any map type variable `params` to another map type variable
+// `pointer` recursively.
+// See gconv.MapToMapsDeep.
+func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
+	return gconv.MapToMaps(v.Val(), pointer, mapping...)
+}

+ 19 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_scan.go

@@ -0,0 +1,19 @@
+// 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 gvar
+
+import (
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
+// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
+//
+// See gconv.Scan.
+func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error {
+	return gconv.Scan(v.Val(), pointer, mapping...)
+}

+ 77 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_slice.go

@@ -0,0 +1,77 @@
+// 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 gvar
+
+import "github.com/gogf/gf/v2/util/gconv"
+
+// Ints converts and returns `v` as []int.
+func (v *Var) Ints() []int {
+	return gconv.Ints(v.Val())
+}
+
+// Int64s converts and returns `v` as []int64.
+func (v *Var) Int64s() []int64 {
+	return gconv.Int64s(v.Val())
+}
+
+// Uints converts and returns `v` as []uint.
+func (v *Var) Uints() []uint {
+	return gconv.Uints(v.Val())
+}
+
+// Uint64s converts and returns `v` as []uint64.
+func (v *Var) Uint64s() []uint64 {
+	return gconv.Uint64s(v.Val())
+}
+
+// Floats is alias of Float64s.
+func (v *Var) Floats() []float64 {
+	return gconv.Floats(v.Val())
+}
+
+// Float32s converts and returns `v` as []float32.
+func (v *Var) Float32s() []float32 {
+	return gconv.Float32s(v.Val())
+}
+
+// Float64s converts and returns `v` as []float64.
+func (v *Var) Float64s() []float64 {
+	return gconv.Float64s(v.Val())
+}
+
+// Strings converts and returns `v` as []string.
+func (v *Var) Strings() []string {
+	return gconv.Strings(v.Val())
+}
+
+// Interfaces converts and returns `v` as []interfaces{}.
+func (v *Var) Interfaces() []interface{} {
+	return gconv.Interfaces(v.Val())
+}
+
+// Slice is alias of Interfaces.
+func (v *Var) Slice() []interface{} {
+	return v.Interfaces()
+}
+
+// Array is alias of Interfaces.
+func (v *Var) Array() []interface{} {
+	return v.Interfaces()
+}
+
+// Vars converts and returns `v` as []Var.
+func (v *Var) Vars() []*Var {
+	array := gconv.Interfaces(v.Val())
+	if len(array) == 0 {
+		return nil
+	}
+	vars := make([]*Var, len(array))
+	for k, v := range array {
+		vars[k] = New(v)
+	}
+	return vars
+}

+ 23 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_struct.go

@@ -0,0 +1,23 @@
+// 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 gvar
+
+import (
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Struct maps value of `v` to `pointer`.
+// The parameter `pointer` should be a pointer to a struct instance.
+// The parameter `mapping` is used to specify the key-to-attribute mapping rules.
+func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
+	return gconv.Struct(v.Val(), pointer, mapping...)
+}
+
+// Structs converts and returns `v` as given struct slice.
+func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error {
+	return gconv.Structs(v.Val(), pointer, mapping...)
+}

+ 131 - 0
vendor/github.com/gogf/gf/v2/container/gvar/gvar_vars.go

@@ -0,0 +1,131 @@
+// 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 gvar
+
+import (
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Vars is a slice of *Var.
+type Vars []*Var
+
+// Strings converts and returns `vs` as []string.
+func (vs Vars) Strings() (s []string) {
+	for _, v := range vs {
+		s = append(s, v.String())
+	}
+	return s
+}
+
+// Interfaces converts and returns `vs` as []interface{}.
+func (vs Vars) Interfaces() (s []interface{}) {
+	for _, v := range vs {
+		s = append(s, v.Val())
+	}
+	return s
+}
+
+// Float32s converts and returns `vs` as []float32.
+func (vs Vars) Float32s() (s []float32) {
+	for _, v := range vs {
+		s = append(s, v.Float32())
+	}
+	return s
+}
+
+// Float64s converts and returns `vs` as []float64.
+func (vs Vars) Float64s() (s []float64) {
+	for _, v := range vs {
+		s = append(s, v.Float64())
+	}
+	return s
+}
+
+// Ints converts and returns `vs` as []Int.
+func (vs Vars) Ints() (s []int) {
+	for _, v := range vs {
+		s = append(s, v.Int())
+	}
+	return s
+}
+
+// Int8s converts and returns `vs` as []int8.
+func (vs Vars) Int8s() (s []int8) {
+	for _, v := range vs {
+		s = append(s, v.Int8())
+	}
+	return s
+}
+
+// Int16s converts and returns `vs` as []int16.
+func (vs Vars) Int16s() (s []int16) {
+	for _, v := range vs {
+		s = append(s, v.Int16())
+	}
+	return s
+}
+
+// Int32s converts and returns `vs` as []int32.
+func (vs Vars) Int32s() (s []int32) {
+	for _, v := range vs {
+		s = append(s, v.Int32())
+	}
+	return s
+}
+
+// Int64s converts and returns `vs` as []int64.
+func (vs Vars) Int64s() (s []int64) {
+	for _, v := range vs {
+		s = append(s, v.Int64())
+	}
+	return s
+}
+
+// Uints converts and returns `vs` as []uint.
+func (vs Vars) Uints() (s []uint) {
+	for _, v := range vs {
+		s = append(s, v.Uint())
+	}
+	return s
+}
+
+// Uint8s converts and returns `vs` as []uint8.
+func (vs Vars) Uint8s() (s []uint8) {
+	for _, v := range vs {
+		s = append(s, v.Uint8())
+	}
+	return s
+}
+
+// Uint16s converts and returns `vs` as []uint16.
+func (vs Vars) Uint16s() (s []uint16) {
+	for _, v := range vs {
+		s = append(s, v.Uint16())
+	}
+	return s
+}
+
+// Uint32s converts and returns `vs` as []uint32.
+func (vs Vars) Uint32s() (s []uint32) {
+	for _, v := range vs {
+		s = append(s, v.Uint32())
+	}
+	return s
+}
+
+// Uint64s converts and returns `vs` as []uint64.
+func (vs Vars) Uint64s() (s []uint64) {
+	for _, v := range vs {
+		s = append(s, v.Uint64())
+	}
+	return s
+}
+
+// Scan converts `vs` to []struct/[]*struct.
+func (vs Vars) Scan(pointer interface{}, mapping ...map[string]string) error {
+	return gconv.Structs(vs.Interfaces(), pointer, mapping...)
+}

+ 78 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis.go

@@ -0,0 +1,78 @@
+// 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 gredis provides convenient client for redis server.
+//
+// Redis Client.
+//
+// Redis Commands Official: https://redis.io/commands
+//
+// Redis Chinese Documentation: http://redisdoc.com/
+package gredis
+
+import (
+	"github.com/gogf/gf/v2/errors/gcode"
+	"github.com/gogf/gf/v2/errors/gerror"
+)
+
+// AdapterFunc is the function creating redis adapter.
+type AdapterFunc func(config *Config) Adapter
+
+var (
+	// defaultAdapterFunc is the default adapter function creating redis adapter.
+	defaultAdapterFunc AdapterFunc = func(config *Config) Adapter {
+		return nil
+	}
+)
+
+// New creates and returns a redis client.
+// It creates a default redis adapter of go-redis.
+func New(config ...*Config) (*Redis, error) {
+	var (
+		usedConfig  *Config
+		usedAdapter Adapter
+	)
+	if len(config) > 0 && config[0] != nil {
+		// Redis client with go redis implements adapter from given configuration.
+		usedConfig = config[0]
+		usedAdapter = defaultAdapterFunc(config[0])
+	} else if configFromGlobal, ok := GetConfig(); ok {
+		// Redis client with go redis implements adapter from package configuration.
+		usedConfig = configFromGlobal
+		usedAdapter = defaultAdapterFunc(configFromGlobal)
+	}
+	if usedConfig == nil {
+		return nil, gerror.NewCode(
+			gcode.CodeInvalidConfiguration,
+			`no configuration found for creating Redis client`,
+		)
+	}
+	if usedAdapter == nil {
+		return nil, gerror.NewCode(
+			gcode.CodeNecessaryPackageNotImport,
+			errorNilAdapter,
+		)
+	}
+	redis := &Redis{
+		config:       usedConfig,
+		localAdapter: usedAdapter,
+	}
+	return redis.initGroup(), nil
+}
+
+// NewWithAdapter creates and returns a redis client with given adapter.
+func NewWithAdapter(adapter Adapter) (*Redis, error) {
+	if adapter == nil {
+		return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `adapter cannot be nil`)
+	}
+	redis := &Redis{localAdapter: adapter}
+	return redis.initGroup(), nil
+}
+
+// RegisterAdapterFunc registers default function creating redis adapter.
+func RegisterAdapterFunc(adapterFunc AdapterFunc) {
+	defaultAdapterFunc = adapterFunc
+}

+ 78 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_adapter.go

@@ -0,0 +1,78 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// Adapter is an interface for universal redis operations.
+type Adapter interface {
+	AdapterGroup
+
+	// Do send a command to the server and returns the received reply.
+	// It uses json.Marshal for struct/slice/map type values before committing them to redis.
+	Do(ctx context.Context, command string, args ...interface{}) (*gvar.Var, error)
+
+	// Conn retrieves and returns a connection object for continuous operations.
+	// Note that you should call Close function manually if you do not use this connection any further.
+	Conn(ctx context.Context) (conn Conn, err error)
+
+	// Close closes current redis client, closes its connection pool and releases all its related resources.
+	Close(ctx context.Context) (err error)
+}
+
+// Conn is an interface of a connection from universal redis client.
+type Conn interface {
+	ConnCommand
+
+	// Do send a command to the server and returns the received reply.
+	// It uses json.Marshal for struct/slice/map type values before committing them to redis.
+	Do(ctx context.Context, command string, args ...interface{}) (result *gvar.Var, err error)
+
+	// Close puts the connection back to connection pool.
+	Close(ctx context.Context) (err error)
+}
+
+// AdapterGroup is an interface managing group operations for redis.
+type AdapterGroup interface {
+	GroupGeneric() IGroupGeneric
+	GroupHash() IGroupHash
+	GroupList() IGroupList
+	GroupPubSub() IGroupPubSub
+	GroupScript() IGroupScript
+	GroupSet() IGroupSet
+	GroupSortedSet() IGroupSortedSet
+	GroupString() IGroupString
+}
+
+// ConnCommand is an interface managing some operations bound to certain connection.
+type ConnCommand interface {
+	// Subscribe subscribes the client to the specified channels.
+	// https://redis.io/commands/subscribe/
+	Subscribe(ctx context.Context, channel string, channels ...string) ([]*Subscription, error)
+
+	// PSubscribe subscribes the client to the given patterns.
+	//
+	// Supported glob-style patterns:
+	// - h?llo subscribes to hello, hallo and hxllo
+	// - h*llo subscribes to hllo and heeeello
+	// - h[ae]llo subscribes to hello and hallo, but not hillo
+	//
+	// Use \ to escape special characters if you want to match them verbatim.
+	//
+	// https://redis.io/commands/psubscribe/
+	PSubscribe(ctx context.Context, pattern string, patterns ...string) ([]*Subscription, error)
+
+	// ReceiveMessage receives a single message of subscription from the Redis server.
+	ReceiveMessage(ctx context.Context) (*Message, error)
+
+	// Receive receives a single reply as gvar.Var from the Redis server.
+	Receive(ctx context.Context) (result *gvar.Var, err error)
+}

+ 140 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_config.go

@@ -0,0 +1,140 @@
+// 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 gredis
+
+import (
+	"context"
+	"crypto/tls"
+	"time"
+
+	"github.com/gogf/gf/v2/container/gmap"
+	"github.com/gogf/gf/v2/errors/gcode"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/internal/intlog"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+// Config is redis configuration.
+type Config struct {
+	// Address It supports single and cluster redis server. Multiple addresses joined with char ','. Eg: 192.168.1.1:6379, 192.168.1.2:6379.
+	Address         string        `json:"address"`
+	Db              int           `json:"db"`              // Redis db.
+	User            string        `json:"user"`            // Username for AUTH.
+	Pass            string        `json:"pass"`            // Password for AUTH.
+	MinIdle         int           `json:"minIdle"`         // Minimum number of connections allowed to be idle (default is 0)
+	MaxIdle         int           `json:"maxIdle"`         // Maximum number of connections allowed to be idle (default is 10)
+	MaxActive       int           `json:"maxActive"`       // Maximum number of connections limit (default is 0 means no limit).
+	MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
+	IdleTimeout     time.Duration `json:"idleTimeout"`     // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
+	WaitTimeout     time.Duration `json:"waitTimeout"`     // Timed out duration waiting to get a connection from the connection pool.
+	DialTimeout     time.Duration `json:"dialTimeout"`     // Dial connection timeout for TCP.
+	ReadTimeout     time.Duration `json:"readTimeout"`     // Read timeout for TCP. DO NOT set it if not necessary.
+	WriteTimeout    time.Duration `json:"writeTimeout"`    // Write timeout for TCP.
+	MasterName      string        `json:"masterName"`      // Used in Redis Sentinel mode.
+	TLS             bool          `json:"tls"`             // Specifies whether TLS should be used when connecting to the server.
+	TLSSkipVerify   bool          `json:"tlsSkipVerify"`   // Disables server name verification when connecting over TLS.
+	TLSConfig       *tls.Config   `json:"-"`               // TLS Config to use. When set TLS will be negotiated.
+	SlaveOnly       bool          `json:"slaveOnly"`       // Route all commands to slave read-only nodes.
+	Cluster         bool          `json:"cluster"`         // Specifies whether cluster mode be used.
+	Protocol        int           `json:"protocol"`        // Specifies the RESP version (Protocol 2 or 3.)
+}
+
+const (
+	DefaultGroupName = "default" // Default configuration group name.
+)
+
+var (
+	// Configuration groups.
+	localConfigMap = gmap.NewStrAnyMap(true)
+)
+
+// SetConfig sets the global configuration for specified group.
+// If `name` is not passed, it sets configuration for the default group name.
+func SetConfig(config *Config, name ...string) {
+	group := DefaultGroupName
+	if len(name) > 0 {
+		group = name[0]
+	}
+	localConfigMap.Set(group, config)
+
+	intlog.Printf(context.TODO(), `SetConfig for group "%s": %+v`, group, config)
+}
+
+// SetConfigByMap sets the global configuration for specified group with map.
+// If `name` is not passed, it sets configuration for the default group name.
+func SetConfigByMap(m map[string]interface{}, name ...string) error {
+	group := DefaultGroupName
+	if len(name) > 0 {
+		group = name[0]
+	}
+	config, err := ConfigFromMap(m)
+	if err != nil {
+		return err
+	}
+	localConfigMap.Set(group, config)
+	return nil
+}
+
+// ConfigFromMap parses and returns config from given map.
+func ConfigFromMap(m map[string]interface{}) (config *Config, err error) {
+	config = &Config{}
+	if err = gconv.Scan(m, config); err != nil {
+		err = gerror.NewCodef(gcode.CodeInvalidConfiguration, `invalid redis configuration: %#v`, m)
+	}
+	if config.DialTimeout < time.Second {
+		config.DialTimeout = config.DialTimeout * time.Second
+	}
+	if config.WaitTimeout < time.Second {
+		config.WaitTimeout = config.WaitTimeout * time.Second
+	}
+	if config.WriteTimeout < time.Second {
+		config.WriteTimeout = config.WriteTimeout * time.Second
+	}
+	if config.ReadTimeout < time.Second {
+		config.ReadTimeout = config.ReadTimeout * time.Second
+	}
+	if config.IdleTimeout < time.Second {
+		config.IdleTimeout = config.IdleTimeout * time.Second
+	}
+	if config.MaxConnLifetime < time.Second {
+		config.MaxConnLifetime = config.MaxConnLifetime * time.Second
+	}
+	if config.Protocol != 2 && config.Protocol != 3 {
+		config.Protocol = 3
+	}
+	return
+}
+
+// GetConfig returns the global configuration with specified group name.
+// If `name` is not passed, it returns configuration of the default group name.
+func GetConfig(name ...string) (config *Config, ok bool) {
+	group := DefaultGroupName
+	if len(name) > 0 {
+		group = name[0]
+	}
+	if v := localConfigMap.Get(group); v != nil {
+		return v.(*Config), true
+	}
+	return &Config{}, false
+}
+
+// RemoveConfig removes the global configuration with specified group.
+// If `name` is not passed, it removes configuration of the default group name.
+func RemoveConfig(name ...string) {
+	group := DefaultGroupName
+	if len(name) > 0 {
+		group = name[0]
+	}
+	localConfigMap.Remove(group)
+
+	intlog.Printf(context.TODO(), `RemoveConfig: %s`, group)
+}
+
+// ClearConfig removes all configurations of redis.
+func ClearConfig() {
+	localConfigMap.Clear()
+}

+ 44 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_instance.go

@@ -0,0 +1,44 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gmap"
+	"github.com/gogf/gf/v2/internal/intlog"
+)
+
+var (
+	// localInstances for instance management of redis client.
+	localInstances = gmap.NewStrAnyMap(true)
+)
+
+// Instance returns an instance of redis client with specified group.
+// The `name` param is unnecessary, if `name` is not passed,
+// it returns a redis instance with default configuration group.
+func Instance(name ...string) *Redis {
+	group := DefaultGroupName
+	if len(name) > 0 && name[0] != "" {
+		group = name[0]
+	}
+	v := localInstances.GetOrSetFuncLock(group, func() interface{} {
+		if config, ok := GetConfig(group); ok {
+			r, err := New(config)
+			if err != nil {
+				intlog.Errorf(context.TODO(), `%+v`, err)
+				return nil
+			}
+			return r
+		}
+		return nil
+	})
+	if v != nil {
+		return v.(*Redis)
+	}
+	return nil
+}

+ 137 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis.go

@@ -0,0 +1,137 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/errors/gcode"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/text/gstr"
+)
+
+// Redis client.
+type Redis struct {
+	config *Config
+	localAdapter
+	localGroup
+}
+
+type (
+	localGroup struct {
+		localGroupGeneric
+		localGroupHash
+		localGroupList
+		localGroupPubSub
+		localGroupScript
+		localGroupSet
+		localGroupSortedSet
+		localGroupString
+	}
+	localAdapter        = Adapter
+	localGroupGeneric   = IGroupGeneric
+	localGroupHash      = IGroupHash
+	localGroupList      = IGroupList
+	localGroupPubSub    = IGroupPubSub
+	localGroupScript    = IGroupScript
+	localGroupSet       = IGroupSet
+	localGroupSortedSet = IGroupSortedSet
+	localGroupString    = IGroupString
+)
+
+const (
+	errorNilRedis = `the Redis object is nil`
+)
+
+var (
+	errorNilAdapter = gstr.Trim(gstr.Replace(`
+redis adapter is not set, missing configuration or adapter register? 
+possible reference: https://github.com/gogf/gf/tree/master/contrib/nosql/redis
+`, "\n", ""))
+)
+
+// initGroup initializes the group object of redis.
+func (r *Redis) initGroup() *Redis {
+	r.localGroup = localGroup{
+		localGroupGeneric:   r.localAdapter.GroupGeneric(),
+		localGroupHash:      r.localAdapter.GroupHash(),
+		localGroupList:      r.localAdapter.GroupList(),
+		localGroupPubSub:    r.localAdapter.GroupPubSub(),
+		localGroupScript:    r.localAdapter.GroupScript(),
+		localGroupSet:       r.localAdapter.GroupSet(),
+		localGroupSortedSet: r.localAdapter.GroupSortedSet(),
+		localGroupString:    r.localAdapter.GroupString(),
+	}
+	return r
+}
+
+// SetAdapter changes the underlying adapter with custom adapter for current redis client.
+func (r *Redis) SetAdapter(adapter Adapter) {
+	if r == nil {
+		panic(gerror.NewCode(gcode.CodeInvalidParameter, errorNilRedis))
+	}
+	r.localAdapter = adapter
+}
+
+// GetAdapter returns the adapter that is set in current redis client.
+func (r *Redis) GetAdapter() Adapter {
+	if r == nil {
+		return nil
+	}
+	return r.localAdapter
+}
+
+// Conn retrieves and returns a connection object for continuous operations.
+// Note that you should call Close function manually if you do not use this connection any further.
+func (r *Redis) Conn(ctx context.Context) (Conn, error) {
+	if r == nil {
+		return nil, gerror.NewCode(gcode.CodeInvalidParameter, errorNilRedis)
+	}
+	if r.localAdapter == nil {
+		return nil, gerror.NewCode(gcode.CodeNecessaryPackageNotImport, errorNilAdapter)
+	}
+	return r.localAdapter.Conn(ctx)
+}
+
+// Do send a command to the server and returns the received reply.
+// It uses json.Marshal for struct/slice/map type values before committing them to redis.
+func (r *Redis) Do(ctx context.Context, command string, args ...interface{}) (*gvar.Var, error) {
+	if r == nil {
+		return nil, gerror.NewCode(gcode.CodeInvalidParameter, errorNilRedis)
+	}
+	if r.localAdapter == nil {
+		return nil, gerror.NewCodef(gcode.CodeMissingConfiguration, errorNilAdapter)
+	}
+	return r.localAdapter.Do(ctx, command, args...)
+}
+
+// MustConn performs as function Conn, but it panics if any error occurs internally.
+func (r *Redis) MustConn(ctx context.Context) Conn {
+	c, err := r.Conn(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// MustDo performs as function Do, but it panics if any error occurs internally.
+func (r *Redis) MustDo(ctx context.Context, command string, args ...interface{}) *gvar.Var {
+	v, err := r.Do(ctx, command, args...)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Close closes current redis client, closes its connection pool and releases all its related resources.
+func (r *Redis) Close(ctx context.Context) error {
+	if r == nil || r.localAdapter == nil {
+		return nil
+	}
+	return r.localAdapter.Close(ctx)
+}

+ 62 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_generic.go

@@ -0,0 +1,62 @@
+// 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 gredis
+
+import (
+	"context"
+	"time"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupGeneric manages generic redis operations.
+// Implements see redis.GroupGeneric.
+type IGroupGeneric interface {
+	Copy(ctx context.Context, source, destination string, option ...CopyOption) (int64, error)
+	Exists(ctx context.Context, keys ...string) (int64, error)
+	Type(ctx context.Context, key string) (string, error)
+	Unlink(ctx context.Context, keys ...string) (int64, error)
+	Rename(ctx context.Context, key, newKey string) error
+	RenameNX(ctx context.Context, key, newKey string) (int64, error)
+	Move(ctx context.Context, key string, db int) (int64, error)
+	Del(ctx context.Context, keys ...string) (int64, error)
+	RandomKey(ctx context.Context) (string, error)
+	DBSize(ctx context.Context) (int64, error)
+	Keys(ctx context.Context, pattern string) ([]string, error)
+	FlushDB(ctx context.Context, option ...FlushOp) error
+	FlushAll(ctx context.Context, option ...FlushOp) error
+	Expire(ctx context.Context, key string, seconds int64, option ...ExpireOption) (int64, error)
+	ExpireAt(ctx context.Context, key string, time time.Time, option ...ExpireOption) (int64, error)
+	ExpireTime(ctx context.Context, key string) (*gvar.Var, error)
+	TTL(ctx context.Context, key string) (int64, error)
+	Persist(ctx context.Context, key string) (int64, error)
+	PExpire(ctx context.Context, key string, milliseconds int64, option ...ExpireOption) (int64, error)
+	PExpireAt(ctx context.Context, key string, time time.Time, option ...ExpireOption) (int64, error)
+	PExpireTime(ctx context.Context, key string) (*gvar.Var, error)
+	PTTL(ctx context.Context, key string) (int64, error)
+}
+
+// CopyOption provides options for function Copy.
+type CopyOption struct {
+	DB      int  // DB option allows specifying an alternative logical database index for the destination key.
+	REPLACE bool // REPLACE option removes the destination key before copying the value to it.
+}
+
+type FlushOp string
+
+const (
+	FlushAsync FlushOp = "ASYNC" // ASYNC: flushes the databases asynchronously
+	FlushSync  FlushOp = "SYNC"  // SYNC: flushes the databases synchronously
+)
+
+// ExpireOption provides options for function Expire.
+type ExpireOption struct {
+	NX bool // NX -- Set expiry only when the key has no expiry
+	XX bool // XX -- Set expiry only when the key has an existing expiry
+	GT bool // GT -- Set expiry only when the new expiry is greater than current one
+	LT bool // LT -- Set expiry only when the new expiry is less than current one
+}

+ 32 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_hash.go

@@ -0,0 +1,32 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupHash manages redis hash operations.
+// Implements see redis.GroupHash.
+type IGroupHash interface {
+	HSet(ctx context.Context, key string, fields map[string]interface{}) (int64, error)
+	HSetNX(ctx context.Context, key, field string, value interface{}) (int64, error)
+	HGet(ctx context.Context, key, field string) (*gvar.Var, error)
+	HStrLen(ctx context.Context, key, field string) (int64, error)
+	HExists(ctx context.Context, key, field string) (int64, error)
+	HDel(ctx context.Context, key string, fields ...string) (int64, error)
+	HLen(ctx context.Context, key string) (int64, error)
+	HIncrBy(ctx context.Context, key, field string, increment int64) (int64, error)
+	HIncrByFloat(ctx context.Context, key, field string, increment float64) (float64, error)
+	HMSet(ctx context.Context, key string, fields map[string]interface{}) error
+	HMGet(ctx context.Context, key string, fields ...string) (gvar.Vars, error)
+	HKeys(ctx context.Context, key string) ([]string, error)
+	HVals(ctx context.Context, key string) (gvar.Vars, error)
+	HGetAll(ctx context.Context, key string) (*gvar.Var, error)
+}

+ 43 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_list.go

@@ -0,0 +1,43 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupList manages redis list operations.
+// Implements see redis.GroupList.
+type IGroupList interface {
+	LPush(ctx context.Context, key string, values ...interface{}) (int64, error)
+	LPushX(ctx context.Context, key string, element interface{}, elements ...interface{}) (int64, error)
+	RPush(ctx context.Context, key string, values ...interface{}) (int64, error)
+	RPushX(ctx context.Context, key string, value interface{}) (int64, error)
+	LPop(ctx context.Context, key string, count ...int) (*gvar.Var, error)
+	RPop(ctx context.Context, key string, count ...int) (*gvar.Var, error)
+	LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error)
+	LLen(ctx context.Context, key string) (int64, error)
+	LIndex(ctx context.Context, key string, index int64) (*gvar.Var, error)
+	LInsert(ctx context.Context, key string, op LInsertOp, pivot, value interface{}) (int64, error)
+	LSet(ctx context.Context, key string, index int64, value interface{}) (*gvar.Var, error)
+	LRange(ctx context.Context, key string, start, stop int64) (gvar.Vars, error)
+	LTrim(ctx context.Context, key string, start, stop int64) error
+	BLPop(ctx context.Context, timeout int64, keys ...string) (gvar.Vars, error)
+	BRPop(ctx context.Context, timeout int64, keys ...string) (gvar.Vars, error)
+	RPopLPush(ctx context.Context, source, destination string) (*gvar.Var, error)
+	BRPopLPush(ctx context.Context, source, destination string, timeout int64) (*gvar.Var, error)
+}
+
+// LInsertOp defines the operation name for function LInsert.
+type LInsertOp string
+
+const (
+	LInsertBefore LInsertOp = "BEFORE"
+	LInsertAfter  LInsertOp = "AFTER"
+)

+ 40 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_pubsub.go

@@ -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 gredis
+
+import (
+	"context"
+	"fmt"
+)
+
+// IGroupPubSub manages redis pub/sub operations.
+// Implements see redis.GroupPubSub.
+type IGroupPubSub interface {
+	Publish(ctx context.Context, channel string, message interface{}) (int64, error)
+	Subscribe(ctx context.Context, channel string, channels ...string) (Conn, []*Subscription, error)
+	PSubscribe(ctx context.Context, pattern string, patterns ...string) (Conn, []*Subscription, error)
+}
+
+// Message received as result of a PUBLISH command issued by another client.
+type Message struct {
+	Channel      string
+	Pattern      string
+	Payload      string
+	PayloadSlice []string
+}
+
+// Subscription received after a successful subscription to channel.
+type Subscription struct {
+	Kind    string // Can be "subscribe", "unsubscribe", "psubscribe" or "punsubscribe".
+	Channel string // Channel name we have subscribed to.
+	Count   int    // Number of channels we are currently subscribed to.
+}
+
+// String converts current object to a readable string.
+func (m *Subscription) String() string {
+	return fmt.Sprintf("%s: %s", m.Kind, m.Channel)
+}

+ 30 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_script.go

@@ -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 this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupScript manages redis script operations.
+// Implements see redis.GroupScript.
+type IGroupScript interface {
+	Eval(ctx context.Context, script string, numKeys int64, keys []string, args []interface{}) (*gvar.Var, error)
+	EvalSha(ctx context.Context, sha1 string, numKeys int64, keys []string, args []interface{}) (*gvar.Var, error)
+	ScriptLoad(ctx context.Context, script string) (string, error)
+	ScriptExists(ctx context.Context, sha1 string, sha1s ...string) (map[string]bool, error)
+	ScriptFlush(ctx context.Context, option ...ScriptFlushOption) error
+	ScriptKill(ctx context.Context) error
+}
+
+// ScriptFlushOption provides options for function ScriptFlush.
+type ScriptFlushOption struct {
+	SYNC  bool // SYNC  flushes the cache synchronously.
+	ASYNC bool // ASYNC flushes the cache asynchronously.
+}

+ 33 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_set.go

@@ -0,0 +1,33 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupSet manages redis set operations.
+// Implements see redis.GroupSet.
+type IGroupSet interface {
+	SAdd(ctx context.Context, key string, member interface{}, members ...interface{}) (int64, error)
+	SIsMember(ctx context.Context, key string, member interface{}) (int64, error)
+	SPop(ctx context.Context, key string, count ...int) (*gvar.Var, error)
+	SRandMember(ctx context.Context, key string, count ...int) (*gvar.Var, error)
+	SRem(ctx context.Context, key string, member interface{}, members ...interface{}) (int64, error)
+	SMove(ctx context.Context, source, destination string, member interface{}) (int64, error)
+	SCard(ctx context.Context, key string) (int64, error)
+	SMembers(ctx context.Context, key string) (gvar.Vars, error)
+	SMIsMember(ctx context.Context, key, member interface{}, members ...interface{}) ([]int, error)
+	SInter(ctx context.Context, key string, keys ...string) (gvar.Vars, error)
+	SInterStore(ctx context.Context, destination string, key string, keys ...string) (int64, error)
+	SUnion(ctx context.Context, key string, keys ...string) (gvar.Vars, error)
+	SUnionStore(ctx context.Context, destination, key string, keys ...string) (int64, error)
+	SDiff(ctx context.Context, key string, keys ...string) (gvar.Vars, error)
+	SDiffStore(ctx context.Context, destination string, key string, keys ...string) (int64, error)
+}

+ 85 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_sorted_set.go

@@ -0,0 +1,85 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupSortedSet manages redis sorted set operations.
+// Implements see redis.GroupSortedSet.
+type IGroupSortedSet interface {
+	ZAdd(ctx context.Context, key string, option *ZAddOption, member ZAddMember, members ...ZAddMember) (*gvar.Var, error)
+	ZScore(ctx context.Context, key string, member interface{}) (float64, error)
+	ZIncrBy(ctx context.Context, key string, increment float64, member interface{}) (float64, error)
+	ZCard(ctx context.Context, key string) (int64, error)
+	ZCount(ctx context.Context, key string, min, max string) (int64, error)
+	ZRange(ctx context.Context, key string, start, stop int64, option ...ZRangeOption) (gvar.Vars, error)
+	ZRevRange(ctx context.Context, key string, start, stop int64, option ...ZRevRangeOption) (*gvar.Var, error)
+	ZRank(ctx context.Context, key string, member interface{}) (int64, error)
+	ZRevRank(ctx context.Context, key string, member interface{}) (int64, error)
+	ZRem(ctx context.Context, key string, member interface{}, members ...interface{}) (int64, error)
+	ZRemRangeByRank(ctx context.Context, key string, start, stop int64) (int64, error)
+	ZRemRangeByScore(ctx context.Context, key string, min, max string) (int64, error)
+	ZRemRangeByLex(ctx context.Context, key string, min, max string) (int64, error)
+	ZLexCount(ctx context.Context, key, min, max string) (int64, error)
+}
+
+// ZAddOption provides options for function ZAdd.
+type ZAddOption struct {
+	XX bool // Only update elements that already exist. Don't add new elements.
+	NX bool // Only add new elements. Don't update already existing elements.
+	// Only update existing elements if the new score is less than the current score.
+	// This flag doesn't prevent adding new elements.
+	LT bool
+
+	// Only update existing elements if the new score is greater than the current score.
+	// This flag doesn't prevent adding new elements.
+	GT bool
+
+	// Modify the return value from the number of new elements added, to the total number of elements changed (CH is an abbreviation of changed).
+	// Changed elements are new elements added and elements already existing for which the score was updated.
+	// So elements specified in the command line having the same score as they had in the past are not counted.
+	// Note: normally the return value of ZAdd only counts the number of new elements added.
+	CH bool
+
+	// When this option is specified ZAdd acts like ZIncrBy. Only one score-element pair can be specified in this mode.
+	INCR bool
+}
+
+// ZAddMember is element struct for set.
+type ZAddMember struct {
+	Score  float64
+	Member interface{}
+}
+
+// ZRangeOption provides extra option for ZRange function.
+type ZRangeOption struct {
+	ByScore bool
+	ByLex   bool
+	// The optional REV argument reverses the ordering, so elements are ordered from highest to lowest score,
+	// and score ties are resolved by reverse lexicographical ordering.
+	Rev   bool
+	Limit *ZRangeOptionLimit
+	// The optional WithScores argument supplements the command's reply with the scores of elements returned.
+	WithScores bool
+}
+
+// ZRangeOptionLimit provides LIMIT argument for ZRange function.
+// The optional LIMIT argument can be used to obtain a sub-range from the matching elements
+// (similar to SELECT LIMIT offset, count in SQL). A negative `Count` returns all elements from the `Offset`.
+type ZRangeOptionLimit struct {
+	Offset *int
+	Count  *int
+}
+
+// ZRevRangeOption provides options for function ZRevRange.
+type ZRevRangeOption struct {
+	WithScores bool
+}

+ 63 - 0
vendor/github.com/gogf/gf/v2/database/gredis/gredis_redis_group_string.go

@@ -0,0 +1,63 @@
+// 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 gredis
+
+import (
+	"context"
+
+	"github.com/gogf/gf/v2/container/gvar"
+)
+
+// IGroupString manages redis string operations.
+// Implements see redis.GroupString.
+type IGroupString interface {
+	Set(ctx context.Context, key string, value interface{}, option ...SetOption) (*gvar.Var, error)
+	SetNX(ctx context.Context, key string, value interface{}) (bool, error)
+	SetEX(ctx context.Context, key string, value interface{}, ttlInSeconds int64) error
+	Get(ctx context.Context, key string) (*gvar.Var, error)
+	GetDel(ctx context.Context, key string) (*gvar.Var, error)
+	GetEX(ctx context.Context, key string, option ...GetEXOption) (*gvar.Var, error)
+	GetSet(ctx context.Context, key string, value interface{}) (*gvar.Var, error)
+	StrLen(ctx context.Context, key string) (int64, error)
+	Append(ctx context.Context, key string, value string) (int64, error)
+	SetRange(ctx context.Context, key string, offset int64, value string) (int64, error)
+	GetRange(ctx context.Context, key string, start, end int64) (string, error)
+	Incr(ctx context.Context, key string) (int64, error)
+	IncrBy(ctx context.Context, key string, increment int64) (int64, error)
+	IncrByFloat(ctx context.Context, key string, increment float64) (float64, error)
+	Decr(ctx context.Context, key string) (int64, error)
+	DecrBy(ctx context.Context, key string, decrement int64) (int64, error)
+	MSet(ctx context.Context, keyValueMap map[string]interface{}) error
+	MSetNX(ctx context.Context, keyValueMap map[string]interface{}) (bool, error)
+	MGet(ctx context.Context, keys ...string) (map[string]*gvar.Var, error)
+}
+
+// TTLOption provides extra option for TTL related functions.
+type TTLOption struct {
+	EX      *int64 // EX seconds -- Set the specified expire time, in seconds.
+	PX      *int64 // PX milliseconds -- Set the specified expire time, in milliseconds.
+	EXAT    *int64 // EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
+	PXAT    *int64 // PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
+	KeepTTL bool   // Retain the time to live associated with the key.
+}
+
+// SetOption provides extra option for Set function.
+type SetOption struct {
+	TTLOption
+	NX bool // Only set the key if it does not already exist.
+	XX bool // Only set the key if it already exists.
+
+	// Return the old string stored at key, or nil if key did not exist.
+	// An error is returned and SET aborted if the value stored at key is not a string.
+	Get bool
+}
+
+// GetEXOption provides extra option for GetEx function.
+type GetEXOption struct {
+	TTLOption
+	Persist bool // Persist -- Remove the time to live associated with the key.
+}

+ 8 - 0
vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug.go

@@ -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 gdebug contains facilities for programs to debug themselves while they are running.
+package gdebug

+ 196 - 0
vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_caller.go

@@ -0,0 +1,196 @@
+// 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 gdebug
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+)
+
+const (
+	maxCallerDepth = 1000
+	stackFilterKey = "/debug/gdebug/gdebug"
+)
+
+var (
+	goRootForFilter  = runtime.GOROOT() // goRootForFilter is used for stack filtering purpose.
+	binaryVersion    = ""               // The version of current running binary(uint64 hex).
+	binaryVersionMd5 = ""               // The version of current running binary(MD5).
+	selfPath         = ""               // Current running binary absolute path.
+)
+
+func init() {
+	if goRootForFilter != "" {
+		goRootForFilter = strings.ReplaceAll(goRootForFilter, "\\", "/")
+	}
+	// Initialize internal package variable: selfPath.
+	selfPath, _ = exec.LookPath(os.Args[0])
+	if selfPath != "" {
+		selfPath, _ = filepath.Abs(selfPath)
+	}
+	if selfPath == "" {
+		selfPath, _ = filepath.Abs(os.Args[0])
+	}
+}
+
+// Caller returns the function name and the absolute file path along with its line
+// number of the caller.
+func Caller(skip ...int) (function string, path string, line int) {
+	return CallerWithFilter(nil, skip...)
+}
+
+// CallerWithFilter returns the function name and the absolute file path along with
+// its line number of the caller.
+//
+// The parameter `filters` is used to filter the path of the caller.
+func CallerWithFilter(filters []string, skip ...int) (function string, path string, line int) {
+	var (
+		number = 0
+		ok     = true
+	)
+	if len(skip) > 0 {
+		number = skip[0]
+	}
+	pc, file, line, start := callerFromIndex(filters)
+	if start != -1 {
+		for i := start + number; i < maxCallerDepth; i++ {
+			if i != start {
+				pc, file, line, ok = runtime.Caller(i)
+			}
+			if ok {
+				if filterFileByFilters(file, filters) {
+					continue
+				}
+				function = ""
+				if fn := runtime.FuncForPC(pc); fn == nil {
+					function = "unknown"
+				} else {
+					function = fn.Name()
+				}
+				return function, file, line
+			} else {
+				break
+			}
+		}
+	}
+	return "", "", -1
+}
+
+// callerFromIndex returns the caller position and according information exclusive of the
+// debug package.
+//
+// VERY NOTE THAT, the returned index value should be `index - 1` as the caller's start point.
+func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) {
+	var ok bool
+	for index = 0; index < maxCallerDepth; index++ {
+		if pc, file, line, ok = runtime.Caller(index); ok {
+			if filterFileByFilters(file, filters) {
+				continue
+			}
+			if index > 0 {
+				index--
+			}
+			return
+		}
+	}
+	return 0, "", -1, -1
+}
+
+func filterFileByFilters(file string, filters []string) (filtered bool) {
+	// Filter empty file.
+	if file == "" {
+		return true
+	}
+	// Filter gdebug package callings.
+	if strings.Contains(file, stackFilterKey) {
+		return true
+	}
+	for _, filter := range filters {
+		if filter != "" && strings.Contains(file, filter) {
+			return true
+		}
+	}
+	// GOROOT filter.
+	if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
+		// https://github.com/gogf/gf/issues/2047
+		fileSeparator := file[len(goRootForFilter)]
+		if fileSeparator == filepath.Separator || fileSeparator == '\\' || fileSeparator == '/' {
+			return true
+		}
+	}
+	return false
+}
+
+// CallerPackage returns the package name of the caller.
+func CallerPackage() string {
+	function, _, _ := Caller()
+	indexSplit := strings.LastIndexByte(function, '/')
+	if indexSplit == -1 {
+		return function[:strings.IndexByte(function, '.')]
+	} else {
+		leftPart := function[:indexSplit+1]
+		rightPart := function[indexSplit+1:]
+		indexDot := strings.IndexByte(function, '.')
+		rightPart = rightPart[:indexDot-1]
+		return leftPart + rightPart
+	}
+}
+
+// CallerFunction returns the function name of the caller.
+func CallerFunction() string {
+	function, _, _ := Caller()
+	function = function[strings.LastIndexByte(function, '/')+1:]
+	function = function[strings.IndexByte(function, '.')+1:]
+	return function
+}
+
+// CallerFilePath returns the file path of the caller.
+func CallerFilePath() string {
+	_, path, _ := Caller()
+	return path
+}
+
+// CallerDirectory returns the directory of the caller.
+func CallerDirectory() string {
+	_, path, _ := Caller()
+	return filepath.Dir(path)
+}
+
+// CallerFileLine returns the file path along with the line number of the caller.
+func CallerFileLine() string {
+	_, path, line := Caller()
+	return fmt.Sprintf(`%s:%d`, path, line)
+}
+
+// CallerFileLineShort returns the file name along with the line number of the caller.
+func CallerFileLineShort() string {
+	_, path, line := Caller()
+	return fmt.Sprintf(`%s:%d`, filepath.Base(path), line)
+}
+
+// FuncPath returns the complete function path of given `f`.
+func FuncPath(f interface{}) string {
+	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+}
+
+// FuncName returns the function name of given `f`.
+func FuncName(f interface{}) string {
+	path := FuncPath(f)
+	if path == "" {
+		return ""
+	}
+	index := strings.LastIndexByte(path, '/')
+	if index < 0 {
+		index = strings.LastIndexByte(path, '\\')
+	}
+	return path[index+1:]
+}

+ 29 - 0
vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_grid.go

@@ -0,0 +1,29 @@
+// 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 gdebug
+
+import (
+	"regexp"
+	"runtime"
+	"strconv"
+)
+
+var (
+	// gridRegex is the regular expression object for parsing goroutine id from stack information.
+	gridRegex = regexp.MustCompile(`^\w+\s+(\d+)\s+`)
+)
+
+// GoroutineId retrieves and returns the current goroutine id from stack information.
+// Be very aware that, it is with low performance as it uses runtime.Stack function.
+// It is commonly used for debugging purpose.
+func GoroutineId() int {
+	buf := make([]byte, 26)
+	runtime.Stack(buf, false)
+	match := gridRegex.FindSubmatch(buf)
+	id, _ := strconv.Atoi(string(match[1]))
+	return id
+}

+ 77 - 0
vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_stack.go

@@ -0,0 +1,77 @@
+// 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 gdebug
+
+import (
+	"bytes"
+	"fmt"
+	"runtime"
+)
+
+// PrintStack prints to standard error the stack trace returned by runtime.Stack.
+func PrintStack(skip ...int) {
+	fmt.Print(Stack(skip...))
+}
+
+// Stack returns a formatted stack trace of the goroutine that calls it.
+// It calls runtime.Stack with a large enough buffer to capture the entire trace.
+func Stack(skip ...int) string {
+	return StackWithFilter(nil, skip...)
+}
+
+// StackWithFilter returns a formatted stack trace of the goroutine that calls it.
+// It calls runtime.Stack with a large enough buffer to capture the entire trace.
+//
+// The parameter `filter` is used to filter the path of the caller.
+func StackWithFilter(filters []string, skip ...int) string {
+	return StackWithFilters(filters, skip...)
+}
+
+// StackWithFilters returns a formatted stack trace of the goroutine that calls it.
+// It calls runtime.Stack with a large enough buffer to capture the entire trace.
+//
+// The parameter `filters` is a slice of strings, which are used to filter the path of the
+// caller.
+//
+// TODO Improve the performance using debug.Stack.
+func StackWithFilters(filters []string, skip ...int) string {
+	number := 0
+	if len(skip) > 0 {
+		number = skip[0]
+	}
+	var (
+		name                  string
+		space                 = "  "
+		index                 = 1
+		buffer                = bytes.NewBuffer(nil)
+		ok                    = true
+		pc, file, line, start = callerFromIndex(filters)
+	)
+	for i := start + number; i < maxCallerDepth; i++ {
+		if i != start {
+			pc, file, line, ok = runtime.Caller(i)
+		}
+		if ok {
+			if filterFileByFilters(file, filters) {
+				continue
+			}
+			if fn := runtime.FuncForPC(pc); fn == nil {
+				name = "unknown"
+			} else {
+				name = fn.Name()
+			}
+			if index > 9 {
+				space = " "
+			}
+			buffer.WriteString(fmt.Sprintf("%d.%s%s\n    %s:%d\n", index, space, name, file, line))
+			index++
+		} else {
+			break
+		}
+	}
+	return buffer.String()
+}

+ 57 - 0
vendor/github.com/gogf/gf/v2/debug/gdebug/gdebug_version.go

@@ -0,0 +1,57 @@
+// 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 gdebug
+
+import (
+	"crypto/md5"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+
+	"github.com/gogf/gf/v2/encoding/ghash"
+	"github.com/gogf/gf/v2/errors/gerror"
+)
+
+// BinVersion returns the version of current running binary.
+// It uses ghash.BKDRHash+BASE36 algorithm to calculate the unique version of the binary.
+func BinVersion() string {
+	if binaryVersion == "" {
+		binaryContent, _ := os.ReadFile(selfPath)
+		binaryVersion = strconv.FormatInt(
+			int64(ghash.BKDR(binaryContent)),
+			36,
+		)
+	}
+	return binaryVersion
+}
+
+// BinVersionMd5 returns the version of current running binary.
+// It uses MD5 algorithm to calculate the unique version of the binary.
+func BinVersionMd5() string {
+	if binaryVersionMd5 == "" {
+		binaryVersionMd5, _ = md5File(selfPath)
+	}
+	return binaryVersionMd5
+}
+
+// md5File encrypts file content of `path` using MD5 algorithms.
+func md5File(path string) (encrypt string, err error) {
+	f, err := os.Open(path)
+	if err != nil {
+		err = gerror.Wrapf(err, `os.Open failed for name "%s"`, path)
+		return "", err
+	}
+	defer f.Close()
+	h := md5.New()
+	_, err = io.Copy(h, f)
+	if err != nil {
+		err = gerror.Wrap(err, `io.Copy failed`)
+		return "", err
+	}
+	return fmt.Sprintf("%x", h.Sum(nil)), nil
+}

+ 134 - 0
vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary.go

@@ -0,0 +1,134 @@
+// 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 gbinary provides useful API for handling binary/bytes data.
+//
+// Note that package gbinary encodes the data using LittleEndian in default.
+package gbinary
+
+func Encode(values ...interface{}) []byte {
+	return LeEncode(values...)
+}
+
+func EncodeByLength(length int, values ...interface{}) []byte {
+	return LeEncodeByLength(length, values...)
+}
+
+func Decode(b []byte, values ...interface{}) error {
+	return LeDecode(b, values...)
+}
+
+func EncodeString(s string) []byte {
+	return LeEncodeString(s)
+}
+
+func DecodeToString(b []byte) string {
+	return LeDecodeToString(b)
+}
+
+func EncodeBool(b bool) []byte {
+	return LeEncodeBool(b)
+}
+
+func EncodeInt(i int) []byte {
+	return LeEncodeInt(i)
+}
+
+func EncodeUint(i uint) []byte {
+	return LeEncodeUint(i)
+}
+
+func EncodeInt8(i int8) []byte {
+	return LeEncodeInt8(i)
+}
+
+func EncodeUint8(i uint8) []byte {
+	return LeEncodeUint8(i)
+}
+
+func EncodeInt16(i int16) []byte {
+	return LeEncodeInt16(i)
+}
+
+func EncodeUint16(i uint16) []byte {
+	return LeEncodeUint16(i)
+}
+
+func EncodeInt32(i int32) []byte {
+	return LeEncodeInt32(i)
+}
+
+func EncodeUint32(i uint32) []byte {
+	return LeEncodeUint32(i)
+}
+
+func EncodeInt64(i int64) []byte {
+	return LeEncodeInt64(i)
+}
+
+func EncodeUint64(i uint64) []byte {
+	return LeEncodeUint64(i)
+}
+
+func EncodeFloat32(f float32) []byte {
+	return LeEncodeFloat32(f)
+}
+
+func EncodeFloat64(f float64) []byte {
+	return LeEncodeFloat64(f)
+}
+
+func DecodeToInt(b []byte) int {
+	return LeDecodeToInt(b)
+}
+
+func DecodeToUint(b []byte) uint {
+	return LeDecodeToUint(b)
+}
+
+func DecodeToBool(b []byte) bool {
+	return LeDecodeToBool(b)
+}
+
+func DecodeToInt8(b []byte) int8 {
+	return LeDecodeToInt8(b)
+}
+
+func DecodeToUint8(b []byte) uint8 {
+	return LeDecodeToUint8(b)
+}
+
+func DecodeToInt16(b []byte) int16 {
+	return LeDecodeToInt16(b)
+}
+
+func DecodeToUint16(b []byte) uint16 {
+	return LeDecodeToUint16(b)
+}
+
+func DecodeToInt32(b []byte) int32 {
+	return LeDecodeToInt32(b)
+}
+
+func DecodeToUint32(b []byte) uint32 {
+	return LeDecodeToUint32(b)
+}
+
+func DecodeToInt64(b []byte) int64 {
+	return LeDecodeToInt64(b)
+}
+
+func DecodeToUint64(b []byte) uint64 {
+	return LeDecodeToUint64(b)
+}
+
+func DecodeToFloat32(b []byte) float32 {
+	return LeDecodeToFloat32(b)
+}
+
+func DecodeToFloat64(b []byte) float64 {
+	return LeDecodeToFloat64(b)
+}

+ 287 - 0
vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_be.go

@@ -0,0 +1,287 @@
+// 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 gbinary
+
+import (
+	"bytes"
+	"context"
+	"encoding/binary"
+	"fmt"
+	"math"
+
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/internal/intlog"
+)
+
+// BeEncode encodes one or multiple `values` into bytes using BigEndian.
+// It uses type asserting checking the type of each value of `values` and internally
+// calls corresponding converting function do the bytes converting.
+//
+// It supports common variable type asserting, and finally it uses fmt.Sprintf converting
+// value to string and then to bytes.
+func BeEncode(values ...interface{}) []byte {
+	buf := new(bytes.Buffer)
+	for i := 0; i < len(values); i++ {
+		if values[i] == nil {
+			return buf.Bytes()
+		}
+
+		switch value := values[i].(type) {
+		case int:
+			buf.Write(BeEncodeInt(value))
+		case int8:
+			buf.Write(BeEncodeInt8(value))
+		case int16:
+			buf.Write(BeEncodeInt16(value))
+		case int32:
+			buf.Write(BeEncodeInt32(value))
+		case int64:
+			buf.Write(BeEncodeInt64(value))
+		case uint:
+			buf.Write(BeEncodeUint(value))
+		case uint8:
+			buf.Write(BeEncodeUint8(value))
+		case uint16:
+			buf.Write(BeEncodeUint16(value))
+		case uint32:
+			buf.Write(BeEncodeUint32(value))
+		case uint64:
+			buf.Write(BeEncodeUint64(value))
+		case bool:
+			buf.Write(BeEncodeBool(value))
+		case string:
+			buf.Write(BeEncodeString(value))
+		case []byte:
+			buf.Write(value)
+		case float32:
+			buf.Write(BeEncodeFloat32(value))
+		case float64:
+			buf.Write(BeEncodeFloat64(value))
+		default:
+			if err := binary.Write(buf, binary.BigEndian, value); err != nil {
+				intlog.Errorf(context.TODO(), `%+v`, err)
+				buf.Write(BeEncodeString(fmt.Sprintf("%v", value)))
+			}
+		}
+	}
+	return buf.Bytes()
+}
+
+func BeEncodeByLength(length int, values ...interface{}) []byte {
+	b := BeEncode(values...)
+	if len(b) < length {
+		b = append(b, make([]byte, length-len(b))...)
+	} else if len(b) > length {
+		b = b[0:length]
+	}
+	return b
+}
+
+func BeDecode(b []byte, values ...interface{}) error {
+	var (
+		err error
+		buf = bytes.NewBuffer(b)
+	)
+	for i := 0; i < len(values); i++ {
+		if err = binary.Read(buf, binary.BigEndian, values[i]); err != nil {
+			err = gerror.Wrap(err, `binary.Read failed`)
+			return err
+		}
+	}
+	return nil
+}
+
+func BeEncodeString(s string) []byte {
+	return []byte(s)
+}
+
+func BeDecodeToString(b []byte) string {
+	return string(b)
+}
+
+func BeEncodeBool(b bool) []byte {
+	if b {
+		return []byte{1}
+	} else {
+		return []byte{0}
+	}
+}
+
+func BeEncodeInt(i int) []byte {
+	if i <= math.MaxInt8 {
+		return BeEncodeInt8(int8(i))
+	} else if i <= math.MaxInt16 {
+		return BeEncodeInt16(int16(i))
+	} else if i <= math.MaxInt32 {
+		return BeEncodeInt32(int32(i))
+	} else {
+		return BeEncodeInt64(int64(i))
+	}
+}
+
+func BeEncodeUint(i uint) []byte {
+	if i <= math.MaxUint8 {
+		return BeEncodeUint8(uint8(i))
+	} else if i <= math.MaxUint16 {
+		return BeEncodeUint16(uint16(i))
+	} else if i <= math.MaxUint32 {
+		return BeEncodeUint32(uint32(i))
+	} else {
+		return BeEncodeUint64(uint64(i))
+	}
+}
+
+func BeEncodeInt8(i int8) []byte {
+	return []byte{byte(i)}
+}
+
+func BeEncodeUint8(i uint8) []byte {
+	return []byte{i}
+}
+
+func BeEncodeInt16(i int16) []byte {
+	b := make([]byte, 2)
+	binary.BigEndian.PutUint16(b, uint16(i))
+	return b
+}
+
+func BeEncodeUint16(i uint16) []byte {
+	b := make([]byte, 2)
+	binary.BigEndian.PutUint16(b, i)
+	return b
+}
+
+func BeEncodeInt32(i int32) []byte {
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, uint32(i))
+	return b
+}
+
+func BeEncodeUint32(i uint32) []byte {
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, i)
+	return b
+}
+
+func BeEncodeInt64(i int64) []byte {
+	b := make([]byte, 8)
+	binary.BigEndian.PutUint64(b, uint64(i))
+	return b
+}
+
+func BeEncodeUint64(i uint64) []byte {
+	b := make([]byte, 8)
+	binary.BigEndian.PutUint64(b, i)
+	return b
+}
+
+func BeEncodeFloat32(f float32) []byte {
+	bits := math.Float32bits(f)
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, bits)
+	return b
+}
+
+func BeEncodeFloat64(f float64) []byte {
+	bits := math.Float64bits(f)
+	b := make([]byte, 8)
+	binary.BigEndian.PutUint64(b, bits)
+	return b
+}
+
+func BeDecodeToInt(b []byte) int {
+	if len(b) < 2 {
+		return int(BeDecodeToUint8(b))
+	} else if len(b) < 3 {
+		return int(BeDecodeToUint16(b))
+	} else if len(b) < 5 {
+		return int(BeDecodeToUint32(b))
+	} else {
+		return int(BeDecodeToUint64(b))
+	}
+}
+
+func BeDecodeToUint(b []byte) uint {
+	if len(b) < 2 {
+		return uint(BeDecodeToUint8(b))
+	} else if len(b) < 3 {
+		return uint(BeDecodeToUint16(b))
+	} else if len(b) < 5 {
+		return uint(BeDecodeToUint32(b))
+	} else {
+		return uint(BeDecodeToUint64(b))
+	}
+}
+
+func BeDecodeToBool(b []byte) bool {
+	if len(b) == 0 {
+		return false
+	}
+	if bytes.Equal(b, make([]byte, len(b))) {
+		return false
+	}
+	return true
+}
+
+func BeDecodeToInt8(b []byte) int8 {
+	if len(b) == 0 {
+		panic(`empty slice given`)
+	}
+	return int8(b[0])
+}
+
+func BeDecodeToUint8(b []byte) uint8 {
+	if len(b) == 0 {
+		panic(`empty slice given`)
+	}
+	return b[0]
+}
+
+func BeDecodeToInt16(b []byte) int16 {
+	return int16(binary.BigEndian.Uint16(BeFillUpSize(b, 2)))
+}
+
+func BeDecodeToUint16(b []byte) uint16 {
+	return binary.BigEndian.Uint16(BeFillUpSize(b, 2))
+}
+
+func BeDecodeToInt32(b []byte) int32 {
+	return int32(binary.BigEndian.Uint32(BeFillUpSize(b, 4)))
+}
+
+func BeDecodeToUint32(b []byte) uint32 {
+	return binary.BigEndian.Uint32(BeFillUpSize(b, 4))
+}
+
+func BeDecodeToInt64(b []byte) int64 {
+	return int64(binary.BigEndian.Uint64(BeFillUpSize(b, 8)))
+}
+
+func BeDecodeToUint64(b []byte) uint64 {
+	return binary.BigEndian.Uint64(BeFillUpSize(b, 8))
+}
+
+func BeDecodeToFloat32(b []byte) float32 {
+	return math.Float32frombits(binary.BigEndian.Uint32(BeFillUpSize(b, 4)))
+}
+
+func BeDecodeToFloat64(b []byte) float64 {
+	return math.Float64frombits(binary.BigEndian.Uint64(BeFillUpSize(b, 8)))
+}
+
+// BeFillUpSize fills up the bytes `b` to given length `l` using big BigEndian.
+//
+// Note that it creates a new bytes slice by copying the original one to avoid changing
+// the original parameter bytes.
+func BeFillUpSize(b []byte, l int) []byte {
+	if len(b) >= l {
+		return b[:l]
+	}
+	c := make([]byte, l)
+	copy(c[l-len(b):], b)
+	return c
+}

+ 74 - 0
vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_bit.go

@@ -0,0 +1,74 @@
+// 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 gbinary
+
+// NOTE: THIS IS AN EXPERIMENTAL FEATURE!
+
+// Bit Binary bit (0 | 1)
+type Bit int8
+
+// EncodeBits does encode bits return bits Default coding
+func EncodeBits(bits []Bit, i int, l int) []Bit {
+	return EncodeBitsWithUint(bits, uint(i), l)
+}
+
+// EncodeBitsWithUint . Merge ui bitwise into the bits array and occupy the length bits
+// (Note: binary 0 | 1 digits are stored in the uis array)
+func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit {
+	a := make([]Bit, l)
+	for i := l - 1; i >= 0; i-- {
+		a[i] = Bit(ui & 1)
+		ui >>= 1
+	}
+	if bits != nil {
+		return append(bits, a...)
+	}
+	return a
+}
+
+// EncodeBitsToBytes . does encode bits to bytes
+// Convert bits to [] byte, encode from left to right, and add less than 1 byte from 0 to the end.
+func EncodeBitsToBytes(bits []Bit) []byte {
+	if len(bits)%8 != 0 {
+		for i := 0; i < len(bits)%8; i++ {
+			bits = append(bits, 0)
+		}
+	}
+	b := make([]byte, 0)
+	for i := 0; i < len(bits); i += 8 {
+		b = append(b, byte(DecodeBitsToUint(bits[i:i+8])))
+	}
+	return b
+}
+
+// DecodeBits .does decode bits to int
+// Resolve to int
+func DecodeBits(bits []Bit) int {
+	v := 0
+	for _, i := range bits {
+		v = v<<1 | int(i)
+	}
+	return v
+}
+
+// DecodeBitsToUint .Resolve to uint
+func DecodeBitsToUint(bits []Bit) uint {
+	v := uint(0)
+	for _, i := range bits {
+		v = v<<1 | uint(i)
+	}
+	return v
+}
+
+// DecodeBytesToBits .Parsing [] byte into character array [] uint8
+func DecodeBytesToBits(bs []byte) []Bit {
+	bits := make([]Bit, 0)
+	for _, b := range bs {
+		bits = EncodeBitsWithUint(bits, uint(b), 8)
+	}
+	return bits
+}

+ 7 - 0
vendor/github.com/gogf/gf/v2/encoding/gbinary/gbinary_func.go

@@ -0,0 +1,7 @@
+// 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 gbinary

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است