Browse Source

修改etcd v3

lijian 4 năm trước cách đây
mục cha
commit
5260fb3f71
100 tập tin đã thay đổi với 22121 bổ sung85 xóa
  1. 201 0
      pkg/logger/logger.go
  2. 16 18
      pkg/server/server.go
  3. 54 58
      pkg/server/server_manager.go
  4. 9 9
      run.sh
  5. 55 0
      vendor/github.com/clbanning/mxj/LICENSE
  6. 201 0
      vendor/github.com/clbanning/mxj/anyxml.go
  7. 54 0
      vendor/github.com/clbanning/mxj/atomFeedString.xml
  8. 138 0
      vendor/github.com/clbanning/mxj/doc.go
  9. 93 0
      vendor/github.com/clbanning/mxj/escapechars.go
  10. 9 0
      vendor/github.com/clbanning/mxj/exists.go
  11. 287 0
      vendor/github.com/clbanning/mxj/files.go
  12. 2 0
      vendor/github.com/clbanning/mxj/files_test.badjson
  13. 9 0
      vendor/github.com/clbanning/mxj/files_test.badxml
  14. 2 0
      vendor/github.com/clbanning/mxj/files_test.json
  15. 9 0
      vendor/github.com/clbanning/mxj/files_test.xml
  16. 1 0
      vendor/github.com/clbanning/mxj/files_test_dup.json
  17. 1 0
      vendor/github.com/clbanning/mxj/files_test_dup.xml
  18. 12 0
      vendor/github.com/clbanning/mxj/files_test_indent.json
  19. 8 0
      vendor/github.com/clbanning/mxj/files_test_indent.xml
  20. 3 0
      vendor/github.com/clbanning/mxj/go.mod
  21. 35 0
      vendor/github.com/clbanning/mxj/gob.go
  22. 323 0
      vendor/github.com/clbanning/mxj/json.go
  23. 668 0
      vendor/github.com/clbanning/mxj/keyvalues.go
  24. 112 0
      vendor/github.com/clbanning/mxj/leafnode.go
  25. 86 0
      vendor/github.com/clbanning/mxj/misc.go
  26. 128 0
      vendor/github.com/clbanning/mxj/mxj.go
  27. 184 0
      vendor/github.com/clbanning/mxj/newmap.go
  28. 206 0
      vendor/github.com/clbanning/mxj/readme.md
  29. 37 0
      vendor/github.com/clbanning/mxj/remove.go
  30. 61 0
      vendor/github.com/clbanning/mxj/rename.go
  31. 26 0
      vendor/github.com/clbanning/mxj/set.go
  32. 20 0
      vendor/github.com/clbanning/mxj/setfieldsep.go
  33. 29 0
      vendor/github.com/clbanning/mxj/songtext.xml
  34. 30 0
      vendor/github.com/clbanning/mxj/strict.go
  35. 54 0
      vendor/github.com/clbanning/mxj/struct.go
  36. 258 0
      vendor/github.com/clbanning/mxj/updatevalues.go
  37. 1339 0
      vendor/github.com/clbanning/mxj/xml.go
  38. 850 0
      vendor/github.com/clbanning/mxj/xmlseq.go
  39. 18 0
      vendor/github.com/clbanning/mxj/xmlseq2.go
  40. 202 0
      vendor/github.com/coreos/go-semver/LICENSE
  41. 268 0
      vendor/github.com/coreos/go-semver/semver/semver.go
  42. 38 0
      vendor/github.com/coreos/go-semver/semver/sort.go
  43. 191 0
      vendor/github.com/coreos/go-systemd/LICENSE
  44. 5 0
      vendor/github.com/coreos/go-systemd/NOTICE
  45. 182 0
      vendor/github.com/coreos/go-systemd/journal/journal.go
  46. 202 0
      vendor/github.com/coreos/pkg/LICENSE
  47. 5 0
      vendor/github.com/coreos/pkg/NOTICE
  48. 157 0
      vendor/github.com/coreos/pkg/capnslog/formatters.go
  49. 96 0
      vendor/github.com/coreos/pkg/capnslog/glog_formatter.go
  50. 49 0
      vendor/github.com/coreos/pkg/capnslog/init.go
  51. 25 0
      vendor/github.com/coreos/pkg/capnslog/init_windows.go
  52. 68 0
      vendor/github.com/coreos/pkg/capnslog/journald_formatter.go
  53. 39 0
      vendor/github.com/coreos/pkg/capnslog/log_hijack.go
  54. 240 0
      vendor/github.com/coreos/pkg/capnslog/logmap.go
  55. 177 0
      vendor/github.com/coreos/pkg/capnslog/pkg_logger.go
  56. 65 0
      vendor/github.com/coreos/pkg/capnslog/syslog_formatter.go
  57. 52 0
      vendor/github.com/fsnotify/fsnotify/AUTHORS
  58. 317 0
      vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
  59. 77 0
      vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
  60. 28 0
      vendor/github.com/fsnotify/fsnotify/LICENSE
  61. 130 0
      vendor/github.com/fsnotify/fsnotify/README.md
  62. 37 0
      vendor/github.com/fsnotify/fsnotify/fen.go
  63. 68 0
      vendor/github.com/fsnotify/fsnotify/fsnotify.go
  64. 5 0
      vendor/github.com/fsnotify/fsnotify/go.mod
  65. 2 0
      vendor/github.com/fsnotify/fsnotify/go.sum
  66. 337 0
      vendor/github.com/fsnotify/fsnotify/inotify.go
  67. 187 0
      vendor/github.com/fsnotify/fsnotify/inotify_poller.go
  68. 521 0
      vendor/github.com/fsnotify/fsnotify/kqueue.go
  69. 11 0
      vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
  70. 12 0
      vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
  71. 561 0
      vendor/github.com/fsnotify/fsnotify/windows.go
  72. 21 0
      vendor/github.com/gogf/gf/LICENSE
  73. 113 0
      vendor/github.com/gogf/gf/README.MD
  74. 117 0
      vendor/github.com/gogf/gf/README_ZH.MD
  75. 8 0
      vendor/github.com/gogf/gf/container/garray/garray.go
  76. 74 0
      vendor/github.com/gogf/gf/container/garray/garray_func.go
  77. 800 0
      vendor/github.com/gogf/gf/container/garray/garray_normal_any.go
  78. 784 0
      vendor/github.com/gogf/gf/container/garray/garray_normal_int.go
  79. 799 0
      vendor/github.com/gogf/gf/container/garray/garray_normal_str.go
  80. 790 0
      vendor/github.com/gogf/gf/container/garray/garray_sorted_any.go
  81. 736 0
      vendor/github.com/gogf/gf/container/garray/garray_sorted_int.go
  82. 749 0
      vendor/github.com/gogf/gf/container/garray/garray_sorted_str.go
  83. 545 0
      vendor/github.com/gogf/gf/container/glist/glist.go
  84. 45 0
      vendor/github.com/gogf/gf/container/gmap/gmap.go
  85. 499 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_any_any_map.go
  86. 500 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_int_any_map.go
  87. 471 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_int_int_map.go
  88. 471 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_int_str_map.go
  89. 486 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_str_any_map.go
  90. 474 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_str_int_map.go
  91. 464 0
      vendor/github.com/gogf/gf/container/gmap/gmap_hash_str_str_map.go
  92. 562 0
      vendor/github.com/gogf/gf/container/gmap/gmap_list_map.go
  93. 30 0
      vendor/github.com/gogf/gf/container/gmap/gmap_tree_map.go
  94. 180 0
      vendor/github.com/gogf/gf/container/gpool/gpool.go
  95. 146 0
      vendor/github.com/gogf/gf/container/gqueue/gqueue.go
  96. 507 0
      vendor/github.com/gogf/gf/container/gset/gset_any_set.go
  97. 467 0
      vendor/github.com/gogf/gf/container/gset/gset_int_set.go
  98. 495 0
      vendor/github.com/gogf/gf/container/gset/gset_str_set.go
  99. 11 0
      vendor/github.com/gogf/gf/container/gtree/gtree.go
  100. 795 0
      vendor/github.com/gogf/gf/container/gtree/gtree_avltree.go

+ 201 - 0
pkg/logger/logger.go

@@ -0,0 +1,201 @@
+package logger
+
+import (
+	"context"
+	"errors"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/glog"
+)
+
+// 定义键名
+const (
+	TraceIDKey      = "trace_id"
+	UserIDKey       = "user_id"
+	SpanTitleKey    = "span_title"
+	SpanFunctionKey = "span_function"
+	VersionKey      = "version"
+)
+
+var log *glog.Logger
+
+// TraceIDFunc 定义获取跟踪ID的函数
+type TraceIDFunc func() string
+
+var (
+	version     string
+	traceIDFunc TraceIDFunc
+)
+
+// SetTraceIdFunc 设置获取追踪Id的生成函数
+func SetTraceIdFunc(fn TraceIDFunc) {
+	traceIDFunc = fn
+}
+
+// SetVersion 设置版本号
+func SetVersion(ver string) {
+	version = ver
+}
+
+// FromTraceIDContext 从上下文中获取跟踪ID
+func FromTraceIDContext(ctx context.Context) string {
+	v := ctx.Value(TraceIDKey)
+	if v != nil {
+		if s, ok := v.(string); ok {
+			return s
+		}
+	}
+	return getTraceID()
+}
+
+// NewUserIDContext 创建用户ID上下文
+func NewUserIDContext(ctx context.Context, userID string) context.Context {
+	return context.WithValue(ctx, UserIDKey, userID)
+}
+
+// FromUserIDContext 从上下文中获取用户ID
+func FromUserIDContext(ctx context.Context) string {
+	v := ctx.Value(UserIDKey)
+	if v != nil {
+		if s, ok := v.(string); ok {
+			return s
+		}
+	}
+	return ""
+}
+
+func getTraceID() string {
+	if traceIDFunc != nil {
+		return traceIDFunc()
+	}
+	return ""
+}
+
+// GetLogger 获取logger
+func GetLogger() *glog.Logger {
+	return log
+}
+
+// NewTraceIDContext 创建跟踪ID上下文
+func NewTraceIDContext(ctx context.Context, traceID string) context.Context {
+	return context.WithValue(ctx, TraceIDKey, traceID)
+}
+
+// Init 初始化日志配置
+func Init(runMode string) {
+	if log != nil {
+		panic(errors.New("重复初始化logger"))
+	}
+	log = g.Log()
+	var lv string
+	switch runMode {
+	case "debug":
+		lv = "DEV"
+		break
+	case "test":
+		lv = "DEV"
+		break
+	case "release":
+		lv = "PRODUCT"
+	default:
+		lv = "DEV"
+		break
+	}
+	err := log.SetLevelStr(lv)
+	if err != nil {
+		panic(err)
+	}
+}
+
+type spanOptions struct {
+	Title    string
+	FuncName string
+}
+
+// SpanOption 定义跟踪单元的数据项
+type SpanOption func(*spanOptions)
+
+// SetSpanTitle 设置跟踪单元的标题
+func SetSpanTitle(title string) SpanOption {
+	return func(o *spanOptions) {
+		o.Title = title
+	}
+}
+
+// SetSpanFuncName 设置跟踪单元的函数名
+func SetSpanFuncName(funcName string) SpanOption {
+	return func(o *spanOptions) {
+		o.FuncName = funcName
+	}
+}
+
+// StartSpan 开始一个追踪单元
+func StartSpan(ctx context.Context, opts ...SpanOption) *Entry {
+	if ctx == nil {
+		ctx = context.Background()
+	}
+
+	var o spanOptions
+	for _, opt := range opts {
+		opt(&o)
+	}
+	return NewEntry(log)
+}
+
+// Entry 定义统一的日志写入方式
+type Entry struct {
+	entry *glog.Logger
+}
+
+func NewEntry(entry *glog.Logger) *Entry {
+	return &Entry{entry: entry}
+}
+
+// Debugf 写入调试日志
+func Debugf(ctx context.Context, format string, args ...interface{}) {
+	StartSpan(ctx).entry.Ctx(ctx).Debugf(format, args)
+}
+
+// Printf 写入消息日志
+func Printf(ctx context.Context, format string, args ...interface{}) {
+	StartSpan(ctx).entry.Ctx(ctx).Printf(format, args...)
+}
+
+// Warnf 写入警告日志
+func Warnf(ctx context.Context, format string, args ...interface{}) {
+	StartSpan(ctx).entry.Ctx(ctx).Warningf(format, args...)
+}
+
+// Fatalf 写入重大错误日志
+func Fatalf(ctx context.Context, format string, args ...interface{}) {
+	StartSpan(ctx).entry.Ctx(ctx).Fatalf(format, args...)
+}
+
+// Errorf 错误日志
+func Errorf(ctx context.Context, format string, args ...interface{}) {
+	StartSpan(ctx).entry.Ctx(ctx).Errorf(format, args...)
+}
+
+// Errorf 错误日志
+func (e *Entry) Errorf(format string, args ...interface{}) {
+	e.entry.Errorf(format, args...)
+}
+
+// Warnf 警告日志
+func (e *Entry) Warnf(format string, args ...interface{}) {
+	e.entry.Warningf(format, args...)
+}
+
+// Infof 消息日志
+func (e *Entry) Infof(format string, args ...interface{}) {
+	e.entry.Infof(format, args...)
+}
+
+// Printf 消息日志
+func (e *Entry) Printf(format string, args ...interface{}) {
+	e.entry.Printf(format, args...)
+}
+
+// Debugf 写入调试日志
+func (e *Entry) Debugf(format string, args ...interface{}) {
+	e.entry.Debugf(format, args...)
+}

+ 16 - 18
pkg/server/server.go

@@ -8,7 +8,6 @@ package server
 
 import (
 	"context"
-
 	"github.com/opentracing/opentracing-go"
 	"github.com/opentracing/opentracing-go/log"
 
@@ -46,18 +45,17 @@ func Init(name string) error {
 
 		// read network info
 		readNetInterfaces()
-
 		// log
 		err := initLog(name, *confLogLevel)
 		if err != nil {
 			return err
 		}
 
+
 		// server instance
 		serverInstance = &Server{
 			name: name,
 		}
-
 		// init service manager
 		serverInstance.svrmgr, err = NewServerManager(name, *confEtcd)
 		if err != nil {
@@ -168,13 +166,13 @@ func RegisterRPCHandler(rcvr interface{}) error {
 
 		serverInstance.rpcsvr = &RPCServer{
 			TCPServer{
-				addr:    addr,
-				handler: &handler,
-				useTls:  false, // rpc service do not use tls because it's in internal network
-			},
-		}
-	}
-	return nil
+addr:    addr,
+	handler: &handler,
+		useTls:  false, // rpc service do not use tls because it's in internal network
+},
+}
+}
+return nil
 }
 
 // RegisterTimerTask register timer task
@@ -281,6 +279,13 @@ func Run() error {
 		}
 		Log.Info("starting rpc server ... OK")
 	}
+	// server manager update
+	err := serverInstance.svrmgr.RegisterServer()
+	if err != nil {
+		Log.Warnf("RegisterServer error: %s", err)
+	} else {
+		Log.Info("RegisterServer Success")
+	}
 	tracer, closer := tracing.Init(serverInstance.name)
 	// opentracing
 	defer closer.Close()
@@ -289,14 +294,7 @@ func Run() error {
 
 	// loop to do something
 	for {
-		// server manager update
-		err := serverInstance.svrmgr.RegisterServer()
-		if err != nil {
-			Log.Warnf("RegisterServer error: %s", err)
-		} else {
-			Log.Info("RegisterServer Success")
-		}
-		err = serverInstance.svrmgr.UpdateServerHosts()
+		err := serverInstance.svrmgr.UpdateServerHosts()
 		if err != nil {
 			Log.Errorf("UpdateServerHosts error: %s", err)
 		} else {

+ 54 - 58
pkg/server/server_manager.go

@@ -4,11 +4,13 @@ package server
 
 import (
 	"errors"
+	"fmt"
+	"go.etcd.io/etcd/clientv3"
 	"os"
 	"strings"
+	"sync"
 	"time"
 
-	"github.com/coreos/etcd/client"
 	"golang.org/x/net/context"
 )
 
@@ -19,6 +21,8 @@ const (
 	EnvTCPProxy          = "TCP_PROXY_ADDR"
 	EnvHTTPProxy         = "HTTP_PROXY_ADDR"
 	EnvUDPProxy          = "UDP_PROXY_ADDR"
+
+	lease = 90
 )
 
 // ServerManager server manager
@@ -26,8 +30,12 @@ type ServerManager struct {
 	serverName string
 	// servername -> hosttype -> hostlist
 	// eg. var hosts []string = mapServers["testserver"]["rpchost"]
-	mapServers map[string](map[string][]string)
-	etcdHosts  []string
+	mapServers    map[string]map[string][]string
+	etcdHosts     []string
+	cli           *clientv3.Client
+	leaseId       clientv3.LeaseID
+	keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse
+	mu sync.Mutex
 }
 
 // NewServerManager new server manager
@@ -36,10 +44,19 @@ func NewServerManager(name string, etcd string) (*ServerManager, error) {
 	if etcd == "" {
 		return nil, errors.New("no etcd host found!")
 	}
+	etcdHosts := strings.Split(etcd, ";")
+	cli, err := clientv3.New(clientv3.Config{
+		Endpoints:            etcdHosts,
+		DialTimeout:          5 * time.Second,
+	})
+	if err != nil {
+		return nil, err
+	}
 	return &ServerManager{
 		serverName: name,
-		etcdHosts:  strings.Split(etcd, ";"),
-		mapServers: make(map[string](map[string][]string)),
+		cli:cli,
+		etcdHosts: etcdHosts,
+		mapServers: make(map[string]map[string][]string),
 	}, nil
 }
 
@@ -48,51 +65,59 @@ func (mgr *ServerManager) RegisterServer() error {
 	if serverInstance == nil {
 		return errorf(errServerNotInit)
 	}
-	cfg := client.Config{
-		Endpoints: mgr.etcdHosts,
-		Transport: client.DefaultTransport,
-		// set timeout per request to fail fast when the target endpoint is unavailable
-		HeaderTimeoutPerRequest: time.Second,
-	}
-	c, err := client.New(cfg)
+	ctx := context.Background()
+	resp, err := mgr.cli.Grant(ctx, lease)
 	if err != nil {
 		return err
 	}
-	kapi := client.NewKeysAPI(c)
-	prefix := EtcdServersPrefix + mgr.serverName + "/"
-	var response *client.Response
-	opt := &client.SetOptions{TTL: 90 * time.Second}
+	prefix := fmt.Sprintf("%s%s/",EtcdServersPrefix, mgr.serverName )
+	var (
+		addr string
+		key string
+	)
+
 	if serverInstance.tcpsvr != nil {
 		addr := os.Getenv(EnvTCPProxy)
 		if addr == "" {
 			addr, _ = fixHostIp(*confTCPHost)
 		}
-		response, err = kapi.Set(context.Background(), prefix+FlagTCPHost+"/"+addr, addr, opt)
+		key = fmt.Sprintf("%s%s/%s", prefix, FlagTCPHost, addr)
 	}
 	if serverInstance.rpcsvr != nil {
 		addr, _ := fixHostIp(*confRPCHost)
-		response, err = kapi.Set(context.Background(), prefix+FlagRPCHost+"/"+addr, addr, opt)
+		key = fmt.Sprintf("%s%s/%s", prefix, FlagTCPHost, addr)
 	}
 	if serverInstance.udpsvr != nil {
 		addr := os.Getenv(EnvUDPProxy)
 		if addr == "" {
 			addr, _ = fixHostIp(*confUDPHost)
 		}
-		response, err = kapi.Set(context.Background(), prefix+FlagUDPHost+"/"+addr, addr, opt)
+		key = fmt.Sprintf("%s%s/%s", prefix, FlagUDPHost, addr)
 	}
 	if serverInstance.httpsvr != nil {
 		addr := os.Getenv(EnvHTTPProxy)
 		if addr == "" {
 			addr, _ = fixHostIp(*confHTTPHost)
 		}
-		response, err = kapi.Set(context.Background(), prefix+FlagHTTPHost+"/"+addr, addr, opt)
+		key = fmt.Sprintf("%s%s/%s", prefix, FlagHTTPHost, addr)
 	}
+	_, err = mgr.cli.Put(ctx, key, addr, clientv3.WithLease(resp.ID))
+	if err != nil {
+		return nil
+	}
+	mgr.leaseId = resp.ID
+	leaseRespChan, err := mgr.cli.KeepAlive(ctx, resp.ID)
 	if err != nil {
 		return err
 	}
+	mgr.keepAliveChan = leaseRespChan
 	// print common key info
-	Log.Infof("RegisterServer is done. Metadata is %v\n", response)
-
+	Log.Infof("RegisterServer is done. leaseId is %v\n", mgr.leaseId)
+	go func() {
+		for leaseResp := range mgr.keepAliveChan {
+			Log.Infof("update lease success:%d", leaseResp.ID)
+		}
+	}()
 	return nil
 }
 
@@ -102,52 +127,23 @@ func (mgr *ServerManager) UpdateServerHosts() error {
 		return errorf(errServerNotInit)
 	}
 
-	cfg := client.Config{
-		Endpoints: mgr.etcdHosts,
-		Transport: client.DefaultTransport,
-		// set timeout per request to fail fast when the target endpoint is unavailable
-		HeaderTimeoutPerRequest: time.Second,
-	}
-	c, err := client.New(cfg)
-	if err != nil {
-		return err
-	}
-
-	kapi := client.NewKeysAPI(c)
 	prefix := EtcdServersPrefix
-	opt := &client.GetOptions{Recursive: true}
-	response, err := kapi.Get(context.Background(), prefix, opt)
+	response, err := mgr.cli.Get(context.Background(), prefix, clientv3.WithPrefix())
 	if err != nil {
 		return err
 	}
 
 	servers := make(map[string](map[string][]string))
 
-	root := response.Node
-	if root.Dir != true {
-		return errorf(errWrongEtcdPath, root.Key)
-	}
-	for _, server := range root.Nodes {
-		if server.Dir != true {
-			return errorf(errWrongEtcdPath, server.Key)
-		}
-		name := strings.Split(server.Key, "/")[EtcdServersPrefixCnt+1]
+	for _, server := range response.Kvs {
+		name := strings.Split(string(server.Key), "/")[EtcdServersPrefixCnt + 1]
 		servers[name] = make(map[string][]string)
-		for _, hosttype := range server.Nodes {
-			if hosttype.Dir != true {
-				return errorf(errWrongEtcdPath, hosttype.Key)
-			}
-			host := strings.Split(hosttype.Key, "/")[EtcdServersPrefixCnt+2]
-			servers[name][host] = []string{}
-			for _, hostaddr := range hosttype.Nodes {
-				addr := strings.Split(hostaddr.Key, "/")[EtcdServersPrefixCnt+3]
-				servers[name][host] = append(servers[name][host], addr)
-			}
-		}
+		host := strings.Split(string(server.Key), "/")[EtcdServersPrefixCnt + 2]
+		servers[name][host] = []string{}
+		addr := strings.Split(string(server.Key), "/")[EtcdServersPrefixCnt+3]
+		servers[name][host] = append(servers[name][host], addr)
 	}
-
 	mgr.mapServers = servers
-
 	Log.Infof("UpdateServerHosts is done: %v", mgr.mapServers)
 	return nil
 

+ 9 - 9
run.sh

@@ -4,16 +4,16 @@ sudo killall -9 httpaccess registry apiprovider devicemanager controller mqttacc
 
 # start services
 #$GOPATH/bin/httpaccess -etcd http://localhost:2379 -httphost internal:443 -loglevel debug -usehttps -keyfile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/key.pem -cafile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/cert.pem &
-$GOPATH/bin/httpaccess -etcd http://192.168.175.60:2379 -httphost internal:8088 -loglevel debug &
-$GOPATH/bin/registry -etcd http://192.168.175.60:2379 -rpchost localhost:20034 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP -dbhost 192.168.175.60 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -loglevel debug &
-$GOPATH/bin/apiprovider -etcd http://192.168.175.60:2379 -loglevel debug  -httphost localhost:8888 &
-$GOPATH/bin/devicemanager -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20033 &
-$GOPATH/bin/controller -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20032 &
+$GOPATH/bin/httpaccess -etcd http://127.0.0.1:2379 -httphost internal:8088 -loglevel debug &
+$GOPATH/bin/registry -etcd http://127.0.0.1:2379 -rpchost localhost:20034 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP -dbhost 127.0.0.1 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -loglevel debug &
+$GOPATH/bin/apiprovider -etcd http://127.0.0.1:2379 -loglevel debug  -httphost localhost:8888 &
+$GOPATH/bin/devicemanager -etcd http://127.0.0.1:2379 -loglevel debug  -rpchost localhost:20033 &
+$GOPATH/bin/controller -etcd http://127.0.0.1:2379 -loglevel debug  -rpchost localhost:20032 &
 #$GOPATH/bin/mqttaccess -etcd http://localhost:2379 -loglevel debug  -rpchost localhost:20030 -tcphost internal:1883 -usetls -keyfile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/key.pem -cafile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/cert.pem &
-$GOPATH/bin/mqttaccess -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20030 -tcphost internal:1883  &
-$GOPATH/bin/knowoapi -etcd http://192.168.175.60:2379 -loglevel debug  -httphost localhost:8889 -dbhost 192.168.175.60 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP &
-$GOPATH/bin/fileaccess -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20035 -httphost localhost:9000 &
-$GOPATH/bin/coapaccess -etcd http://192.168.175.60:2379 -loglevel debug  -udphost localhost:56883 &
+$GOPATH/bin/mqttaccess -etcd http://127.0.0.1:2379 -loglevel debug  -rpchost localhost:20030 -tcphost internal:1883  &
+$GOPATH/bin/knowoapi -etcd http://127.0.0.1:2379 -loglevel debug  -httphost localhost:8889 -dbhost 127.0.0.1 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP &
+$GOPATH/bin/fileaccess -etcd http://127.0.0.1:2379 -loglevel debug  -rpchost localhost:20035 -httphost localhost:9000 &
+$GOPATH/bin/coapaccess -etcd http://127.0.0.1:2379 -loglevel debug  -udphost localhost:56883 &
 exit 0
 
 

+ 55 - 0
vendor/github.com/clbanning/mxj/LICENSE

@@ -0,0 +1,55 @@
+Copyright (c) 2012-2019 Charles Banning <clbanning@gmail.com>.  All rights reserved.
+
+The MIT License (MIT)
+
+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.
+
+===================== for some Go code used in test case ======================
+
+Go Language Copyright & License - 
+
+Copyright 2009 The Go Authors. All rights reserved.
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 201 - 0
vendor/github.com/clbanning/mxj/anyxml.go

@@ -0,0 +1,201 @@
+package mxj
+
+import (
+	"bytes"
+	"encoding/xml"
+	"reflect"
+)
+
+const (
+	DefaultElementTag = "element"
+)
+
+// Encode arbitrary value as XML.
+//
+// Note: unmarshaling the resultant
+// XML may not return the original value, since tag labels may have been injected
+// to create the XML representation of the value.
+/*
+ Encode an arbitrary JSON object.
+	package main
+
+	import (
+		"encoding/json"
+		"fmt"
+		"github.com/clbanning/mxj"
+	)
+
+	func main() {
+		jsondata := []byte(`[
+			{ "somekey":"somevalue" },
+			"string",
+			3.14159265,
+			true
+		]`)
+		var i interface{}
+		err := json.Unmarshal(jsondata, &i)
+		if err != nil {
+			// do something
+		}
+		x, err := mxj.AnyXmlIndent(i, "", "  ", "mydoc")
+		if err != nil {
+			// do something else
+		}
+		fmt.Println(string(x))
+	}
+
+	output:
+		<mydoc>
+		  <somekey>somevalue</somekey>
+		  <element>string</element>
+		  <element>3.14159265</element>
+		  <element>true</element>
+		</mydoc>
+
+An extreme example is available in examples/goofy_map.go.
+*/
+// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
+// AnyXml( v, myRootTag, myElementTag).
+func AnyXml(v interface{}, tags ...string) ([]byte, error) {
+	var rt, et string
+	if len(tags) == 1 || len(tags) == 2 {
+		rt = tags[0]
+	} else {
+		rt = DefaultRootTag
+	}
+	if len(tags) == 2 {
+		et = tags[1]
+	} else {
+		et = DefaultElementTag
+	}
+
+	if v == nil {
+		if useGoXmlEmptyElemSyntax {
+			return []byte("<" + rt + "></" + rt + ">"), nil
+		}
+		return []byte("<" + rt + "/>"), nil
+	}
+	if reflect.TypeOf(v).Kind() == reflect.Struct {
+		return xml.Marshal(v)
+	}
+
+	var err error
+	s := new(bytes.Buffer)
+	p := new(pretty)
+
+	var b []byte
+	switch v.(type) {
+	case []interface{}:
+		if _, err = s.WriteString("<" + rt + ">"); err != nil {
+			return nil, err
+		}
+		for _, vv := range v.([]interface{}) {
+			switch vv.(type) {
+			case map[string]interface{}:
+				m := vv.(map[string]interface{})
+				if len(m) == 1 {
+					for tag, val := range m {
+						err = marshalMapToXmlIndent(false, s, tag, val, p)
+					}
+				} else {
+					err = marshalMapToXmlIndent(false, s, et, vv, p)
+				}
+			default:
+				err = marshalMapToXmlIndent(false, s, et, vv, p)
+			}
+			if err != nil {
+				break
+			}
+		}
+		if _, err = s.WriteString("</" + rt + ">"); err != nil {
+			return nil, err
+		}
+		b = s.Bytes()
+	case map[string]interface{}:
+		m := Map(v.(map[string]interface{}))
+		b, err = m.Xml(rt)
+	default:
+		err = marshalMapToXmlIndent(false, s, rt, v, p)
+		b = s.Bytes()
+	}
+
+	return b, err
+}
+
+// Encode an arbitrary value as a pretty XML string.
+// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
+// AnyXmlIndent( v, "", "  ", myRootTag, myElementTag).
+func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) {
+	var rt, et string
+	if len(tags) == 1 || len(tags) == 2 {
+		rt = tags[0]
+	} else {
+		rt = DefaultRootTag
+	}
+	if len(tags) == 2 {
+		et = tags[1]
+	} else {
+		et = DefaultElementTag
+	}
+
+	if v == nil {
+		if useGoXmlEmptyElemSyntax {
+			return []byte(prefix + "<" + rt + "></" + rt + ">"), nil
+		}
+		return []byte(prefix + "<" + rt + "/>"), nil
+	}
+	if reflect.TypeOf(v).Kind() == reflect.Struct {
+		return xml.MarshalIndent(v, prefix, indent)
+	}
+
+	var err error
+	s := new(bytes.Buffer)
+	p := new(pretty)
+	p.indent = indent
+	p.padding = prefix
+
+	var b []byte
+	switch v.(type) {
+	case []interface{}:
+		if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
+			return nil, err
+		}
+		p.Indent()
+		for _, vv := range v.([]interface{}) {
+			switch vv.(type) {
+			case map[string]interface{}:
+				m := vv.(map[string]interface{})
+				if len(m) == 1 {
+					for tag, val := range m {
+						err = marshalMapToXmlIndent(true, s, tag, val, p)
+					}
+				} else {
+					p.start = 1 // we 1 tag in
+					err = marshalMapToXmlIndent(true, s, et, vv, p)
+					// *s += "\n"
+					if _, err = s.WriteString("\n"); err != nil {
+						return nil, err
+					}
+				}
+			default:
+				p.start = 0 // in case trailing p.start = 1
+				err = marshalMapToXmlIndent(true, s, et, vv, p)
+			}
+			if err != nil {
+				break
+			}
+		}
+		if _, err = s.WriteString(`</` + rt + `>`); err != nil {
+			return nil, err
+		}
+		b = s.Bytes()
+	case map[string]interface{}:
+		m := Map(v.(map[string]interface{}))
+		b, err = m.XmlIndent(prefix, indent, rt)
+	default:
+		err = marshalMapToXmlIndent(true, s, rt, v, p)
+		b = s.Bytes()
+	}
+
+	return b, err
+}

+ 54 - 0
vendor/github.com/clbanning/mxj/atomFeedString.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld&lt;&gt;</name></author><entry><title>rietveld: an attempt at pubsubhubbub
+</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
+  An attempt at adding pubsubhubbub support to Rietveld.
+http://code.google.com/p/pubsubhubbub
+http://code.google.com/p/rietveld/issues/detail?id=155
+
+The server side of the protocol is trivial:
+  1. add a &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;gt; tag to all
+     feeds that will be pubsubhubbubbed.
+  2. every time one of those feeds changes, tell the hub
+     with a simple POST request.
+
+I have tested this by adding debug prints to a local hub
+server and checking that the server got the right publish
+requests.
+
+I can&amp;#39;t quite get the server to work, but I think the bug
+is not in my code.  I think that the server expects to be
+able to grab the feed and see the feed&amp;#39;s actual URL in
+the link rel=&amp;quot;self&amp;quot;, but the default value for that drops
+the :port from the URL, and I cannot for the life of me
+figure out how to get the Atom generator deep inside
+django not to do that, or even where it is doing that,
+or even what code is running to generate the Atom feed.
+(I thought I knew but I added some assert False statements
+and it kept running!)
+
+Ignoring that particular problem, I would appreciate
+feedback on the right way to get the two values at
+the top of feeds.py marked NOTE(rsc).
+
+
+</summary></entry><entry><title>rietveld: correct tab handling
+</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
+  This fixes the buggy tab rendering that can be seen at
+http://codereview.appspot.com/116075/diff/1/2
+
+The fundamental problem was that the tab code was
+not being told what column the text began in, so it
+didn&amp;#39;t know where to put the tab stops.  Another problem
+was that some of the code assumed that string byte
+offsets were the same as column offsets, which is only
+true if there are no tabs.
+
+In the process of fixing this, I cleaned up the arguments
+to Fold and ExpandTabs and renamed them Break and
+_ExpandTabs so that I could be sure that I found all the
+call sites.  I also wanted to verify that ExpandTabs was
+not being used from outside intra_region_diff.py.
+
+
+</summary></entry></feed> 	   `
+

+ 138 - 0
vendor/github.com/clbanning/mxj/doc.go

@@ -0,0 +1,138 @@
+// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
+// Copyright 2012-2019, Charles Banning. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file
+
+/*
+Marshal/Unmarshal XML to/from map[string]interface{} values (and JSON); extract/modify values from maps by key or key-path, including wildcards.
+
+mxj supplants the legacy x2j and j2x packages. The subpackage x2j-wrapper is provided to facilitate migrating from the x2j package.  The x2j and j2x subpackages provide similar functionality of the old packages but are not function-name compatible with them.
+
+Note: this library was designed for processing ad hoc anonymous messages.  Bulk processing large data sets may be much more efficiently performed using the encoding/xml or encoding/json packages from Go's standard library directly.
+
+Related Packages:
+	checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
+
+Notes:
+	2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
+	2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
+	2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
+	2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>].
+	2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
+	2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
+	2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
+	2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
+	2017.02.21: github.com/clbanning/checkxml provides functions for validating XML data.
+	2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
+	2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
+	2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
+	2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
+	2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
+	2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
+	2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
+	            To cast them to float64, first set flag with CastNanInf(true).
+	2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
+	2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
+	2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
+	2015-12-02: NewMapXmlSeq() with mv.XmlSeq() & co. will try to preserve structure of XML doc when re-encoding.
+	2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
+
+SUMMARY
+
+   type Map map[string]interface{}
+
+   Create a Map value, 'mv', from any map[string]interface{} value, 'v':
+      mv := Map(v)
+
+   Unmarshal / marshal XML as a Map value, 'mv':
+      mv, err := NewMapXml(xmlValue) // unmarshal
+      xmlValue, err := mv.Xml()      // marshal
+
+   Unmarshal XML from an io.Reader as a Map value, 'mv':
+      mv, err := NewMapXmlReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream
+      mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded
+
+   Marshal Map value, 'mv', to an XML Writer (io.Writer):
+      err := mv.XmlWriter(xmlWriter)
+      raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter
+
+   Also, for prettified output:
+      xmlValue, err := mv.XmlIndent(prefix, indent, ...)
+      err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...)
+      raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)
+
+   Bulk process XML with error handling (note: handlers must return a boolean value):
+      err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
+      err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))
+
+   Converting XML to JSON: see Examples for NewMapXml and HandleXmlReader.
+
+   There are comparable functions and methods for JSON processing.
+
+   Arbitrary structure values can be decoded to / encoded from Map values:
+      mv, err := NewMapStruct(structVal)
+      err := mv.Struct(structPointer)
+
+   To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
+   or structure to a Map value, 'mv', or cast a map[string]interface{} value to a Map value, 'mv', then:
+      paths := mv.PathsForKey(key)
+      path := mv.PathForKeyShortest(key)
+      values, err := mv.ValuesForKey(key, subkeys)
+      values, err := mv.ValuesForPath(path, subkeys) // 'path' can be dot-notation with wildcards and indexed arrays.
+      count, err := mv.UpdateValuesForPath(newVal, path, subkeys)
+
+   Get everything at once, irrespective of path depth:
+      leafnodes := mv.LeafNodes()
+      leafvalues := mv.LeafValues()
+
+   A new Map with whatever keys are desired can be created from the current Map and then encoded in XML
+   or JSON. (Note: keys can use dot-notation. 'oldKey' can also use wildcards and indexed arrays.)
+      newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
+      newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
+      newXml, err := newMap.Xml()   // for example
+      newJson, err := newMap.Json() // ditto
+
+XML PARSING CONVENTIONS
+
+   Using NewMapXml()
+
+   - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
+     to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
+     `SetAttrPrefix()`.)
+   - If the element is a simple element and has attributes, the element value
+     is given the key `#text` for its `map[string]interface{}` representation.  (See
+     the 'atomFeedString.xml' test data, below.)
+   - XML comments, directives, and process instructions are ignored.
+   - If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
+
+   Using NewMapXmlSeq()
+
+   - Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values
+     where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the 
+     value for `<attr_label>`.
+   - All elements, except for the root, have a "#seq" key.
+   - Comments, directives, and process instructions are unmarshalled into the Map using the
+     keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
+     specifics.)
+   - Name space syntax is preserved: 
+      - <ns:key>something</ns.key> parses to map["ns:key"]interface{}{"something"}
+      - xmlns:ns="http://myns.com/ns" parses to map["xmlns:ns"]interface{}{"http://myns.com/ns"}
+
+   Both
+
+   - By default, "Nan", "Inf", and "-Inf" values are not cast to float64.  If you want them
+     to be cast, set a flag to cast them  using CastNanInf(true).
+
+XML ENCODING CONVENTIONS
+   
+   - 'nil' Map values, which may represent 'null' JSON values, are encoded as "<tag/>".
+     NOTE: the operation is not symmetric as "<tag/>" elements are decoded as 'tag:""' Map values,
+           which, then, encode in JSON as '"tag":""' values..
+   - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one.  (Go
+           randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
+           Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
+           mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
+           working with the Map representation.
+
+*/
+package mxj

+ 93 - 0
vendor/github.com/clbanning/mxj/escapechars.go

@@ -0,0 +1,93 @@
+// Copyright 2016 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+import (
+	"bytes"
+)
+
+var xmlEscapeChars bool
+
+// XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
+// NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
+// then '&amp;' will be re-escaped as '&amp;amp;'.
+//
+/*
+	The values are:
+	"   &quot;
+	'   &apos;
+	<   &lt;
+	>   &gt;
+	&   &amp;
+*/
+//
+// Note: if XMLEscapeCharsDecoder(true) has been called - or the default, 'false,' value
+// has been toggled to 'true' - then XMLEscapeChars(true) is ignored.  If XMLEscapeChars(true)
+// has already been called before XMLEscapeCharsDecoder(true), XMLEscapeChars(false) is called
+// to turn escape encoding on mv.Xml, etc., to prevent double escaping ampersands, '&'.
+func XMLEscapeChars(b ...bool) {
+	var bb bool
+	if len(b) == 0 {
+		bb = !xmlEscapeChars
+	} else {
+		bb = b[0]
+	}
+	if bb == true && xmlEscapeCharsDecoder == false {
+		xmlEscapeChars = true
+	} else {
+		xmlEscapeChars = false
+	}
+}
+
+// Scan for '&' first, since 's' may contain "&amp;" that is parsed to "&amp;amp;"
+// - or "&lt;" that is parsed to "&amp;lt;".
+var escapechars = [][2][]byte{
+	{[]byte(`&`), []byte(`&amp;`)},
+	{[]byte(`<`), []byte(`&lt;`)},
+	{[]byte(`>`), []byte(`&gt;`)},
+	{[]byte(`"`), []byte(`&quot;`)},
+	{[]byte(`'`), []byte(`&apos;`)},
+}
+
+func escapeChars(s string) string {
+	if len(s) == 0 {
+		return s
+	}
+
+	b := []byte(s)
+	for _, v := range escapechars {
+		n := bytes.Count(b, v[0])
+		if n == 0 {
+			continue
+		}
+		b = bytes.Replace(b, v[0], v[1], n)
+	}
+	return string(b)
+}
+
+// per issue #84, escape CharData values from xml.Decoder
+
+var xmlEscapeCharsDecoder bool
+
+// XMLEscapeCharsDecoder(b ...bool) escapes XML characters in xml.CharData values
+// returned by Decoder.Token.  Thus, the internal Map values will contain escaped
+// values, and you do not need to set XMLEscapeChars for proper encoding.
+//
+// By default, the Map values have the non-escaped values returned by Decoder.Token.
+// XMLEscapeCharsDecoder(true) - or, XMLEscapeCharsDecoder() - will toggle escape
+// encoding 'on.'
+//
+// Note: if XMLEscapeCharDecoder(true) is call then XMLEscapeChars(false) is
+// called to prevent re-escaping the values on encoding using mv.Xml, etc.
+func XMLEscapeCharsDecoder(b ...bool) {
+	if len(b) == 0 {
+		xmlEscapeCharsDecoder = !xmlEscapeCharsDecoder
+	} else {
+		xmlEscapeCharsDecoder = b[0]
+	}
+	if xmlEscapeCharsDecoder == true && xmlEscapeChars == true {
+		xmlEscapeChars = false
+	}
+}

+ 9 - 0
vendor/github.com/clbanning/mxj/exists.go

@@ -0,0 +1,9 @@
+package mxj
+
+// Checks whether the path exists. If err != nil then 'false' is returned
+// along with the error encountered parsing either the "path" or "subkeys"
+// argument.
+func (mv Map) Exists(path string, subkeys ...string) (bool, error) {
+	v, err := mv.ValuesForPath(path, subkeys...)
+	return (err == nil && len(v) > 0), err
+}

+ 287 - 0
vendor/github.com/clbanning/mxj/files.go

@@ -0,0 +1,287 @@
+package mxj
+
+import (
+	"fmt"
+	"io"
+	"os"
+)
+
+type Maps []Map
+
+func NewMaps() Maps {
+	return make(Maps, 0)
+}
+
+type MapRaw struct {
+	M Map
+	R []byte
+}
+
+// NewMapsFromXmlFile - creates an array from a file of JSON values.
+func NewMapsFromJsonFile(name string) (Maps, error) {
+	fi, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	if !fi.Mode().IsRegular() {
+		return nil, fmt.Errorf("file %s is not a regular file", name)
+	}
+
+	fh, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer fh.Close()
+
+	am := make([]Map, 0)
+	for {
+		m, raw, err := NewMapJsonReaderRaw(fh)
+		if err != nil && err != io.EOF {
+			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
+		}
+		if len(m) > 0 {
+			am = append(am, m)
+		}
+		if err == io.EOF {
+			break
+		}
+	}
+	return am, nil
+}
+
+// ReadMapsFromJsonFileRaw - creates an array of MapRaw from a file of JSON values.
+func NewMapsFromJsonFileRaw(name string) ([]MapRaw, error) {
+	fi, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	if !fi.Mode().IsRegular() {
+		return nil, fmt.Errorf("file %s is not a regular file", name)
+	}
+
+	fh, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer fh.Close()
+
+	am := make([]MapRaw, 0)
+	for {
+		mr := new(MapRaw)
+		mr.M, mr.R, err = NewMapJsonReaderRaw(fh)
+		if err != nil && err != io.EOF {
+			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
+		}
+		if len(mr.M) > 0 {
+			am = append(am, *mr)
+		}
+		if err == io.EOF {
+			break
+		}
+	}
+	return am, nil
+}
+
+// NewMapsFromXmlFile - creates an array from a file of XML values.
+func NewMapsFromXmlFile(name string) (Maps, error) {
+	fi, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	if !fi.Mode().IsRegular() {
+		return nil, fmt.Errorf("file %s is not a regular file", name)
+	}
+
+	fh, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer fh.Close()
+
+	am := make([]Map, 0)
+	for {
+		m, raw, err := NewMapXmlReaderRaw(fh)
+		if err != nil && err != io.EOF {
+			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
+		}
+		if len(m) > 0 {
+			am = append(am, m)
+		}
+		if err == io.EOF {
+			break
+		}
+	}
+	return am, nil
+}
+
+// NewMapsFromXmlFileRaw - creates an array of MapRaw from a file of XML values.
+// NOTE: the slice with the raw XML is clean with no extra capacity - unlike NewMapXmlReaderRaw().
+// It is slow at parsing a file from disk and is intended for relatively small utility files.
+func NewMapsFromXmlFileRaw(name string) ([]MapRaw, error) {
+	fi, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	if !fi.Mode().IsRegular() {
+		return nil, fmt.Errorf("file %s is not a regular file", name)
+	}
+
+	fh, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer fh.Close()
+
+	am := make([]MapRaw, 0)
+	for {
+		mr := new(MapRaw)
+		mr.M, mr.R, err = NewMapXmlReaderRaw(fh)
+		if err != nil && err != io.EOF {
+			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
+		}
+		if len(mr.M) > 0 {
+			am = append(am, *mr)
+		}
+		if err == io.EOF {
+			break
+		}
+	}
+	return am, nil
+}
+
+// ------------------------ Maps writing -------------------------
+// These are handy-dandy methods for dumping configuration data, etc.
+
+// JsonString - analogous to mv.Json()
+func (mvs Maps) JsonString(safeEncoding ...bool) (string, error) {
+	var s string
+	for _, v := range mvs {
+		j, err := v.Json()
+		if err != nil {
+			return s, err
+		}
+		s += string(j)
+	}
+	return s, nil
+}
+
+// JsonStringIndent - analogous to mv.JsonIndent()
+func (mvs Maps) JsonStringIndent(prefix, indent string, safeEncoding ...bool) (string, error) {
+	var s string
+	var haveFirst bool
+	for _, v := range mvs {
+		j, err := v.JsonIndent(prefix, indent)
+		if err != nil {
+			return s, err
+		}
+		if haveFirst {
+			s += "\n"
+		} else {
+			haveFirst = true
+		}
+		s += string(j)
+	}
+	return s, nil
+}
+
+// XmlString - analogous to mv.Xml()
+func (mvs Maps) XmlString() (string, error) {
+	var s string
+	for _, v := range mvs {
+		x, err := v.Xml()
+		if err != nil {
+			return s, err
+		}
+		s += string(x)
+	}
+	return s, nil
+}
+
+// XmlStringIndent - analogous to mv.XmlIndent()
+func (mvs Maps) XmlStringIndent(prefix, indent string) (string, error) {
+	var s string
+	for _, v := range mvs {
+		x, err := v.XmlIndent(prefix, indent)
+		if err != nil {
+			return s, err
+		}
+		s += string(x)
+	}
+	return s, nil
+}
+
+// JsonFile - write Maps to named file as JSON
+// Note: the file will be created, if necessary; if it exists it will be truncated.
+// If you need to append to a file, open it and use JsonWriter method.
+func (mvs Maps) JsonFile(file string, safeEncoding ...bool) error {
+	var encoding bool
+	if len(safeEncoding) == 1 {
+		encoding = safeEncoding[0]
+	}
+	s, err := mvs.JsonString(encoding)
+	if err != nil {
+		return err
+	}
+	fh, err := os.Create(file)
+	if err != nil {
+		return err
+	}
+	defer fh.Close()
+	fh.WriteString(s)
+	return nil
+}
+
+// JsonFileIndent - write Maps to named file as pretty JSON
+// Note: the file will be created, if necessary; if it exists it will be truncated.
+// If you need to append to a file, open it and use JsonIndentWriter method.
+func (mvs Maps) JsonFileIndent(file, prefix, indent string, safeEncoding ...bool) error {
+	var encoding bool
+	if len(safeEncoding) == 1 {
+		encoding = safeEncoding[0]
+	}
+	s, err := mvs.JsonStringIndent(prefix, indent, encoding)
+	if err != nil {
+		return err
+	}
+	fh, err := os.Create(file)
+	if err != nil {
+		return err
+	}
+	defer fh.Close()
+	fh.WriteString(s)
+	return nil
+}
+
+// XmlFile - write Maps to named file as XML
+// Note: the file will be created, if necessary; if it exists it will be truncated.
+// If you need to append to a file, open it and use XmlWriter method.
+func (mvs Maps) XmlFile(file string) error {
+	s, err := mvs.XmlString()
+	if err != nil {
+		return err
+	}
+	fh, err := os.Create(file)
+	if err != nil {
+		return err
+	}
+	defer fh.Close()
+	fh.WriteString(s)
+	return nil
+}
+
+// XmlFileIndent - write Maps to named file as pretty XML
+// Note: the file will be created,if necessary; if it exists it will be truncated.
+// If you need to append to a file, open it and use XmlIndentWriter method.
+func (mvs Maps) XmlFileIndent(file, prefix, indent string) error {
+	s, err := mvs.XmlStringIndent(prefix, indent)
+	if err != nil {
+		return err
+	}
+	fh, err := os.Create(file)
+	if err != nil {
+		return err
+	}
+	defer fh.Close()
+	fh.WriteString(s)
+	return nil
+}

+ 2 - 0
vendor/github.com/clbanning/mxj/files_test.badjson

@@ -0,0 +1,2 @@
+{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
+{ "with":"some", "bad":JSON, "in":"it" }

+ 9 - 0
vendor/github.com/clbanning/mxj/files_test.badxml

@@ -0,0 +1,9 @@
+<doc>
+	<some>test</some>
+	<data>for files.go</data>
+</doc>
+<msg>
+	<just>some</just>
+	<another>doc</other>
+	<for>test case</for>
+</msg>

+ 2 - 0
vendor/github.com/clbanning/mxj/files_test.json

@@ -0,0 +1,2 @@
+{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
+{ "with":"just", "two":2, "JSON":"values", "true":true }

+ 9 - 0
vendor/github.com/clbanning/mxj/files_test.xml

@@ -0,0 +1,9 @@
+<doc>
+	<some>test</some>
+	<data>for files.go</data>
+</doc>
+<msg>
+	<just>some</just>
+	<another>doc</another>
+	<for>test case</for>
+</msg>

+ 1 - 0
vendor/github.com/clbanning/mxj/files_test_dup.json

@@ -0,0 +1 @@
+{"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"}

+ 1 - 0
vendor/github.com/clbanning/mxj/files_test_dup.xml

@@ -0,0 +1 @@
+<doc><data>for files.go</data><some>test</some></doc><msg><another>doc</another><for>test case</for><just>some</just></msg>

+ 12 - 0
vendor/github.com/clbanning/mxj/files_test_indent.json

@@ -0,0 +1,12 @@
+{
+  "a": "test",
+  "file": "for",
+  "files_test.go": "case",
+  "this": "is"
+}
+{
+  "JSON": "values",
+  "true": true,
+  "two": 2,
+  "with": "just"
+}

+ 8 - 0
vendor/github.com/clbanning/mxj/files_test_indent.xml

@@ -0,0 +1,8 @@
+<doc>
+  <data>for files.go</data>
+  <some>test</some>
+</doc><msg>
+  <another>doc</another>
+  <for>test case</for>
+  <just>some</just>
+</msg>

+ 3 - 0
vendor/github.com/clbanning/mxj/go.mod

@@ -0,0 +1,3 @@
+module github.com/clbanning/mxj/v2
+
+go 1.15

+ 35 - 0
vendor/github.com/clbanning/mxj/gob.go

@@ -0,0 +1,35 @@
+// gob.go - Encode/Decode a Map into a gob object.
+
+package mxj
+
+import (
+	"bytes"
+	"encoding/gob"
+)
+
+// NewMapGob returns a Map value for a gob object that has been
+// encoded from a map[string]interface{} (or compatible type) value.
+// It is intended to provide symmetric handling of Maps that have
+// been encoded using mv.Gob.
+func NewMapGob(gobj []byte) (Map, error) {
+	m := make(map[string]interface{}, 0)
+	if len(gobj) == 0 {
+		return m, nil
+	}
+	r := bytes.NewReader(gobj)
+	dec := gob.NewDecoder(r)
+	if err := dec.Decode(&m); err != nil {
+		return m, err
+	}
+	return m, nil
+}
+
+// Gob returns a gob-encoded value for the Map 'mv'.
+func (mv Map) Gob() ([]byte, error) {
+	var buf bytes.Buffer
+	enc := gob.NewEncoder(&buf)
+	if err := enc.Encode(map[string]interface{}(mv)); err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
+}

+ 323 - 0
vendor/github.com/clbanning/mxj/json.go

@@ -0,0 +1,323 @@
+// Copyright 2012-2014 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"time"
+)
+
+// ------------------------------ write JSON -----------------------
+
+// Just a wrapper on json.Marshal.
+// If option safeEncoding is'true' then safe encoding of '<', '>' and '&'
+// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
+func (mv Map) Json(safeEncoding ...bool) ([]byte, error) {
+	var s bool
+	if len(safeEncoding) == 1 {
+		s = safeEncoding[0]
+	}
+
+	b, err := json.Marshal(mv)
+
+	if !s {
+		b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
+		b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
+		b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
+	}
+	return b, err
+}
+
+// Just a wrapper on json.MarshalIndent.
+// If option safeEncoding is'true' then safe encoding of '<' , '>' and '&'
+// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
+func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error) {
+	var s bool
+	if len(safeEncoding) == 1 {
+		s = safeEncoding[0]
+	}
+
+	b, err := json.MarshalIndent(mv, prefix, indent)
+	if !s {
+		b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
+		b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
+		b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
+	}
+	return b, err
+}
+
+// The following implementation is provided for symmetry with NewMapJsonReader[Raw]
+// The names will also provide a key for the number of return arguments.
+
+// Writes the Map as JSON on the Writer.
+// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
+func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error {
+	b, err := mv.Json(safeEncoding...)
+	if err != nil {
+		return err
+	}
+
+	_, err = jsonWriter.Write(b)
+	return err
+}
+
+// Writes the Map as JSON on the Writer. []byte is the raw JSON that was written.
+// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
+func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error) {
+	b, err := mv.Json(safeEncoding...)
+	if err != nil {
+		return b, err
+	}
+
+	_, err = jsonWriter.Write(b)
+	return b, err
+}
+
+// Writes the Map as pretty JSON on the Writer.
+// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
+func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error {
+	b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
+	if err != nil {
+		return err
+	}
+
+	_, err = jsonWriter.Write(b)
+	return err
+}
+
+// Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written.
+// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
+func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error) {
+	b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
+	if err != nil {
+		return b, err
+	}
+
+	_, err = jsonWriter.Write(b)
+	return b, err
+}
+
+// --------------------------- read JSON -----------------------------
+
+// Decode numericvalues as json.Number type Map values - see encoding/json#Number.
+// NOTE: this is for decoding JSON into a Map with NewMapJson(), NewMapJsonReader(), 
+// etc.; it does not affect NewMapXml(), etc.  The XML encoders mv.Xml() and mv.XmlIndent()
+// do recognize json.Number types; a JSON object can be decoded to a Map with json.Number
+// value types and the resulting Map can be correctly encoded into a XML object.
+var JsonUseNumber bool
+
+// Just a wrapper on json.Unmarshal
+//	Converting JSON to XML is a simple as:
+//		...
+//		mapVal, merr := mxj.NewMapJson(jsonVal)
+//		if merr != nil {
+//			// handle error
+//		}
+//		xmlVal, xerr := mapVal.Xml()
+//		if xerr != nil {
+//			// handle error
+//		}
+// NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}],
+// will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map.
+// See mxj/j2x/j2x_test.go.
+func NewMapJson(jsonVal []byte) (Map, error) {
+	// empty or nil begets empty
+	if len(jsonVal) == 0 {
+		m := make(map[string]interface{}, 0)
+		return m, nil
+	}
+	// handle a goofy case ...
+	if jsonVal[0] == '[' {
+		jsonVal = []byte(`{"object":` + string(jsonVal) + `}`)
+	}
+	m := make(map[string]interface{})
+	// err := json.Unmarshal(jsonVal, &m)
+	buf := bytes.NewReader(jsonVal)
+	dec := json.NewDecoder(buf)
+	if JsonUseNumber {
+		dec.UseNumber()
+	}
+	err := dec.Decode(&m)
+	return m, err
+}
+
+// Retrieve a Map value from an io.Reader.
+//  NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
+//        os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
+//        value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
+//        a JSON object.
+func NewMapJsonReader(jsonReader io.Reader) (Map, error) {
+	jb, err := getJson(jsonReader)
+	if err != nil || len(*jb) == 0 {
+		return nil, err
+	}
+
+	// Unmarshal the 'presumed' JSON string
+	return NewMapJson(*jb)
+}
+
+// Retrieve a Map value and raw JSON - []byte - from an io.Reader.
+//  NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
+//        os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
+//        value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
+//        a JSON object and retrieve the raw JSON in a single call.
+func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error) {
+	jb, err := getJson(jsonReader)
+	if err != nil || len(*jb) == 0 {
+		return nil, *jb, err
+	}
+
+	// Unmarshal the 'presumed' JSON string
+	m, merr := NewMapJson(*jb)
+	return m, *jb, merr
+}
+
+// Pull the next JSON string off the stream: just read from first '{' to its closing '}'.
+// Returning a pointer to the slice saves 16 bytes - maybe unnecessary, but internal to package.
+func getJson(rdr io.Reader) (*[]byte, error) {
+	bval := make([]byte, 1)
+	jb := make([]byte, 0)
+	var inQuote, inJson bool
+	var parenCnt int
+	var previous byte
+
+	// scan the input for a matched set of {...}
+	// json.Unmarshal will handle syntax checking.
+	for {
+		_, err := rdr.Read(bval)
+		if err != nil {
+			if err == io.EOF && inJson && parenCnt > 0 {
+				return &jb, fmt.Errorf("no closing } for JSON string: %s", string(jb))
+			}
+			return &jb, err
+		}
+		switch bval[0] {
+		case '{':
+			if !inQuote {
+				parenCnt++
+				inJson = true
+			}
+		case '}':
+			if !inQuote {
+				parenCnt--
+			}
+			if parenCnt < 0 {
+				return nil, fmt.Errorf("closing } without opening {: %s", string(jb))
+			}
+		case '"':
+			if inQuote {
+				if previous == '\\' {
+					break
+				}
+				inQuote = false
+			} else {
+				inQuote = true
+			}
+		case '\n', '\r', '\t', ' ':
+			if !inQuote {
+				continue
+			}
+		}
+		if inJson {
+			jb = append(jb, bval[0])
+			if parenCnt == 0 {
+				break
+			}
+		}
+		previous = bval[0]
+	}
+
+	return &jb, nil
+}
+
+// ------------------------------- JSON Reader handler via Map values  -----------------------
+
+// Default poll delay to keep Handler from spinning on an open stream
+// like sitting on os.Stdin waiting for imput.
+var jhandlerPollInterval = time.Duration(1e6)
+
+// While unnecessary, we make HandleJsonReader() have the same signature as HandleXmlReader().
+// This avoids treating one or other as a special case and discussing the underlying stdlib logic.
+
+// Bulk process JSON using handlers that process a Map value.
+//	'rdr' is an io.Reader for the JSON (stream).
+//	'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing.
+//	'errHandler' is the error processor. Return of 'false' stops io.Reader  processing and returns the error.
+//	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+//	      This means that you can stop reading the file on error or after processing a particular message.
+//	      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
+func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
+	var n int
+	for {
+		m, merr := NewMapJsonReader(jsonReader)
+		n++
+
+		// handle error condition with errhandler
+		if merr != nil && merr != io.EOF {
+			merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
+			if ok := errHandler(merr); !ok {
+				// caused reader termination
+				return merr
+			}
+			continue
+		}
+
+		// pass to maphandler
+		if len(m) != 0 {
+			if ok := mapHandler(m); !ok {
+				break
+			}
+		} else if merr != io.EOF {
+			<-time.After(jhandlerPollInterval)
+		}
+
+		if merr == io.EOF {
+			break
+		}
+	}
+	return nil
+}
+
+// Bulk process JSON using handlers that process a Map value and the raw JSON.
+//	'rdr' is an io.Reader for the JSON (stream).
+//	'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing.
+//	'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error.
+//	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+//	      This means that you can stop reading the file on error or after processing a particular message.
+//	      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
+func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
+	var n int
+	for {
+		m, raw, merr := NewMapJsonReaderRaw(jsonReader)
+		n++
+
+		// handle error condition with errhandler
+		if merr != nil && merr != io.EOF {
+			merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
+			if ok := errHandler(merr, raw); !ok {
+				// caused reader termination
+				return merr
+			}
+			continue
+		}
+
+		// pass to maphandler
+		if len(m) != 0 {
+			if ok := mapHandler(m, raw); !ok {
+				break
+			}
+		} else if merr != io.EOF {
+			<-time.After(jhandlerPollInterval)
+		}
+
+		if merr == io.EOF {
+			break
+		}
+	}
+	return nil
+}

+ 668 - 0
vendor/github.com/clbanning/mxj/keyvalues.go

@@ -0,0 +1,668 @@
+// Copyright 2012-2014 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+//	keyvalues.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
+
+package mxj
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// ----------------------------- get everything FOR a single key -------------------------
+
+const (
+	minArraySize = 32
+)
+
+var defaultArraySize int = minArraySize
+
+// SetArraySize adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
+// This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
+// Returns the initial buffer size.
+func SetArraySize(size int) int {
+	if size > minArraySize {
+		defaultArraySize = size
+	} else {
+		defaultArraySize = minArraySize
+	}
+	return defaultArraySize
+}
+
+// ValuesForKey return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
+// On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
+//   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
+//             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
+//             - For attributes prefix the label with the attribute prefix character, by default a 
+//               hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
+//             - If the 'key' refers to a list, then "key:value" could select a list member of the list.
+//             - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
+//             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
+//               exclusion critera - e.g., "!author:William T. Gaddis".
+//             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
+func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) {
+	m := map[string]interface{}(mv)
+	var subKeyMap map[string]interface{}
+	if len(subkeys) > 0 {
+		var err error
+		subKeyMap, err = getSubKeyMap(subkeys...)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	ret := make([]interface{}, 0, defaultArraySize)
+	var cnt int
+	hasKey(m, key, &ret, &cnt, subKeyMap)
+	return ret[:cnt], nil
+}
+
+var KeyNotExistError = errors.New("Key does not exist")
+
+// ValueForKey is a wrapper on ValuesForKey.  It returns the first member of []interface{}, if any.
+// If there is no value, "nil, nil" is returned.
+func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) {
+	vals, err := mv.ValuesForKey(key, subkeys...)
+	if err != nil {
+		return nil, err
+	}
+	if len(vals) == 0 {
+		return nil, KeyNotExistError
+	}
+	return vals[0], nil
+}
+
+// hasKey - if the map 'key' exists append it to array
+//          if it doesn't do nothing except scan array and map values
+func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) {
+	// func hasKey(iv interface{}, key string, ret *[]interface{}, subkeys map[string]interface{}) {
+	switch iv.(type) {
+	case map[string]interface{}:
+		vv := iv.(map[string]interface{})
+		// see if the current value is of interest
+		if v, ok := vv[key]; ok {
+			switch v.(type) {
+			case map[string]interface{}:
+				if hasSubKeys(v, subkeys) {
+					*ret = append(*ret, v)
+					*cnt++
+				}
+			case []interface{}:
+				for _, av := range v.([]interface{}) {
+					if hasSubKeys(av, subkeys) {
+						*ret = append(*ret, av)
+						*cnt++
+					}
+				}
+			default:
+				if len(subkeys) == 0 {
+					*ret = append(*ret, v)
+					*cnt++
+				}
+			}
+		}
+
+		// wildcard case
+		if key == "*" {
+			for _, v := range vv {
+				switch v.(type) {
+				case map[string]interface{}:
+					if hasSubKeys(v, subkeys) {
+						*ret = append(*ret, v)
+						*cnt++
+					}
+				case []interface{}:
+					for _, av := range v.([]interface{}) {
+						if hasSubKeys(av, subkeys) {
+							*ret = append(*ret, av)
+							*cnt++
+						}
+					}
+				default:
+					if len(subkeys) == 0 {
+						*ret = append(*ret, v)
+						*cnt++
+					}
+				}
+			}
+		}
+
+		// scan the rest
+		for _, v := range vv {
+			hasKey(v, key, ret, cnt, subkeys)
+		}
+	case []interface{}:
+		for _, v := range iv.([]interface{}) {
+			hasKey(v, key, ret, cnt, subkeys)
+		}
+	}
+}
+
+// -----------------------  get everything for a node in the Map ---------------------------
+
+// Allow indexed arrays in "path" specification. (Request from Abhijit Kadam - abhijitk100@gmail.com.)
+// 2014.04.28 - implementation note.
+// Implemented as a wrapper of (old)ValuesForPath() because we need look-ahead logic to handle expansion
+// of wildcards and unindexed arrays.  Embedding such logic into valuesForKeyPath() would have made the
+// code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
+
+// ValuesForPatb retrieves all values for a path from the Map.  If len(returned_values) == 0, then no match.
+// On error, the returned array is 'nil'.
+//   'path' is a dot-separated path of key values.
+//          - If a node in the path is '*', then everything beyond is walked.
+//          - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" -
+//            even "*[2].*[0].field".
+//   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
+//             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
+//             - For attributes prefix the label with the attribute prefix character, by default a 
+//               hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
+//             - If the 'path' refers to a list, then "tag:value" would return member of the list.
+//             - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
+//             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
+//               exclusion critera - e.g., "!author:William T. Gaddis".
+//             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
+func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
+	// If there are no array indexes in path, use legacy ValuesForPath() logic.
+	if strings.Index(path, "[") < 0 {
+		return mv.oldValuesForPath(path, subkeys...)
+	}
+
+	var subKeyMap map[string]interface{}
+	if len(subkeys) > 0 {
+		var err error
+		subKeyMap, err = getSubKeyMap(subkeys...)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	keys, kerr := parsePath(path)
+	if kerr != nil {
+		return nil, kerr
+	}
+
+	vals, verr := valuesForArray(keys, mv)
+	if verr != nil {
+		return nil, verr // Vals may be nil, but return empty array.
+	}
+
+	// Need to handle subkeys ... only return members of vals that satisfy conditions.
+	retvals := make([]interface{}, 0)
+	for _, v := range vals {
+		if hasSubKeys(v, subKeyMap) {
+			retvals = append(retvals, v)
+		}
+	}
+	return retvals, nil
+}
+
+func valuesForArray(keys []*key, m Map) ([]interface{}, error) {
+	var tmppath string
+	var haveFirst bool
+	var vals []interface{}
+	var verr error
+
+	lastkey := len(keys) - 1
+	for i := 0; i <= lastkey; i++ {
+		if !haveFirst {
+			tmppath = keys[i].name
+			haveFirst = true
+		} else {
+			tmppath += "." + keys[i].name
+		}
+
+		// Look-ahead: explode wildcards and unindexed arrays.
+		// Need to handle un-indexed list recursively:
+		// e.g., path is "stuff.data[0]" rather than "stuff[0].data[0]".
+		// Need to treat it as "stuff[0].data[0]", "stuff[1].data[0]", ...
+		if !keys[i].isArray && i < lastkey && keys[i+1].isArray {
+			// Can't pass subkeys because we may not be at literal end of path.
+			vv, vverr := m.oldValuesForPath(tmppath)
+			if vverr != nil {
+				return nil, vverr
+			}
+			for _, v := range vv {
+				// See if we can walk the value.
+				am, ok := v.(map[string]interface{})
+				if !ok {
+					continue
+				}
+				// Work the backend.
+				nvals, nvalserr := valuesForArray(keys[i+1:], Map(am))
+				if nvalserr != nil {
+					return nil, nvalserr
+				}
+				vals = append(vals, nvals...)
+			}
+			break // have recursed the whole path - return
+		}
+
+		if keys[i].isArray || i == lastkey {
+			// Don't pass subkeys because may not be at literal end of path.
+			vals, verr = m.oldValuesForPath(tmppath)
+		} else {
+			continue
+		}
+		if verr != nil {
+			return nil, verr
+		}
+
+		if i == lastkey && !keys[i].isArray {
+			break
+		}
+
+		// Now we're looking at an array - supposedly.
+		// Is index in range of vals?
+		if len(vals) <= keys[i].position {
+			vals = nil
+			break
+		}
+
+		// Return the array member of interest, if at end of path.
+		if i == lastkey {
+			vals = vals[keys[i].position:(keys[i].position + 1)]
+			break
+		}
+
+		// Extract the array member of interest.
+		am := vals[keys[i].position:(keys[i].position + 1)]
+
+		// must be a map[string]interface{} value so we can keep walking the path
+		amm, ok := am[0].(map[string]interface{})
+		if !ok {
+			vals = nil
+			break
+		}
+
+		m = Map(amm)
+		haveFirst = false
+	}
+
+	return vals, nil
+}
+
+type key struct {
+	name     string
+	isArray  bool
+	position int
+}
+
+func parsePath(s string) ([]*key, error) {
+	keys := strings.Split(s, ".")
+
+	ret := make([]*key, 0)
+
+	for i := 0; i < len(keys); i++ {
+		if keys[i] == "" {
+			continue
+		}
+
+		newkey := new(key)
+		if strings.Index(keys[i], "[") < 0 {
+			newkey.name = keys[i]
+			ret = append(ret, newkey)
+			continue
+		}
+
+		p := strings.Split(keys[i], "[")
+		newkey.name = p[0]
+		p = strings.Split(p[1], "]")
+		if p[0] == "" { // no right bracket
+			return nil, fmt.Errorf("no right bracket on key index: %s", keys[i])
+		}
+		// convert p[0] to a int value
+		pos, nerr := strconv.ParseInt(p[0], 10, 32)
+		if nerr != nil {
+			return nil, fmt.Errorf("cannot convert index to int value: %s", p[0])
+		}
+		newkey.position = int(pos)
+		newkey.isArray = true
+		ret = append(ret, newkey)
+	}
+
+	return ret, nil
+}
+
+// legacy ValuesForPath() - now wrapped to handle special case of indexed arrays in 'path'.
+func (mv Map) oldValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
+	m := map[string]interface{}(mv)
+	var subKeyMap map[string]interface{}
+	if len(subkeys) > 0 {
+		var err error
+		subKeyMap, err = getSubKeyMap(subkeys...)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	keys := strings.Split(path, ".")
+	if keys[len(keys)-1] == "" {
+		keys = keys[:len(keys)-1]
+	}
+	ivals := make([]interface{}, 0, defaultArraySize)
+	var cnt int
+	valuesForKeyPath(&ivals, &cnt, m, keys, subKeyMap)
+	return ivals[:cnt], nil
+}
+
+func valuesForKeyPath(ret *[]interface{}, cnt *int, m interface{}, keys []string, subkeys map[string]interface{}) {
+	lenKeys := len(keys)
+
+	// load 'm' values into 'ret'
+	// expand any lists
+	if lenKeys == 0 {
+		switch m.(type) {
+		case map[string]interface{}:
+			if subkeys != nil {
+				if ok := hasSubKeys(m, subkeys); !ok {
+					return
+				}
+			}
+			*ret = append(*ret, m)
+			*cnt++
+		case []interface{}:
+			for i, v := range m.([]interface{}) {
+				if subkeys != nil {
+					if ok := hasSubKeys(v, subkeys); !ok {
+						continue // only load list members with subkeys
+					}
+				}
+				*ret = append(*ret, (m.([]interface{}))[i])
+				*cnt++
+			}
+		default:
+			if subkeys != nil {
+				return // must be map[string]interface{} if there are subkeys
+			}
+			*ret = append(*ret, m)
+			*cnt++
+		}
+		return
+	}
+
+	// key of interest
+	key := keys[0]
+	switch key {
+	case "*": // wildcard - scan all values
+		switch m.(type) {
+		case map[string]interface{}:
+			for _, v := range m.(map[string]interface{}) {
+				// valuesForKeyPath(ret, v, keys[1:], subkeys)
+				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
+			}
+		case []interface{}:
+			for _, v := range m.([]interface{}) {
+				switch v.(type) {
+				// flatten out a list of maps - keys are processed
+				case map[string]interface{}:
+					for _, vv := range v.(map[string]interface{}) {
+						// valuesForKeyPath(ret, vv, keys[1:], subkeys)
+						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
+					}
+				default:
+					// valuesForKeyPath(ret, v, keys[1:], subkeys)
+					valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
+				}
+			}
+		}
+	default: // key - must be map[string]interface{}
+		switch m.(type) {
+		case map[string]interface{}:
+			if v, ok := m.(map[string]interface{})[key]; ok {
+				// valuesForKeyPath(ret, v, keys[1:], subkeys)
+				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
+			}
+		case []interface{}: // may be buried in list
+			for _, v := range m.([]interface{}) {
+				switch v.(type) {
+				case map[string]interface{}:
+					if vv, ok := v.(map[string]interface{})[key]; ok {
+						// valuesForKeyPath(ret, vv, keys[1:], subkeys)
+						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
+					}
+				}
+			}
+		}
+	}
+}
+
+// hasSubKeys() - interface{} equality works for string, float64, bool
+// 'v' must be a map[string]interface{} value to have subkeys
+// 'a' can have k:v pairs with v.(string) == "*", which is treated like a wildcard.
+func hasSubKeys(v interface{}, subkeys map[string]interface{}) bool {
+	if len(subkeys) == 0 {
+		return true
+	}
+
+	switch v.(type) {
+	case map[string]interface{}:
+		// do all subKey name:value pairs match?
+		mv := v.(map[string]interface{})
+		for skey, sval := range subkeys {
+			isNotKey := false
+			if skey[:1] == "!" { // a NOT-key
+				skey = skey[1:]
+				isNotKey = true
+			}
+			vv, ok := mv[skey]
+			if !ok { // key doesn't exist
+				if isNotKey { // key not there, but that's what we want
+					if kv, ok := sval.(string); ok && kv == "*" {
+						continue
+					}
+				}
+				return false
+			}
+			// wildcard check
+			if kv, ok := sval.(string); ok && kv == "*" {
+				if isNotKey { // key is there, and we don't want it
+					return false
+				}
+				continue
+			}
+			switch sval.(type) {
+			case string:
+				if s, ok := vv.(string); ok && s == sval.(string) {
+					if isNotKey {
+						return false
+					}
+					continue
+				}
+			case bool:
+				if b, ok := vv.(bool); ok && b == sval.(bool) {
+					if isNotKey {
+						return false
+					}
+					continue
+				}
+			case float64:
+				if f, ok := vv.(float64); ok && f == sval.(float64) {
+					if isNotKey {
+						return false
+					}
+					continue
+				}
+			}
+			// key there but didn't match subkey value
+			if isNotKey { // that's what we want
+				continue
+			}
+			return false
+		}
+		// all subkeys matched
+		return true
+	}
+
+	// not a map[string]interface{} value, can't have subkeys
+	return false
+}
+
+// Generate map of key:value entries as map[string]string.
+//	'kv' arguments are "name:value" pairs: attribute keys are designated with prepended hyphen, '-'.
+//	If len(kv) == 0, the return is (nil, nil).
+func getSubKeyMap(kv ...string) (map[string]interface{}, error) {
+	if len(kv) == 0 {
+		return nil, nil
+	}
+	m := make(map[string]interface{}, 0)
+	for _, v := range kv {
+		vv := strings.Split(v, fieldSep)
+		switch len(vv) {
+		case 2:
+			m[vv[0]] = interface{}(vv[1])
+		case 3:
+			switch vv[2] {
+			case "string", "char", "text":
+				m[vv[0]] = interface{}(vv[1])
+			case "bool", "boolean":
+				// ParseBool treats "1"==true & "0"==false
+				b, err := strconv.ParseBool(vv[1])
+				if err != nil {
+					return nil, fmt.Errorf("can't convert subkey value to bool: %s", vv[1])
+				}
+				m[vv[0]] = interface{}(b)
+			case "float", "float64", "num", "number", "numeric":
+				f, err := strconv.ParseFloat(vv[1], 64)
+				if err != nil {
+					return nil, fmt.Errorf("can't convert subkey value to float: %s", vv[1])
+				}
+				m[vv[0]] = interface{}(f)
+			default:
+				return nil, fmt.Errorf("unknown subkey conversion spec: %s", v)
+			}
+		default:
+			return nil, fmt.Errorf("unknown subkey spec: %s", v)
+		}
+	}
+	return m, nil
+}
+
+// -------------------------------  END of valuesFor ... ----------------------------
+
+// ----------------------- locate where a key value is in the tree -------------------
+
+//----------------------------- find all paths to a key --------------------------------
+
+// PathsForKey returns all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
+// Results can be used with ValuesForPath.
+func (mv Map) PathsForKey(key string) []string {
+	m := map[string]interface{}(mv)
+	breadbasket := make(map[string]bool, 0)
+	breadcrumbs := ""
+
+	hasKeyPath(breadcrumbs, m, key, breadbasket)
+	if len(breadbasket) == 0 {
+		return nil
+	}
+
+	// unpack map keys to return
+	res := make([]string, len(breadbasket))
+	var i int
+	for k := range breadbasket {
+		res[i] = k
+		i++
+	}
+
+	return res
+}
+
+// PathForKeyShortest extracts the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
+// Paths are strings using dot-notation.
+func (mv Map) PathForKeyShortest(key string) string {
+	paths := mv.PathsForKey(key)
+
+	lp := len(paths)
+	if lp == 0 {
+		return ""
+	}
+	if lp == 1 {
+		return paths[0]
+	}
+
+	shortest := paths[0]
+	shortestLen := len(strings.Split(shortest, "."))
+
+	for i := 1; i < len(paths); i++ {
+		vlen := len(strings.Split(paths[i], "."))
+		if vlen < shortestLen {
+			shortest = paths[i]
+			shortestLen = vlen
+		}
+	}
+
+	return shortest
+}
+
+// hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth
+// This is really just a breadcrumber that saves all trails that hit the prescribed 'key'.
+func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]bool) {
+	switch iv.(type) {
+	case map[string]interface{}:
+		vv := iv.(map[string]interface{})
+		if _, ok := vv[key]; ok {
+			// create a new breadcrumb, intialized with the one we have
+			var nbc string
+			if crumbs == "" {
+				nbc = key
+			} else {
+				nbc = crumbs + "." + key
+			}
+			basket[nbc] = true
+		}
+		// walk on down the path, key could occur again at deeper node
+		for k, v := range vv {
+			// create a new breadcrumb, intialized with the one we have
+			var nbc string
+			if crumbs == "" {
+				nbc = k
+			} else {
+				nbc = crumbs + "." + k
+			}
+			hasKeyPath(nbc, v, key, basket)
+		}
+	case []interface{}:
+		// crumb-trail doesn't change, pass it on
+		for _, v := range iv.([]interface{}) {
+			hasKeyPath(crumbs, v, key, basket)
+		}
+	}
+}
+
+var PathNotExistError = errors.New("Path does not exist")
+
+// ValueForPath wraps ValuesFor Path and returns the first value returned.
+// If no value is found it returns 'nil' and PathNotExistError.
+func (mv Map) ValueForPath(path string) (interface{}, error) {
+	vals, err := mv.ValuesForPath(path)
+	if err != nil {
+		return nil, err
+	}
+	if len(vals) == 0 {
+		return nil, PathNotExistError
+	}
+	return vals[0], nil
+}
+
+// ValuesForPathString returns the first found value for the path as a string.
+func (mv Map) ValueForPathString(path string) (string, error) {
+	vals, err := mv.ValuesForPath(path)
+	if err != nil {
+		return "", err
+	}
+	if len(vals) == 0 {
+		return "", errors.New("ValueForPath: path not found")
+	}
+	val := vals[0]
+	return fmt.Sprintf("%v", val), nil
+}
+
+// ValueOrEmptyForPathString returns the first found value for the path as a string.
+// If the path is not found then it returns an empty string.
+func (mv Map) ValueOrEmptyForPathString(path string) string {
+	str, _ := mv.ValueForPathString(path)
+	return str
+}

+ 112 - 0
vendor/github.com/clbanning/mxj/leafnode.go

@@ -0,0 +1,112 @@
+package mxj
+
+// leafnode.go - return leaf nodes with paths and values for the Map
+// inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw
+
+import (
+	"strconv"
+	"strings"
+)
+
+const (
+	NoAttributes = true // suppress LeafNode values that are attributes
+)
+
+// LeafNode - a terminal path value in a Map.
+// For XML Map values it represents an attribute or simple element value  - of type
+// string unless Map was created using Cast flag. For JSON Map values it represents
+// a string, numeric, boolean, or null value.
+type LeafNode struct {
+	Path  string      // a dot-notation representation of the path with array subscripting
+	Value interface{} // the value at the path termination
+}
+
+// LeafNodes - returns an array of all LeafNode values for the Map.
+// The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-')
+// as well as the "#text" key for the associated simple element value.
+//
+// PrependAttrWithHypen(false) will result in attributes having .attr-name as 
+// terminal node in 'path' while the path for the element value, itself, will be 
+// the base path w/o "#text". 
+//
+// LeafUseDotNotation(true) causes list members to be identified using ".N" syntax
+// rather than "[N]" syntax.
+func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
+	var a bool
+	if len(no_attr) == 1 {
+		a = no_attr[0]
+	}
+
+	l := make([]LeafNode, 0)
+	getLeafNodes("", "", map[string]interface{}(mv), &l, a)
+	return l
+}
+
+func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
+	// if stripping attributes, then also strip "#text" key
+	if !noattr || node != "#text" {
+		if path != "" && node[:1] != "[" {
+			path += "."
+		}
+		path += node
+	}
+	switch mv.(type) {
+	case map[string]interface{}:
+		for k, v := range mv.(map[string]interface{}) {
+			// if noattr && k[:1] == "-" {
+			if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
+				continue
+			}
+			getLeafNodes(path, k, v, l, noattr)
+		}
+	case []interface{}:
+		for i, v := range mv.([]interface{}) {
+			if useDotNotation {
+				getLeafNodes(path, strconv.Itoa(i), v, l, noattr)
+			} else {
+				getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr)
+			}
+		}
+	default:
+		// can't walk any further, so create leaf
+		n := LeafNode{path, mv}
+		*l = append(*l, n)
+	}
+}
+
+// LeafPaths - all paths that terminate in LeafNode values.
+func (mv Map) LeafPaths(no_attr ...bool) []string {
+	ln := mv.LeafNodes()
+	ss := make([]string, len(ln))
+	for i := 0; i < len(ln); i++ {
+		ss[i] = ln[i].Path
+	}
+	return ss
+}
+
+// LeafValues - all terminal values in the Map.
+func (mv Map) LeafValues(no_attr ...bool) []interface{} {
+	ln := mv.LeafNodes()
+	vv := make([]interface{}, len(ln))
+	for i := 0; i < len(ln); i++ {
+		vv[i] = ln[i].Value
+	}
+	return vv
+}
+
+// ====================== utilities ======================
+
+// https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I
+var useDotNotation bool
+
+// LeafUseDotNotation sets a flag that list members in LeafNode paths
+// should be identified using ".N" syntax rather than the default "[N]"
+// syntax.  Calling LeafUseDotNotation with no arguments toggles the 
+// flag on/off; otherwise, the argument sets the flag value 'true'/'false'.
+func LeafUseDotNotation(b ...bool) {
+	if len(b) == 0 {
+		useDotNotation = !useDotNotation
+		return
+	}
+	useDotNotation = b[0]
+}

+ 86 - 0
vendor/github.com/clbanning/mxj/misc.go

@@ -0,0 +1,86 @@
+// Copyright 2016 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// misc.go - mimic functions (+others) called out in:
+//          https://groups.google.com/forum/#!topic/golang-nuts/jm_aGsJNbdQ
+// Primarily these methods let you retrive XML structure information.
+
+package mxj
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+// Return the root element of the Map. If there is not a single key in Map,
+// then an error is returned.
+func (mv Map) Root() (string, error) {
+	mm := map[string]interface{}(mv)
+	if len(mm) != 1 {
+		return "", fmt.Errorf("Map does not have singleton root. Len: %d.", len(mm))
+	}
+	for k, _ := range mm {
+		return k, nil
+	}
+	return "", nil
+}
+
+// If the path is an element with sub-elements, return a list of the sub-element
+// keys.  (The list is alphabeticly sorted.)  NOTE: Map keys that are prefixed with
+// '-', a hyphen, are considered attributes; see m.Attributes(path).
+func (mv Map) Elements(path string) ([]string, error) {
+	e, err := mv.ValueForPath(path)
+	if err != nil {
+		return nil, err
+	}
+	switch e.(type) {
+	case map[string]interface{}:
+		ee := e.(map[string]interface{})
+		elems := make([]string, len(ee))
+		var i int
+		for k, _ := range ee {
+			if len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
+				continue // skip attributes
+			}
+			elems[i] = k
+			i++
+		}
+		elems = elems[:i]
+		// alphabetic sort keeps things tidy
+		sort.Strings(elems)
+		return elems, nil
+	}
+	return nil, fmt.Errorf("no elements for path: %s", path)
+}
+
+// If the path is an element with attributes, return a list of the attribute
+// keys.  (The list is alphabeticly sorted.)  NOTE: Map keys that are not prefixed with
+// '-', a hyphen, are not treated as attributes; see m.Elements(path). Also, if the
+// attribute prefix is "" - SetAttrPrefix("") or PrependAttrWithHyphen(false) - then
+// there are no identifiable attributes.
+func (mv Map) Attributes(path string) ([]string, error) {
+	a, err := mv.ValueForPath(path)
+	if err != nil {
+		return nil, err
+	}
+	switch a.(type) {
+	case map[string]interface{}:
+		aa := a.(map[string]interface{})
+		attrs := make([]string, len(aa))
+		var i int
+		for k, _ := range aa {
+			if len(attrPrefix) == 0 || strings.Index(k, attrPrefix) != 0 {
+				continue // skip non-attributes
+			}
+			attrs[i] = k[len(attrPrefix):]
+			i++
+		}
+		attrs = attrs[:i]
+		// alphabetic sort keeps things tidy
+		sort.Strings(attrs)
+		return attrs, nil
+	}
+	return nil, fmt.Errorf("no attributes for path: %s", path)
+}

+ 128 - 0
vendor/github.com/clbanning/mxj/mxj.go

@@ -0,0 +1,128 @@
+// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
+// Copyright 2012-2014 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+import (
+	"fmt"
+	"sort"
+)
+
+const (
+	Cast         = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast)
+	SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding)
+)
+
+type Map map[string]interface{}
+
+// Allocate a Map.
+func New() Map {
+	m := make(map[string]interface{}, 0)
+	return m
+}
+
+// Cast a Map to map[string]interface{}
+func (mv Map) Old() map[string]interface{} {
+	return mv
+}
+
+// Return a copy of mv as a newly allocated Map.  If the Map only contains string,
+// numeric, map[string]interface{}, and []interface{} values, then it can be thought
+// of as a "deep copy."  Copying a structure (or structure reference) value is subject
+// to the noted restrictions.
+//	NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags
+//	      then only public fields of the structure are in the new Map - and with
+//	      keys that conform to any encoding tag instructions. The structure itself will
+//	      be represented as a map[string]interface{} value.
+func (mv Map) Copy() (Map, error) {
+	// this is the poor-man's deep copy
+	// not efficient, but it works
+	j, jerr := mv.Json()
+	// must handle, we don't know how mv got built
+	if jerr != nil {
+		return nil, jerr
+	}
+	return NewMapJson(j)
+}
+
+// --------------- StringIndent ... from x2j.WriteMap -------------
+
+// Pretty print a Map.
+func (mv Map) StringIndent(offset ...int) string {
+	return writeMap(map[string]interface{}(mv), true, true, offset...)
+}
+
+// Pretty print a Map without the value type information - just key:value entries.
+func (mv Map) StringIndentNoTypeInfo(offset ...int) string {
+	return writeMap(map[string]interface{}(mv), false, true, offset...)
+}
+
+// writeMap - dumps the map[string]interface{} for examination.
+// 'typeInfo' causes value type to be printed.
+//	'offset' is initial indentation count; typically: Write(m).
+func writeMap(m interface{}, typeInfo, root bool, offset ...int) string {
+	var indent int
+	if len(offset) == 1 {
+		indent = offset[0]
+	}
+
+	var s string
+	switch m.(type) {
+	case []interface{}:
+		if typeInfo {
+			s += "[[]interface{}]"
+		}
+		for _, v := range m.([]interface{}) {
+			s += "\n"
+			for i := 0; i < indent; i++ {
+				s += "  "
+			}
+			s += writeMap(v, typeInfo, false, indent+1)
+		}
+	case map[string]interface{}:
+		list := make([][2]string, len(m.(map[string]interface{})))
+		var n int
+		for k, v := range m.(map[string]interface{}) {
+			list[n][0] = k
+			list[n][1] = writeMap(v, typeInfo, false, indent+1)
+			n++
+		}
+		sort.Sort(mapList(list))
+		for _, v := range list {
+			if root {
+				root = false
+			} else {
+				s += "\n"
+			}
+			for i := 0; i < indent; i++ {
+				s += "  "
+			}
+			s += v[0] + " : " + v[1]
+		}
+	default:
+		if typeInfo {
+			s += fmt.Sprintf("[%T] %+v", m, m)
+		} else {
+			s += fmt.Sprintf("%+v", m)
+		}
+	}
+	return s
+}
+
+// ======================== utility ===============
+
+type mapList [][2]string
+
+func (ml mapList) Len() int {
+	return len(ml)
+}
+
+func (ml mapList) Swap(i, j int) {
+	ml[i], ml[j] = ml[j], ml[i]
+}
+
+func (ml mapList) Less(i, j int) bool {
+	return ml[i][0] <= ml[j][0]
+}

+ 184 - 0
vendor/github.com/clbanning/mxj/newmap.go

@@ -0,0 +1,184 @@
+// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
+// Copyright 2012-2014, 2018 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// remap.go - build a new Map from the current Map based on keyOld:keyNew mapppings
+//            keys can use dot-notation, keyOld can use wildcard, '*'
+//
+// Computational strategy -
+// Using the key path - []string - traverse a new map[string]interface{} and
+// insert the oldVal as the newVal when we arrive at the end of the path.
+// If the type at the end is nil, then that is newVal
+// If the type at the end is a singleton (string, float64, bool) an array is created.
+// If the type at the end is an array, newVal is just appended.
+// If the type at the end is a map, it is inserted if possible or the map value
+//    is converted into an array if necessary.
+
+package mxj
+
+import (
+	"errors"
+	"strings"
+)
+
+// (Map)NewMap - create a new Map from data in the current Map.
+//	'keypairs' are key mappings "oldKey:newKey" and specify that the current value of 'oldKey'
+//	should be the value for 'newKey' in the returned Map.
+//		- 'oldKey' supports dot-notation as described for (Map)ValuesForPath()
+//		- 'newKey' supports dot-notation but with no wildcards, '*', or indexed arrays
+//		- "oldKey" is shorthand for the keypair value "oldKey:oldKey"
+//		- "oldKey:" and ":newKey" are invalid keypair values
+//		- if 'oldKey' does not exist in the current Map, it is not written to the new Map.
+//		  "null" is not supported unless it is the current Map.
+//		- see newmap_test.go for several syntax examples
+// 	- mv.NewMap() == mxj.New()
+//
+//	NOTE: "examples/partial.go" shows how to create arbitrary sub-docs of an XML doc.
+func (mv Map) NewMap(keypairs ...string) (Map, error) {
+	n := make(map[string]interface{}, 0)
+	if len(keypairs) == 0 {
+		return n, nil
+	}
+
+	// loop through the pairs
+	var oldKey, newKey string
+	var path []string
+	for _, v := range keypairs {
+		if len(v) == 0 {
+			continue // just skip over empty keypair arguments
+		}
+
+		// initialize oldKey, newKey and check
+		vv := strings.Split(v, ":")
+		if len(vv) > 2 {
+			return n, errors.New("oldKey:newKey keypair value not valid - " + v)
+		}
+		if len(vv) == 1 {
+			oldKey, newKey = vv[0], vv[0]
+		} else {
+			oldKey, newKey = vv[0], vv[1]
+		}
+		strings.TrimSpace(oldKey)
+		strings.TrimSpace(newKey)
+		if i := strings.Index(newKey, "*"); i > -1 {
+			return n, errors.New("newKey value cannot contain wildcard character - " + v)
+		}
+		if i := strings.Index(newKey, "["); i > -1 {
+			return n, errors.New("newKey value cannot contain indexed arrays - " + v)
+		}
+		if oldKey == "" || newKey == "" {
+			return n, errors.New("oldKey or newKey is not specified - " + v)
+		}
+
+		// get oldKey value
+		oldVal, err := mv.ValuesForPath(oldKey)
+		if err != nil {
+			return n, err
+		}
+		if len(oldVal) == 0 {
+			continue // oldKey has no value, may not exist in mv
+		}
+
+		// break down path
+		path = strings.Split(newKey, ".")
+		if path[len(path)-1] == "" { // ignore a trailing dot in newKey spec
+			path = path[:len(path)-1]
+		}
+
+		addNewVal(&n, path, oldVal)
+	}
+
+	return n, nil
+}
+
+// navigate 'n' to end of path and add val
+func addNewVal(n *map[string]interface{}, path []string, val []interface{}) {
+	// newVal - either singleton or array
+	var newVal interface{}
+	if len(val) == 1 {
+		newVal = val[0] // is type interface{}
+	} else {
+		newVal = interface{}(val)
+	}
+
+	// walk to the position of interest, create it if necessary
+	m := (*n)           // initialize map walker
+	var k string        // key for m
+	lp := len(path) - 1 // when to stop looking
+	for i := 0; i < len(path); i++ {
+		k = path[i]
+		if i == lp {
+			break
+		}
+		var nm map[string]interface{} // holds position of next-map
+		switch m[k].(type) {
+		case nil: // need a map for next node in path, so go there
+			nm = make(map[string]interface{}, 0)
+			m[k] = interface{}(nm)
+			m = m[k].(map[string]interface{})
+		case map[string]interface{}:
+			// OK - got somewhere to walk to, go there
+			m = m[k].(map[string]interface{})
+		case []interface{}:
+			// add a map and nm points to new map unless there's already
+			// a map in the array, then nm points there
+			// The placement of the next value in the array is dependent
+			// on the sequence of members - could land on a map or a nil
+			// value first.  TODO: how to test this.
+			a := make([]interface{}, 0)
+			var foundmap bool
+			for _, vv := range m[k].([]interface{}) {
+				switch vv.(type) {
+				case nil: // doesn't appear that this occurs, need a test case
+					if foundmap { // use the first one in array
+						a = append(a, vv)
+						continue
+					}
+					nm = make(map[string]interface{}, 0)
+					a = append(a, interface{}(nm))
+					foundmap = true
+				case map[string]interface{}:
+					if foundmap { // use the first one in array
+						a = append(a, vv)
+						continue
+					}
+					nm = vv.(map[string]interface{})
+					a = append(a, vv)
+					foundmap = true
+				default:
+					a = append(a, vv)
+				}
+			}
+			// no map found in array
+			if !foundmap {
+				nm = make(map[string]interface{}, 0)
+				a = append(a, interface{}(nm))
+			}
+			m[k] = interface{}(a) // must insert in map
+			m = nm
+		default: // it's a string, float, bool, etc.
+			aa := make([]interface{}, 0)
+			nm = make(map[string]interface{}, 0)
+			aa = append(aa, m[k], nm)
+			m[k] = interface{}(aa)
+			m = nm
+		}
+	}
+
+	// value is nil, array or a singleton of some kind
+	// initially m.(type) == map[string]interface{}
+	v := m[k]
+	switch v.(type) {
+	case nil: // initialized
+		m[k] = newVal
+	case []interface{}:
+		a := m[k].([]interface{})
+		a = append(a, newVal)
+		m[k] = interface{}(a)
+	default: // v exists:string, float64, bool, map[string]interface, etc.
+		a := make([]interface{}, 0)
+		a = append(a, v, newVal)
+		m[k] = interface{}(a)
+	}
+}

+ 206 - 0
vendor/github.com/clbanning/mxj/readme.md

@@ -0,0 +1,206 @@
+<h2>mxj - to/from maps, XML and JSON</h2>
+Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-path, including wildcards.
+
+mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
+
+<h4>Installation</h4>
+Using go.mod:
+<pre>
+go get github.com/clbanning/mxj/v2@v2.3.2
+</pre>
+
+<pre>
+import "github.com/clbanning/mxj/v2"
+</pre>
+
+... or just vendor the package.
+
+<h4>Related Packages</h4>
+
+https://github.com/clbanning/checkxml provides functions for validating XML data.
+
+<h4>Refactor Encoder - 2020.05.01</h4>
+Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)
+
+	Nodes      m.XML() time
+	54809       12.53708ms
+	109780      32.403183ms
+	164678      59.826412ms
+	482598     109.358007ms
+
+<h4>Refactor Decoder - 2015.11.15</h4>
+For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant.  I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi.  Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value.  As shown by:
+
+	BenchmarkNewMapXml-4         	  100000	     18043 ns/op
+	BenchmarkNewStructXml-4      	  100000	     14892 ns/op
+	BenchmarkNewMapJson-4        	  300000	      4633 ns/op
+	BenchmarkNewStructJson-4     	  300000	      3427 ns/op
+	BenchmarkNewMapXmlBooks-4    	   20000	     82850 ns/op
+	BenchmarkNewStructXmlBooks-4 	   20000	     67822 ns/op
+	BenchmarkNewMapJsonBooks-4   	  100000	     17222 ns/op
+	BenchmarkNewStructJsonBooks-4	  100000	     15309 ns/op
+
+<h4>Notices</h4>
+
+	2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
+	2020.10.28: v2.3 - add TrimWhiteSpace option
+	2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
+	2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
+	2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
+	2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
+	2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
+	2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
+	2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
+	2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
+	2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
+	2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
+	2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
+	2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
+	2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
+	2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
+	2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
+	            To cast them to float64, first set flag with CastNanInf(true).
+	2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
+	2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
+	2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
+	2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq()
+	            and mv.XmlSeq() / mv.XmlSeqIndent().
+	2015-05-20: New: mv.StringIndentNoTypeInfo().
+	            Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(),
+	            mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo().
+	2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information.
+	            (NOTE: PreserveXmlList() is similar and will be here soon.)
+	2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag.
+	2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
+	2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references.
+
+<h4>Basic Unmarshal XML to map[string]interface{}</h4>
+<pre>type Map map[string]interface{}</pre>
+
+Create a `Map` value, 'mv', from any `map[string]interface{}` value, 'v':
+<pre>mv := Map(v)</pre>
+
+Unmarshal / marshal XML as a `Map` value, 'mv':
+<pre>mv, err := NewMapXml(xmlValue) // unmarshal
+xmlValue, err := mv.Xml()      // marshal</pre>
+
+Unmarshal XML from an `io.Reader` as a `Map` value, 'mv':
+<pre>mv, err := NewMapXmlReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream
+mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded</pre>
+
+Marshal `Map` value, 'mv', to an XML Writer (`io.Writer`):
+<pre>err := mv.XmlWriter(xmlWriter)
+raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter</pre>
+   
+Also, for prettified output:
+<pre>xmlValue, err := mv.XmlIndent(prefix, indent, ...)
+err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...)
+raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)</pre>
+
+Bulk process XML with error handling (note: handlers must return a boolean value):
+<pre>err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
+err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))</pre>
+
+Converting XML to JSON: see Examples for `NewMapXml` and `HandleXmlReader`.
+
+There are comparable functions and methods for JSON processing.
+
+Arbitrary structure values can be decoded to / encoded from `Map` values:
+<pre>mv, err := NewMapStruct(structVal)
+err := mv.Struct(structPointer)</pre>
+
+<h4>Extract / modify Map values</h4>
+To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
+or structure to a `Map` value, 'mv', or cast a `map[string]interface{}` value to a `Map` value, 'mv', then:
+<pre>paths := mv.PathsForKey(key)
+path := mv.PathForKeyShortest(key)
+values, err := mv.ValuesForKey(key, subkeys)
+values, err := mv.ValuesForPath(path, subkeys)
+count, err := mv.UpdateValuesForPath(newVal, path, subkeys)</pre>
+
+Get everything at once, irrespective of path depth:
+<pre>leafnodes := mv.LeafNodes()
+leafvalues := mv.LeafValues()</pre>
+
+A new `Map` with whatever keys are desired can be created from the current `Map` and then encoded in XML
+or JSON. (Note: keys can use dot-notation.)
+<pre>newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
+newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
+newXml, err := newMap.Xml()   // for example
+newJson, err := newMap.Json() // ditto</pre>
+
+<h4>Usage</h4>
+
+The package is fairly well [self-documented with examples](http://godoc.org/github.com/clbanning/mxj).
+
+Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.
+
+<h4>XML parsing conventions</h4>
+
+Using NewMapXml()
+
+   - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
+     to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
+     `SetAttrPrefix()`.)
+   - If the element is a simple element and has attributes, the element value
+     is given the key `#text` for its `map[string]interface{}` representation.  (See
+     the 'atomFeedString.xml' test data, below.)
+   - XML comments, directives, and process instructions are ignored.
+   - If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
+
+Using NewMapXmlSeq()
+
+   - Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values
+     where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the 
+     value for `<attr_label>`.
+   - All elements, except for the root, have a "#seq" key.
+   - Comments, directives, and process instructions are unmarshalled into the Map using the
+     keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
+     specifics.)
+   - Name space syntax is preserved: 
+      - `<ns:key>something</ns.key>` parses to `map["ns:key"]interface{}{"something"}`
+      - `xmlns:ns="http://myns.com/ns"` parses to `map["xmlns:ns"]interface{}{"http://myns.com/ns"}`
+
+Both
+
+   - By default, "Nan", "Inf", and "-Inf" values are not cast to float64.  If you want them
+     to be cast, set a flag to cast them  using CastNanInf(true).
+
+<h4>XML encoding conventions</h4>
+
+   - 'nil' `Map` values, which may represent 'null' JSON values, are encoded as `<tag/>`.
+     NOTE: the operation is not symmetric as `<tag/>` elements are decoded as `tag:""` `Map` values,
+           which, then, encode in JSON as `"tag":""` values.
+   - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one.  (Go
+           randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
+           Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
+           mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
+           working with the Map representation.
+
+<h4>Running "go test"</h4>
+
+Because there are no guarantees on the sequence map elements are retrieved, the tests have been 
+written for visual verification in most cases.  One advantage is that you can easily use the 
+output from running "go test" as examples of calling the various functions and methods.
+
+<h4>Motivation</h4>
+
+I make extensive use of JSON for messaging and typically unmarshal the messages into
+`map[string]interface{}` values.  This is easily done using `json.Unmarshal` from the
+standard Go libraries.  Unfortunately, many legacy solutions use structured
+XML messages; in those environments the applications would have to be refactored to
+interoperate with my components.
+
+The better solution is to just provide an alternative HTTP handler that receives
+XML messages and parses it into a `map[string]interface{}` value and then reuse
+all the JSON-based code.  The Go `xml.Unmarshal()` function does not provide the same
+option of unmarshaling XML messages into `map[string]interface{}` values. So I wrote
+a couple of small functions to fill this gap and released them as the x2j package.
+
+Over the next year and a half additional features were added, and the companion j2x
+package was released to address XML encoding of arbitrary JSON and `map[string]interface{}`
+values.  As part of a refactoring of our production system and looking at how we had been
+using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or
+JSON-to_XML conversion and that working with the XML or JSON as `map[string]interface{}`
+values was the primary value.  Thus, everything was refactored into the mxj package.
+

+ 37 - 0
vendor/github.com/clbanning/mxj/remove.go

@@ -0,0 +1,37 @@
+package mxj
+
+import "strings"
+
+// Removes the path.
+func (mv Map) Remove(path string) error {
+	m := map[string]interface{}(mv)
+	return remove(m, path)
+}
+
+func remove(m interface{}, path string) error {
+	val, err := prevValueByPath(m, path)
+	if err != nil {
+		return err
+	}
+
+	lastKey := lastKey(path)
+	delete(val, lastKey)
+
+	return nil
+}
+
+// returns the last key of the path.
+// lastKey("a.b.c") would had returned "c"
+func lastKey(path string) string {
+	keys := strings.Split(path, ".")
+	key := keys[len(keys)-1]
+	return key
+}
+
+// returns the path without the last key
+// parentPath("a.b.c") whould had returned "a.b"
+func parentPath(path string) string {
+	keys := strings.Split(path, ".")
+	parentPath := strings.Join(keys[0:len(keys)-1], ".")
+	return parentPath
+}

+ 61 - 0
vendor/github.com/clbanning/mxj/rename.go

@@ -0,0 +1,61 @@
+package mxj
+
+import (
+	"errors"
+	"strings"
+)
+
+// RenameKey renames a key in a Map.
+// It works only for nested maps. 
+// It doesn't work for cases when the key is in a list.
+func (mv Map) RenameKey(path string, newName string) error {
+	var v bool
+	var err error
+	if v, err = mv.Exists(path); err == nil && !v {
+		return errors.New("RenameKey: path not found: " + path)
+	} else if err != nil {
+		return err
+	}
+	if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
+		return errors.New("RenameKey: key already exists: " + newName)
+	} else if err != nil {
+		return err
+	}
+
+	m := map[string]interface{}(mv)
+	return renameKey(m, path, newName)
+}
+
+func renameKey(m interface{}, path string, newName string) error {
+	val, err := prevValueByPath(m, path)
+	if err != nil {
+		return err
+	}
+
+	oldName := lastKey(path)
+	val[newName] = val[oldName]
+	delete(val, oldName)
+
+	return nil
+}
+
+// returns a value which contains a last key in the path
+// For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
+func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
+	keys := strings.Split(path, ".")
+
+	switch mValue := m.(type) {
+	case map[string]interface{}:
+		for key, value := range mValue {
+			if key == keys[0] {
+				if len(keys) == 1 {
+					return mValue, nil
+				} else {
+					// keep looking for the full path to the key
+					return prevValueByPath(value, strings.Join(keys[1:], "."))
+				}
+			}
+		}
+	}
+	return nil, errors.New("prevValueByPath: didn't find path – " + path)
+}

+ 26 - 0
vendor/github.com/clbanning/mxj/set.go

@@ -0,0 +1,26 @@
+package mxj
+
+import (
+	"strings"
+)
+
+// Sets the value for the path
+func (mv Map) SetValueForPath(value interface{}, path string) error {
+	pathAry := strings.Split(path, ".")
+	parentPathAry := pathAry[0 : len(pathAry)-1]
+	parentPath := strings.Join(parentPathAry, ".")
+
+	val, err := mv.ValueForPath(parentPath)
+	if err != nil {
+		return err
+	}
+	if val == nil {
+		return nil // we just ignore the request if there's no val
+	}
+
+	key := pathAry[len(pathAry)-1]
+	cVal := val.(map[string]interface{})
+	cVal[key] = value
+
+	return nil
+}

+ 20 - 0
vendor/github.com/clbanning/mxj/setfieldsep.go

@@ -0,0 +1,20 @@
+package mxj
+
+// Per: https://github.com/clbanning/mxj/issues/37#issuecomment-278651862
+var fieldSep string = ":"
+
+// SetFieldSeparator changes the default field separator, ":", for the
+// newVal argument in mv.UpdateValuesForPath and the optional 'subkey' arguments
+// in mv.ValuesForKey and mv.ValuesForPath. 
+// 
+// E.g., if the newVal value is "http://blah/blah", setting the field separator
+// to "|" will allow the newVal specification, "<key>|http://blah/blah" to parse
+// properly.  If called with no argument or an empty string value, the field
+// separator is set to the default, ":".
+func SetFieldSeparator(s ...string) {
+	if len(s) == 0 || s[0] == "" {
+		fieldSep = ":" // the default
+		return
+	}
+	fieldSep = s[0]
+}

+ 29 - 0
vendor/github.com/clbanning/mxj/songtext.xml

@@ -0,0 +1,29 @@
+<msg mtype="alert" mpriority="1">
+	<text>help me!</text>
+	<song title="A Long Time" author="Mayer Hawthorne">
+		<verses>
+			<verse name="verse 1" no="1">
+				<line no="1">Henry was a renegade</line>
+				<line no="2">Didn't like to play it safe</line>
+				<line no="3">One component at a time</line>
+				<line no="4">There's got to be a better way</line>
+				<line no="5">Oh, people came from miles around</line>
+				<line no="6">Searching for a steady job</line>
+				<line no="7">Welcome to the Motor Town</line>
+				<line no="8">Booming like an atom bomb</line>
+			</verse>
+			<verse name="verse 2" no="2">
+				<line no="1">Oh, Henry was the end of the story</line>
+				<line no="2">Then everything went wrong</line>
+				<line no="3">And we'll return it to its former glory</line>
+				<line no="4">But it just takes so long</line>
+			</verse>
+		</verses>
+		<chorus>
+			<line no="1">It's going to take a long time</line>
+			<line no="2">It's going to take it, but we'll make it one day</line>
+			<line no="3">It's going to take a long time</line>
+			<line no="4">It's going to take it, but we'll make it one day</line>
+		</chorus>
+	</song>
+</msg>

+ 30 - 0
vendor/github.com/clbanning/mxj/strict.go

@@ -0,0 +1,30 @@
+// Copyright 2016 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// strict.go actually addresses setting xml.Decoder attribute
+// values.  This'll let you parse non-standard XML.
+
+package mxj
+
+import (
+	"encoding/xml"
+)
+
+// CustomDecoder can be used to specify xml.Decoder attribute
+// values, e.g., Strict:false, to be used.  By default CustomDecoder
+// is nil.  If CustomeDecoder != nil, then mxj.XmlCharsetReader variable is
+// ignored and must be set as part of the CustomDecoder value, if needed.
+//	Usage:
+//		mxj.CustomDecoder = &xml.Decoder{Strict:false}
+var CustomDecoder *xml.Decoder
+
+// useCustomDecoder copy over public attributes from customDecoder
+func useCustomDecoder(d *xml.Decoder) {
+	d.Strict = CustomDecoder.Strict
+	d.AutoClose = CustomDecoder.AutoClose
+	d.Entity = CustomDecoder.Entity
+	d.CharsetReader = CustomDecoder.CharsetReader
+	d.DefaultSpace = CustomDecoder.DefaultSpace
+}
+

+ 54 - 0
vendor/github.com/clbanning/mxj/struct.go

@@ -0,0 +1,54 @@
+// Copyright 2012-2017 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+import (
+	"encoding/json"
+	"errors"
+	"reflect"
+
+	// "github.com/fatih/structs"
+)
+
+// Create a new Map value from a structure.  Error returned if argument is not a structure.
+// Only public structure fields are decoded in the Map value. See github.com/fatih/structs#Map
+// for handling of "structs" tags.
+
+// DEPRECATED - import github.com/fatih/structs and cast result of structs.Map to mxj.Map.
+//	import "github.com/fatih/structs"
+//	...
+//	   sm, err := structs.Map(<some struct>)
+//	   if err != nil {
+//	      // handle error
+//	   }
+//	   m := mxj.Map(sm)
+// Alernatively uncomment the old source and import in struct.go.
+func NewMapStruct(structVal interface{}) (Map, error) {
+	return nil, errors.New("deprecated - see package documentation")
+	/*
+		if !structs.IsStruct(structVal) {
+			return nil, errors.New("NewMapStruct() error: argument is not type Struct")
+		}
+		return structs.Map(structVal), nil
+	*/
+}
+
+// Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned
+// if argument is not a pointer or if json.Unmarshal returns an error.
+//	json.Unmarshal structure encoding rules are followed to encode public structure fields.
+func (mv Map) Struct(structPtr interface{}) error {
+	// should check that we're getting a pointer.
+	if reflect.ValueOf(structPtr).Kind() != reflect.Ptr {
+		return errors.New("mv.Struct() error: argument is not type Ptr")
+	}
+
+	m := map[string]interface{}(mv)
+	j, err := json.Marshal(m)
+	if err != nil {
+		return err
+	}
+
+	return json.Unmarshal(j, structPtr)
+}

+ 258 - 0
vendor/github.com/clbanning/mxj/updatevalues.go

@@ -0,0 +1,258 @@
+// Copyright 2012-2014, 2017 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// updatevalues.go - modify a value based on path and possibly sub-keys
+// TODO(clb): handle simple elements with attributes and NewMapXmlSeq Map values.
+
+package mxj
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// Update value based on path and possible sub-key values.
+// A count of the number of values changed and any error are returned.
+// If the count == 0, then no path (and subkeys) matched.
+//	'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified
+//	             or a string value "key:value[:type]" where type is "bool" or "num" to cast the value.
+//	'path' is dot-notation list of keys to traverse; last key in path can be newVal key
+//	       NOTE: 'path' spec does not currently support indexed array references.
+//	'subkeys' are "key:value[:type]" entries that must match for path node
+//             - For attributes prefix the label with the attribute prefix character, by default a 
+//               hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
+//             - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
+//             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
+//	              exclusion critera - e.g., "!author:William T. Gaddis".
+//
+//	NOTES:
+//		1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
+//		2. Values in Maps created using NewMapXmlSeq are map[string]interface{} values with a "#text" key.
+//		3. If values in 'newVal' or 'subkeys' args contain ":", use SetFieldSeparator to an unused symbol,
+//	      perhaps "|".
+func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error) {
+	m := map[string]interface{}(mv)
+
+	// extract the subkeys
+	var subKeyMap map[string]interface{}
+	if len(subkeys) > 0 {
+		var err error
+		subKeyMap, err = getSubKeyMap(subkeys...)
+		if err != nil {
+			return 0, err
+		}
+	}
+
+	// extract key and value from newVal
+	var key string
+	var val interface{}
+	switch newVal.(type) {
+	case map[string]interface{}, Map:
+		switch newVal.(type) { // "fallthrough is not permitted in type switch" (Spec)
+		case Map:
+			newVal = newVal.(Map).Old()
+		}
+		if len(newVal.(map[string]interface{})) != 1 {
+			return 0, fmt.Errorf("newVal map can only have len == 1 - %+v", newVal)
+		}
+		for key, val = range newVal.(map[string]interface{}) {
+		}
+	case string: // split it as a key:value pair
+		ss := strings.Split(newVal.(string), fieldSep)
+		n := len(ss)
+		if n < 2 || n > 3 {
+			return 0, fmt.Errorf("unknown newVal spec - %+v", newVal)
+		}
+		key = ss[0]
+		if n == 2 {
+			val = interface{}(ss[1])
+		} else if n == 3 {
+			switch ss[2] {
+			case "bool", "boolean":
+				nv, err := strconv.ParseBool(ss[1])
+				if err != nil {
+					return 0, fmt.Errorf("can't convert newVal to bool - %+v", newVal)
+				}
+				val = interface{}(nv)
+			case "num", "numeric", "float", "int":
+				nv, err := strconv.ParseFloat(ss[1], 64)
+				if err != nil {
+					return 0, fmt.Errorf("can't convert newVal to float64 - %+v", newVal)
+				}
+				val = interface{}(nv)
+			default:
+				return 0, fmt.Errorf("unknown type for newVal value - %+v", newVal)
+			}
+		}
+	default:
+		return 0, fmt.Errorf("invalid newVal type - %+v", newVal)
+	}
+
+	// parse path
+	keys := strings.Split(path, ".")
+
+	var count int
+	updateValuesForKeyPath(key, val, m, keys, subKeyMap, &count)
+
+	return count, nil
+}
+
+// navigate the path
+func updateValuesForKeyPath(key string, value interface{}, m interface{}, keys []string, subkeys map[string]interface{}, cnt *int) {
+	// ----- at end node: looking at possible node to get 'key' ----
+	if len(keys) == 1 {
+		updateValue(key, value, m, keys[0], subkeys, cnt)
+		return
+	}
+
+	// ----- here we are navigating the path thru the penultimate node --------
+	// key of interest is keys[0] - the next in the path
+	switch keys[0] {
+	case "*": // wildcard - scan all values
+		switch m.(type) {
+		case map[string]interface{}:
+			for _, v := range m.(map[string]interface{}) {
+				updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+			}
+		case []interface{}:
+			for _, v := range m.([]interface{}) {
+				switch v.(type) {
+				// flatten out a list of maps - keys are processed
+				case map[string]interface{}:
+					for _, vv := range v.(map[string]interface{}) {
+						updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
+					}
+				default:
+					updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+				}
+			}
+		}
+	default: // key - must be map[string]interface{}
+		switch m.(type) {
+		case map[string]interface{}:
+			if v, ok := m.(map[string]interface{})[keys[0]]; ok {
+				updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+			}
+		case []interface{}: // may be buried in list
+			for _, v := range m.([]interface{}) {
+				switch v.(type) {
+				case map[string]interface{}:
+					if vv, ok := v.(map[string]interface{})[keys[0]]; ok {
+						updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
+					}
+				}
+			}
+		}
+	}
+}
+
+// change value if key and subkeys are present
+func updateValue(key string, value interface{}, m interface{}, keys0 string, subkeys map[string]interface{}, cnt *int) {
+	// there are two possible options for the value of 'keys0': map[string]interface, []interface{}
+	// and 'key' is a key in the map or is a key in a map in a list.
+	switch m.(type) {
+	case map[string]interface{}: // gotta have the last key
+		if keys0 == "*" {
+			for k := range m.(map[string]interface{}) {
+				updateValue(key, value, m, k, subkeys, cnt)
+			}
+			return
+		}
+		endVal, _ := m.(map[string]interface{})[keys0]
+
+		// if newV key is the end of path, replace the value for path-end
+		// may be []interface{} - means replace just an entry w/ subkeys
+		// otherwise replace the keys0 value if subkeys are there
+		// NOTE: this will replace the subkeys, also
+		if key == keys0 {
+			switch endVal.(type) {
+			case map[string]interface{}:
+				if hasSubKeys(m, subkeys) {
+					(m.(map[string]interface{}))[keys0] = value
+					(*cnt)++
+				}
+			case []interface{}:
+				// without subkeys can't select list member to modify
+				// so key:value spec is it ...
+				if hasSubKeys(m, subkeys) {
+					(m.(map[string]interface{}))[keys0] = value
+					(*cnt)++
+					break
+				}
+				nv := make([]interface{}, 0)
+				var valmodified bool
+				for _, v := range endVal.([]interface{}) {
+					// check entry subkeys
+					if hasSubKeys(v, subkeys) {
+						// replace v with value
+						nv = append(nv, value)
+						valmodified = true
+						(*cnt)++
+						continue
+					}
+					nv = append(nv, v)
+				}
+				if valmodified {
+					(m.(map[string]interface{}))[keys0] = interface{}(nv)
+				}
+			default: // anything else is a strict replacement
+				if hasSubKeys(m, subkeys) {
+					(m.(map[string]interface{}))[keys0] = value
+					(*cnt)++
+				}
+			}
+			return
+		}
+
+		// so value is for an element of endVal
+		// if endVal is a map then 'key' must be there w/ subkeys
+		// if endVal is a list then 'key' must be in a list member w/ subkeys
+		switch endVal.(type) {
+		case map[string]interface{}:
+			if !hasSubKeys(endVal, subkeys) {
+				return
+			}
+			if _, ok := (endVal.(map[string]interface{}))[key]; ok {
+				(endVal.(map[string]interface{}))[key] = value
+				(*cnt)++
+			}
+		case []interface{}: // keys0 points to a list, check subkeys
+			for _, v := range endVal.([]interface{}) {
+				// got to be a map so we can replace value for 'key'
+				vv, vok := v.(map[string]interface{})
+				if !vok {
+					continue
+				}
+				if _, ok := vv[key]; !ok {
+					continue
+				}
+				if !hasSubKeys(vv, subkeys) {
+					continue
+				}
+				vv[key] = value
+				(*cnt)++
+			}
+		}
+	case []interface{}: // key may be in a list member
+		// don't need to handle keys0 == "*"; we're looking at everything, anyway.
+		for _, v := range m.([]interface{}) {
+			// only map values - we're looking for 'key'
+			mm, ok := v.(map[string]interface{})
+			if !ok {
+				continue
+			}
+			if _, ok := mm[key]; !ok {
+				continue
+			}
+			if !hasSubKeys(mm, subkeys) {
+				continue
+			}
+			mm[key] = value
+			(*cnt)++
+		}
+	}
+
+	// return
+}

+ 1339 - 0
vendor/github.com/clbanning/mxj/xml.go

@@ -0,0 +1,1339 @@
+// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// xml.go - basically the core of X2j for map[string]interface{} values.
+//          NewMapXml, NewMapXmlReader, mv.Xml, mv.XmlWriter
+// see x2j and j2x for wrappers to provide end-to-end transformation of XML and JSON messages.
+
+package mxj
+
+import (
+	"bytes"
+	"encoding/json"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
+
+// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
+// Note: if CustomDecoder != nil, then XmlCharsetReader is ignored;
+// set the CustomDecoder attribute instead.
+//   import (
+//	     charset "code.google.com/p/go-charset/charset"
+//	     github.com/clbanning/mxj
+//	 )
+//   ...
+//   mxj.XmlCharsetReader = charset.NewReader
+//   m, merr := mxj.NewMapXml(xmlValue)
+var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+// NewMapXml - convert a XML doc into a Map
+// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
+//	If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
+//
+//	Converting XML to JSON is a simple as:
+//		...
+//		mapVal, merr := mxj.NewMapXml(xmlVal)
+//		if merr != nil {
+//			// handle error
+//		}
+//		jsonVal, jerr := mapVal.Json()
+//		if jerr != nil {
+//			// handle error
+//		}
+//
+//	NOTES:
+//	   1. Declarations, directives, process instructions and comments are NOT parsed.
+//	   2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
+//	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	   3. If CoerceKeysToLower() has been called, then all key values will be lower case.
+//	   4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//	   5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
+func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+	return xmlToMap(xmlVal, r)
+}
+
+// Get next XML doc from an io.Reader as a Map value.  Returns Map value.
+//	NOTES:
+//	   1. Declarations, directives, process instructions and comments are NOT parsed.
+//	   2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
+//	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	   3. If CoerceKeysToLower() has been called, then all key values will be lower case.
+//	   4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+
+	// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
+	// will wrap it in a bufio.Reader and seek on the file beyond where the
+	// xml.Decoder parses!
+	if _, ok := xmlReader.(io.ByteReader); !ok {
+		xmlReader = myByteReader(xmlReader) // see code at EOF
+	}
+
+	// build the map
+	return xmlReaderToMap(xmlReader, r)
+}
+
+// Get next XML doc from an io.Reader as a Map value.  Returns Map value and slice with the raw XML.
+//	NOTES:
+//	   1. Declarations, directives, process instructions and comments are NOT parsed.
+//	   2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
+//	      using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
+//	      See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
+//	      data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
+//	      you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
+//	   3. The 'raw' return value may be larger than the XML text value.
+//	   4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
+//	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	   5. If CoerceKeysToLower() has been called, then all key values will be lower case.
+//	   6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+	// create TeeReader so we can retrieve raw XML
+	buf := make([]byte, 0)
+	wb := bytes.NewBuffer(buf)
+	trdr := myTeeReader(xmlReader, wb) // see code at EOF
+
+	m, err := xmlReaderToMap(trdr, r)
+
+	// retrieve the raw XML that was decoded
+	b := wb.Bytes()
+
+	if err != nil {
+		return nil, b, err
+	}
+
+	return m, b, nil
+}
+
+// xmlReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
+func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
+	// parse the Reader
+	p := xml.NewDecoder(rdr)
+	if CustomDecoder != nil {
+		useCustomDecoder(p)
+	} else {
+		p.CharsetReader = XmlCharsetReader
+	}
+	return xmlToMapParser("", nil, p, r)
+}
+
+// xmlToMap - convert a XML doc into map[string]interface{} value
+func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
+	b := bytes.NewReader(doc)
+	p := xml.NewDecoder(b)
+	if CustomDecoder != nil {
+		useCustomDecoder(p)
+	} else {
+		p.CharsetReader = XmlCharsetReader
+	}
+	return xmlToMapParser("", nil, p, r)
+}
+
+// ===================================== where the work happens =============================
+
+// PrependAttrWithHyphen. Prepend attribute tags with a hyphen.
+// Default is 'true'. (Not applicable to NewMapXmlSeq(), mv.XmlSeq(), etc.)
+//	Note:
+//		If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
+//		marshal'd as <attr_tag>attr</attr_tag> and may be part of a list.
+func PrependAttrWithHyphen(v bool) {
+	if v {
+		attrPrefix = "-"
+		lenAttrPrefix = len(attrPrefix)
+		return
+	}
+	attrPrefix = ""
+	lenAttrPrefix = len(attrPrefix)
+}
+
+// Include sequence id with inner tags. - per Sean Murphy, murphysean84@gmail.com.
+var includeTagSeqNum bool
+
+// IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting
+// its position when parsed. This is of limited usefulness, since list values cannot
+// be tagged with "_seq" without changing their depth in the Map.
+// So THIS SHOULD BE USED WITH CAUTION - see the test cases. Here's a sample of what
+// you get.
+/*
+		<Obj c="la" x="dee" h="da">
+			<IntObj id="3"/>
+			<IntObj1 id="1"/>
+			<IntObj id="2"/>
+			<StrObj>hello</StrObj>
+		</Obj>
+
+	parses as:
+
+		{
+		Obj:{
+			"-c":"la",
+			"-h":"da",
+			"-x":"dee",
+			"intObj":[
+				{
+					"-id"="3",
+					"_seq":"0" // if mxj.Cast is passed, then: "_seq":0
+				},
+				{
+					"-id"="2",
+					"_seq":"2"
+				}],
+			"intObj1":{
+				"-id":"1",
+				"_seq":"1"
+				},
+			"StrObj":{
+				"#text":"hello", // simple element value gets "#text" tag
+				"_seq":"3"
+				}
+			}
+		}
+*/
+func IncludeTagSeqNum(b ...bool) {
+	if len(b) == 0 {
+		includeTagSeqNum = !includeTagSeqNum
+	} else if len(b) == 1 {
+		includeTagSeqNum = b[0]
+	}
+}
+
+// all keys will be "lower case"
+var lowerCase bool
+
+// Coerce all tag values to keys in lower case.  This is useful if you've got sources with variable
+// tag capitalization, and you want to use m.ValuesForKeys(), etc., with the key or path spec
+// in lower case.
+//	CoerceKeysToLower() will toggle the coercion flag true|false - on|off
+//	CoerceKeysToLower(true|false) will set the coercion flag on|off
+//
+//	NOTE: only recognized by NewMapXml, NewMapXmlReader, and NewMapXmlReaderRaw functions as well as
+//	      the associated HandleXmlReader and HandleXmlReaderRaw.
+func CoerceKeysToLower(b ...bool) {
+	if len(b) == 0 {
+		lowerCase = !lowerCase
+	} else if len(b) == 1 {
+		lowerCase = b[0]
+	}
+}
+
+// disableTrimWhiteSpace sets if the white space should be removed or not
+var disableTrimWhiteSpace bool
+var trimRunes = "\t\r\b\n "
+
+// DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
+// no argument is provided, trim white space will be disabled.
+func DisableTrimWhiteSpace(b ...bool) {
+	if len(b) == 0 {
+		disableTrimWhiteSpace = true
+	} else {
+		disableTrimWhiteSpace = b[0]
+	}
+
+	if disableTrimWhiteSpace {
+		trimRunes = "\t\r\b\n"
+	} else {
+		trimRunes = "\t\r\b\n "
+	}
+}
+
+// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
+// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
+// and adding a SetAttrPrefix(s string) function.
+
+var attrPrefix string = `-` // the default
+var lenAttrPrefix int = 1   // the default
+
+// SetAttrPrefix changes the default, "-", to the specified value, s.
+// SetAttrPrefix("") is the same as PrependAttrWithHyphen(false).
+// (Not applicable for NewMapXmlSeq(), mv.XmlSeq(), etc.)
+func SetAttrPrefix(s string) {
+	attrPrefix = s
+	lenAttrPrefix = len(attrPrefix)
+}
+
+// 18jan17: Allows user to specify if the map keys should be in snake case instead
+// of the default hyphenated notation.
+var snakeCaseKeys bool
+
+// CoerceKeysToSnakeCase changes the default, false, to the specified value, b.
+// Note: the attribute prefix will be a hyphen, '-', or what ever string value has
+// been specified using SetAttrPrefix.
+func CoerceKeysToSnakeCase(b ...bool) {
+	if len(b) == 0 {
+		snakeCaseKeys = !snakeCaseKeys
+	} else if len(b) == 1 {
+		snakeCaseKeys = b[0]
+	}
+}
+
+// 10jan19: use of pull request #57 should be conditional - legacy code assumes
+// numeric values are float64.
+var castToInt bool
+
+// CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
+// default float64. Repeated calls with no argument will toggle this on/off, or this
+// handling will be set with the value of 'b'.
+func CastValuesToInt(b ...bool) {
+	if len(b) == 0 {
+		castToInt = !castToInt
+	} else if len(b) == 1 {
+		castToInt = b[0]
+	}
+}
+
+// 05feb17: support processing XMPP streams (issue #36)
+var handleXMPPStreamTag bool
+
+// HandleXMPPStreamTag causes decoder to parse XMPP <stream:stream> elements.
+// If called with no argument, XMPP stream element handling is toggled on/off.
+// (See xmppStream_test.go for example.)
+//	If called with NewMapXml, NewMapXmlReader, New MapXmlReaderRaw the "stream"
+//	element will be  returned as:
+//		map["stream"]interface{}{map[-<attrs>]interface{}}.
+//	If called with NewMapSeq, NewMapSeqReader, NewMapSeqReaderRaw the "stream"
+//	element will be returned as:
+//		map["stream:stream"]interface{}{map["#attr"]interface{}{map[string]interface{}}}
+//		where the "#attr" values have "#text" and "#seq" keys. (See NewMapXmlSeq.)
+func HandleXMPPStreamTag(b ...bool) {
+	if len(b) == 0 {
+		handleXMPPStreamTag = !handleXMPPStreamTag
+	} else if len(b) == 1 {
+		handleXMPPStreamTag = b[0]
+	}
+}
+
+// 21jan18 - decode all values as map["#text":value] (issue #56)
+var decodeSimpleValuesAsMap bool
+
+// DecodeSimpleValuesAsMap forces all values to be decoded as map["#text":<value>].
+// If called with no argument, the decoding is toggled on/off.
+//
+// By default the NewMapXml functions decode simple values without attributes as
+// map[<tag>:<value>]. This function causes simple values without attributes to be
+// decoded the same as simple values with attributes - map[<tag>:map["#text":<value>]].
+func DecodeSimpleValuesAsMap(b ...bool) {
+	if len(b) == 0 {
+		decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
+	} else if len(b) == 1 {
+		decodeSimpleValuesAsMap = b[0]
+	}
+}
+
+// xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly.
+// A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one.
+// We've removed the intermediate *node tree with the allocation and subsequent rescanning.
+func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
+	if lowerCase {
+		skey = strings.ToLower(skey)
+	}
+	if snakeCaseKeys {
+		skey = strings.Replace(skey, "-", "_", -1)
+	}
+
+	// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
+	// Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value.
+	var n, na map[string]interface{}
+	var seq int // for includeTagSeqNum
+
+	// Allocate maps and load attributes, if any.
+	// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
+	//       to get StartElement then recurse with skey==xml.StartElement.Name.Local
+	//       where we begin allocating map[string]interface{} values 'n' and 'na'.
+	if skey != "" {
+		n = make(map[string]interface{})  // old n
+		na = make(map[string]interface{}) // old n.nodes
+		if len(a) > 0 {
+			for _, v := range a {
+				if snakeCaseKeys {
+					v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
+				}
+				var key string
+				key = attrPrefix + v.Name.Local
+				if lowerCase {
+					key = strings.ToLower(key)
+				}
+				if xmlEscapeCharsDecoder { // per issue#84
+					v.Value = escapeChars(v.Value)
+				}
+				na[key] = cast(v.Value, r, key)
+			}
+		}
+	}
+	// Return XMPP <stream:stream> message.
+	if handleXMPPStreamTag && skey == "stream" {
+		n[skey] = na
+		return n, nil
+	}
+
+	for {
+		t, err := p.Token()
+		if err != nil {
+			if err != io.EOF {
+				return nil, errors.New("xml.Decoder.Token() - " + err.Error())
+			}
+			return nil, err
+		}
+		switch t.(type) {
+		case xml.StartElement:
+			tt := t.(xml.StartElement)
+
+			// First call to xmlToMapParser() doesn't pass xml.StartElement - the map key.
+			// So when the loop is first entered, the first token is the root tag along
+			// with any attributes, which we process here.
+			//
+			// Subsequent calls to xmlToMapParser() will pass in tag+attributes for
+			// processing before getting the next token which is the element value,
+			// which is done above.
+			if skey == "" {
+				return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
+			}
+
+			// If not initializing the map, parse the element.
+			// len(nn) == 1, necessarily - it is just an 'n'.
+			nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
+			if err != nil {
+				return nil, err
+			}
+
+			// The nn map[string]interface{} value is a na[nn_key] value.
+			// We need to see if nn_key already exists - means we're parsing a list.
+			// This may require converting na[nn_key] value into []interface{} type.
+			// First, extract the key:val for the map - it's a singleton.
+			// Note:
+			// * if CoerceKeysToLower() called, then key will be lower case.
+			// * if CoerceKeysToSnakeCase() called, then key will be converted to snake case.
+			var key string
+			var val interface{}
+			for key, val = range nn {
+				break
+			}
+
+			// IncludeTagSeqNum requests that the element be augmented with a "_seq" sub-element.
+			// In theory, we don't need this if len(na) == 1. But, we don't know what might
+			// come next - we're only parsing forward.  So if you ask for 'includeTagSeqNum' you
+			// get it on every element. (Personally, I never liked this, but I added it on request
+			// and did get a $50 Amazon gift card in return - now we support it for backwards compatibility!)
+			if includeTagSeqNum {
+				switch val.(type) {
+				case []interface{}:
+					// noop - There's no clean way to handle this w/o changing message structure.
+				case map[string]interface{}:
+					val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
+					seq++
+				case interface{}: // a non-nil simple element: string, float64, bool
+					v := map[string]interface{}{"#text": val}
+					v["_seq"] = seq
+					seq++
+					val = v
+				}
+			}
+
+			// 'na' holding sub-elements of n.
+			// See if 'key' already exists.
+			// If 'key' exists, then this is a list, if not just add key:val to na.
+			if v, ok := na[key]; ok {
+				var a []interface{}
+				switch v.(type) {
+				case []interface{}:
+					a = v.([]interface{})
+				default: // anything else - note: v.(type) != nil
+					a = []interface{}{v}
+				}
+				a = append(a, val)
+				na[key] = a
+			} else {
+				na[key] = val // save it as a singleton
+			}
+		case xml.EndElement:
+			// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
+			if len(n) == 0 {
+				// If len(na)==0 we have an empty element == "";
+				// it has no xml.Attr nor xml.CharData.
+				// Note: in original node-tree parser, val defaulted to "";
+				// so we always had the default if len(node.nodes) == 0.
+				if len(na) > 0 {
+					n[skey] = na
+				} else {
+					n[skey] = "" // empty element
+				}
+			}
+			return n, nil
+		case xml.CharData:
+			// clean up possible noise
+			tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
+			if xmlEscapeCharsDecoder { // issue#84
+				tt = escapeChars(tt)
+			}
+			if len(tt) > 0 {
+				if len(na) > 0 || decodeSimpleValuesAsMap {
+					na["#text"] = cast(tt, r, "#text")
+				} else if skey != "" {
+					n[skey] = cast(tt, r, skey)
+				} else {
+					// per Adrian (http://www.adrianlungu.com/) catch stray text
+					// in decoder stream -
+					// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
+					// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
+					// a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
+					continue
+				}
+			}
+		default:
+			// noop
+		}
+	}
+}
+
+var castNanInf bool
+
+// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
+// By default, these values will be decoded as 'string'.
+func CastNanInf(b ...bool) {
+	if len(b) == 0 {
+		castNanInf = !castNanInf
+	} else if len(b) == 1 {
+		castNanInf = b[0]
+	}
+}
+
+// cast - try to cast string values to bool or float64
+// 't' is the tag key that can be checked for 'not-casting'
+func cast(s string, r bool, t string) interface{} {
+	if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
+		// call the check-function here with 't[0]'
+		// if 'true' return s
+		return s
+	}
+
+	if r {
+		// handle nan and inf
+		if !castNanInf {
+			switch strings.ToLower(s) {
+			case "nan", "inf", "-inf":
+				return s
+			}
+		}
+
+		// handle numeric strings ahead of boolean
+		if castToInt {
+			if f, err := strconv.ParseInt(s, 10, 64); err == nil {
+				return f
+			}
+			if f, err := strconv.ParseUint(s, 10, 64); err == nil {
+				return f
+			}
+		}
+
+		if castToFloat {
+			if f, err := strconv.ParseFloat(s, 64); err == nil {
+				return f
+			}
+		}
+
+		// ParseBool treats "1"==true & "0"==false, we've already scanned those
+		// values as float64. See if value has 't' or 'f' as initial screen to
+		// minimize calls to ParseBool; also, see if len(s) < 6.
+		if castToBool {
+			if len(s) > 0 && len(s) < 6 {
+				switch s[:1] {
+				case "t", "T", "f", "F":
+					if b, err := strconv.ParseBool(s); err == nil {
+						return b
+					}
+				}
+			}
+		}
+	}
+	return s
+}
+
+// pull request, #59
+var castToFloat = true
+
+// CastValuesToFloat can be used to skip casting to float64 when
+// "cast" argument is 'true' in NewMapXml, etc.
+// Default is true.
+func CastValuesToFloat(b ...bool) {
+	if len(b) == 0 {
+		castToFloat = !castToFloat
+	} else if len(b) == 1 {
+		castToFloat = b[0]
+	}
+}
+
+var castToBool = true
+
+// CastValuesToBool can be used to skip casting to bool when
+// "cast" argument is 'true' in NewMapXml, etc.
+// Default is true.
+func CastValuesToBool(b ...bool) {
+	if len(b) == 0 {
+		castToBool = !castToBool
+	} else if len(b) == 1 {
+		castToBool = b[0]
+	}
+}
+
+// checkTagToSkip - switch to address Issue #58
+
+var checkTagToSkip func(string) bool
+
+// SetCheckTagToSkipFunc registers function to test whether the value
+// for a tag should be cast to bool or float64 when "cast" argument is 'true'.
+// (Dot tag path notation is not supported.)
+// NOTE: key may be "#text" if it's a simple element with attributes
+//       or "decodeSimpleValuesAsMap == true".
+// NOTE: does not apply to NewMapXmlSeq... functions.
+func SetCheckTagToSkipFunc(fn func(string) bool) {
+	checkTagToSkip = fn
+}
+
+// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
+
+// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
+
+const (
+	DefaultRootTag = "doc"
+)
+
+var useGoXmlEmptyElemSyntax bool
+
+// XmlGoEmptyElemSyntax() - <tag ...></tag> rather than <tag .../>.
+//	Go's encoding/xml package marshals empty XML elements as <tag ...></tag>.  By default this package
+//	encodes empty elements as <tag .../>.  If you're marshaling Map values that include structures
+//	(which are passed to xml.Marshal for encoding), this will let you conform to the standard package.
+func XmlGoEmptyElemSyntax() {
+	useGoXmlEmptyElemSyntax = true
+}
+
+// XmlDefaultEmptyElemSyntax() - <tag .../> rather than <tag ...></tag>.
+// Return XML encoding for empty elements to the default package setting.
+// Reverses effect of XmlGoEmptyElemSyntax().
+func XmlDefaultEmptyElemSyntax() {
+	useGoXmlEmptyElemSyntax = false
+}
+
+// Encode a Map as XML.  The companion of NewMapXml().
+// The following rules apply.
+//    - The key label "#text" is treated as the value for a simple element with attributes.
+//    - Map keys that begin with a hyphen, '-', are interpreted as attributes.
+//      It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
+//    - Map value type encoding:
+//          > string, bool, float64, int, int32, int64, float32: per "%v" formating
+//          > []bool, []uint8: by casting to string
+//          > structures, etc.: handed to xml.Marshal() - if there is an error, the element
+//            value is "UNKNOWN"
+//    - Elements with only attribute values or are null are terminated using "/>".
+//    - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
+//      Thus, `{ "key":"value" }` encodes as "<key>value</key>".
+//    - To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().
+// The attributes tag=value pairs are alphabetized by "tag".  Also, when encoding map[string]interface{} values -
+// complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.
+func (mv Map) Xml(rootTag ...string) ([]byte, error) {
+	m := map[string]interface{}(mv)
+	var err error
+	b := new(bytes.Buffer)
+	p := new(pretty) // just a stub
+
+	if len(m) == 1 && len(rootTag) == 0 {
+		for key, value := range m {
+			// if it an array, see if all values are map[string]interface{}
+			// we force a new root tag if we'll end up with no key:value in the list
+			// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
+			switch value.(type) {
+			case []interface{}:
+				for _, v := range value.([]interface{}) {
+					switch v.(type) {
+					case map[string]interface{}: // noop
+					default: // anything else
+						err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
+						goto done
+					}
+				}
+			}
+			err = marshalMapToXmlIndent(false, b, key, value, p)
+		}
+	} else if len(rootTag) == 1 {
+		err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
+	} else {
+		err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
+	}
+done:
+	return b.Bytes(), err
+}
+
+// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
+// The names will also provide a key for the number of return arguments.
+
+// Writes the Map as  XML on the Writer.
+// See Xml() for encoding rules.
+func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
+	x, err := mv.Xml(rootTag...)
+	if err != nil {
+		return err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return err
+}
+
+// Writes the Map as  XML on the Writer. []byte is the raw XML that was written.
+// See Xml() for encoding rules.
+/*
+func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
+	x, err := mv.Xml(rootTag...)
+	if err != nil {
+		return x, err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return x, err
+}
+*/
+
+// Writes the Map as pretty XML on the Writer.
+// See Xml() for encoding rules.
+func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
+	x, err := mv.XmlIndent(prefix, indent, rootTag...)
+	if err != nil {
+		return err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return err
+}
+
+// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
+// See Xml() for encoding rules.
+/*
+func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
+	x, err := mv.XmlIndent(prefix, indent, rootTag...)
+	if err != nil {
+		return x, err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return x, err
+}
+*/
+
+// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
+
+// --------------  Handle XML stream by processing Map value --------------------
+
+// Default poll delay to keep Handler from spinning on an open stream
+// like sitting on os.Stdin waiting for imput.
+var xhandlerPollInterval = time.Millisecond
+
+// Bulk process XML using handlers that process a Map value.
+//	'rdr' is an io.Reader for XML (stream)
+//	'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
+//	'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
+//	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+//	      This means that you can stop reading the file on error or after processing a particular message.
+//	      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
+func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
+	var n int
+	for {
+		m, merr := NewMapXmlReader(xmlReader)
+		n++
+
+		// handle error condition with errhandler
+		if merr != nil && merr != io.EOF {
+			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
+			if ok := errHandler(merr); !ok {
+				// caused reader termination
+				return merr
+			}
+			continue
+		}
+
+		// pass to maphandler
+		if len(m) != 0 {
+			if ok := mapHandler(m); !ok {
+				break
+			}
+		} else if merr != io.EOF {
+			time.Sleep(xhandlerPollInterval)
+		}
+
+		if merr == io.EOF {
+			break
+		}
+	}
+	return nil
+}
+
+// Bulk process XML using handlers that process a Map value and the raw XML.
+//	'rdr' is an io.Reader for XML (stream)
+//	'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
+//	'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
+//	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+//	      This means that you can stop reading the file on error or after processing a particular message.
+//	      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
+//	See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
+func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
+	var n int
+	for {
+		m, raw, merr := NewMapXmlReaderRaw(xmlReader)
+		n++
+
+		// handle error condition with errhandler
+		if merr != nil && merr != io.EOF {
+			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
+			if ok := errHandler(merr, raw); !ok {
+				// caused reader termination
+				return merr
+			}
+			continue
+		}
+
+		// pass to maphandler
+		if len(m) != 0 {
+			if ok := mapHandler(m, raw); !ok {
+				break
+			}
+		} else if merr != io.EOF {
+			time.Sleep(xhandlerPollInterval)
+		}
+
+		if merr == io.EOF {
+			break
+		}
+	}
+	return nil
+}
+
+// ----------------- END: Handle XML stream by processing Map value --------------
+
+// --------  a hack of io.TeeReader ... need one that's an io.ByteReader for xml.NewDecoder() ----------
+
+// This is a clone of io.TeeReader with the additional method t.ReadByte().
+// Thus, this TeeReader is also an io.ByteReader.
+// This is necessary because xml.NewDecoder uses a ByteReader not a Reader. It appears to have been written
+// with bufio.Reader or bytes.Reader in mind ... not a generic io.Reader, which doesn't have to have ReadByte()..
+// If NewDecoder is passed a Reader that does not satisfy ByteReader() it wraps the Reader with
+// bufio.NewReader and uses ReadByte rather than Read that runs the TeeReader pipe logic.
+
+type teeReader struct {
+	r io.Reader
+	w io.Writer
+	b []byte
+}
+
+func myTeeReader(r io.Reader, w io.Writer) io.Reader {
+	b := make([]byte, 1)
+	return &teeReader{r, w, b}
+}
+
+// need for io.Reader - but we don't use it ...
+func (t *teeReader) Read(p []byte) (int, error) {
+	return 0, nil
+}
+
+func (t *teeReader) ReadByte() (byte, error) {
+	n, err := t.r.Read(t.b)
+	if n > 0 {
+		if _, err := t.w.Write(t.b[:1]); err != nil {
+			return t.b[0], err
+		}
+	}
+	return t.b[0], err
+}
+
+// For use with NewMapXmlReader & NewMapXmlSeqReader.
+type byteReader struct {
+	r io.Reader
+	b []byte
+}
+
+func myByteReader(r io.Reader) io.Reader {
+	b := make([]byte, 1)
+	return &byteReader{r, b}
+}
+
+// Need for io.Reader interface ...
+// Needed if reading a malformed http.Request.Body - issue #38.
+func (b *byteReader) Read(p []byte) (int, error) {
+	return b.r.Read(p)
+}
+
+func (b *byteReader) ReadByte() (byte, error) {
+	_, err := b.r.Read(b.b)
+	if len(b.b) > 0 {
+		return b.b[0], nil
+	}
+	var c byte
+	return c, err
+}
+
+// ----------------------- END: io.TeeReader hack -----------------------------------
+
+// ---------------------- XmlIndent - from j2x package ----------------------------
+
+// Encode a map[string]interface{} as a pretty XML string.
+// See Xml for encoding rules.
+func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
+	m := map[string]interface{}(mv)
+
+	var err error
+	b := new(bytes.Buffer)
+	p := new(pretty)
+	p.indent = indent
+	p.padding = prefix
+
+	if len(m) == 1 && len(rootTag) == 0 {
+		// this can extract the key for the single map element
+		// use it if it isn't a key for a list
+		for key, value := range m {
+			if _, ok := value.([]interface{}); ok {
+				err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
+			} else {
+				err = marshalMapToXmlIndent(true, b, key, value, p)
+			}
+		}
+	} else if len(rootTag) == 1 {
+		err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
+	} else {
+		err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
+	}
+	return b.Bytes(), err
+}
+
+type pretty struct {
+	indent   string
+	cnt      int
+	padding  string
+	mapDepth int
+	start    int
+}
+
+func (p *pretty) Indent() {
+	p.padding += p.indent
+	p.cnt++
+}
+
+func (p *pretty) Outdent() {
+	if p.cnt > 0 {
+		p.padding = p.padding[:len(p.padding)-len(p.indent)]
+		p.cnt--
+	}
+}
+
+// where the work actually happens
+// returns an error if an attribute is not atomic
+// NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
+func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
+	var err error
+	var endTag bool
+	var isSimple bool
+	var elen int
+	p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
+
+	// per issue #48, 18apr18 - try and coerce maps to map[string]interface{}
+	// Don't need for mapToXmlSeqIndent, since maps there are decoded by NewMapXmlSeq().
+	if reflect.ValueOf(value).Kind() == reflect.Map {
+		switch value.(type) {
+		case map[string]interface{}:
+		default:
+			val := make(map[string]interface{})
+			vv := reflect.ValueOf(value)
+			keys := vv.MapKeys()
+			for _, k := range keys {
+				val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
+			}
+			value = val
+		}
+	}
+
+	// 14jul20.  The following block of code has become something of a catch all for odd stuff
+	// that might be passed in as a result of casting an arbitrary map[<T>]<T> to an mxj.Map
+	// value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
+	switch value.(type) {
+	// these types are handled during encoding
+	case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
+	case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
+	case []interface{}:
+	default:
+		// see if value is a struct, if so marshal using encoding/xml package
+		if reflect.ValueOf(value).Kind() == reflect.Struct {
+			if v, err := xml.Marshal(value); err != nil {
+				return err
+			} else {
+				value = string(v)
+			}
+		} else {
+			// coerce eveything else into a string value
+			value = fmt.Sprint(value)
+		}
+	}
+
+	// start the XML tag with required indentaton and padding
+	if doIndent {
+		if _, err = b.WriteString(p.padding); err != nil {
+			return err
+		}
+	}
+	switch value.(type) {
+	case []interface{}:
+	default:
+		if _, err = b.WriteString(`<` + key); err != nil {
+			return err
+		}
+	}
+
+	switch value.(type) {
+	case map[string]interface{}:
+		vv := value.(map[string]interface{})
+		lenvv := len(vv)
+		// scan out attributes - attribute keys have prepended attrPrefix
+		attrlist := make([][2]string, len(vv))
+		var n int
+		var ss string
+		for k, v := range vv {
+			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
+				switch v.(type) {
+				case string:
+					if xmlEscapeChars {
+						ss = escapeChars(v.(string))
+					} else {
+						ss = v.(string)
+					}
+					attrlist[n][0] = k[lenAttrPrefix:]
+					attrlist[n][1] = ss
+				case float64, bool, int, int32, int64, float32, json.Number:
+					attrlist[n][0] = k[lenAttrPrefix:]
+					attrlist[n][1] = fmt.Sprintf("%v", v)
+				case []byte:
+					if xmlEscapeChars {
+						ss = escapeChars(string(v.([]byte)))
+					} else {
+						ss = string(v.([]byte))
+					}
+					attrlist[n][0] = k[lenAttrPrefix:]
+					attrlist[n][1] = ss
+				default:
+					return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
+				}
+				n++
+			}
+		}
+		if n > 0 {
+			attrlist = attrlist[:n]
+			sort.Sort(attrList(attrlist))
+			for _, v := range attrlist {
+				if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
+					return err
+				}
+			}
+		}
+		// only attributes?
+		if n == lenvv {
+			if useGoXmlEmptyElemSyntax {
+				if _, err = b.WriteString(`</` + key + ">"); err != nil {
+					return err
+				}
+			} else {
+				if _, err = b.WriteString(`/>`); err != nil {
+					return err
+				}
+			}
+			break
+		}
+
+		// simple element? Note: '#text" is an invalid XML tag.
+		if v, ok := vv["#text"]; ok && n+1 == lenvv {
+			switch v.(type) {
+			case string:
+				if xmlEscapeChars {
+					v = escapeChars(v.(string))
+				} else {
+					v = v.(string)
+				}
+			case []byte:
+				if xmlEscapeChars {
+					v = escapeChars(string(v.([]byte)))
+				}
+			}
+			if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
+				return err
+			}
+			endTag = true
+			elen = 1
+			isSimple = true
+			break
+		} else if ok {
+			// Handle edge case where simple element with attributes
+			// is unmarshal'd using NewMapXml() where attribute prefix
+			// has been set to "".
+			// TODO(clb): should probably scan all keys for invalid chars.
+			return fmt.Errorf("invalid attribute key label: #text - due to attributes not being prefixed")
+		}
+
+		// close tag with possible attributes
+		if _, err = b.WriteString(">"); err != nil {
+			return err
+		}
+		if doIndent {
+			// *s += "\n"
+			if _, err = b.WriteString("\n"); err != nil {
+				return err
+			}
+		}
+		// something more complex
+		p.mapDepth++
+		// extract the map k:v pairs and sort on key
+		elemlist := make([][2]interface{}, len(vv))
+		n = 0
+		for k, v := range vv {
+			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
+				continue
+			}
+			elemlist[n][0] = k
+			elemlist[n][1] = v
+			n++
+		}
+		elemlist = elemlist[:n]
+		sort.Sort(elemList(elemlist))
+		var i int
+		for _, v := range elemlist {
+			switch v[1].(type) {
+			case []interface{}:
+			default:
+				if i == 0 && doIndent {
+					p.Indent()
+				}
+			}
+			i++
+			if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
+				return err
+			}
+			switch v[1].(type) {
+			case []interface{}: // handled in []interface{} case
+			default:
+				if doIndent {
+					p.Outdent()
+				}
+			}
+			i--
+		}
+		p.mapDepth--
+		endTag = true
+		elen = 1 // we do have some content ...
+	case []interface{}:
+		// special case - found during implementing Issue #23
+		if len(value.([]interface{})) == 0 {
+			if doIndent {
+				if _, err = b.WriteString(p.padding + p.indent); err != nil {
+					return err
+				}
+			}
+			if _, err = b.WriteString("<" + key); err != nil {
+				return err
+			}
+			elen = 0
+			endTag = true
+			break
+		}
+		for _, v := range value.([]interface{}) {
+			if doIndent {
+				p.Indent()
+			}
+			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
+				return err
+			}
+			if doIndent {
+				p.Outdent()
+			}
+		}
+		return nil
+	case []string:
+		// This was added by https://github.com/slotix ... not a type that
+		// would be encountered if mv generated from NewMapXml, NewMapJson.
+		// Could be encountered in AnyXml(), so we'll let it stay, though
+		// it should be merged with case []interface{}, above.
+		//quick fix for []string type
+		//[]string should be treated exaclty as []interface{}
+		if len(value.([]string)) == 0 {
+			if doIndent {
+				if _, err = b.WriteString(p.padding + p.indent); err != nil {
+					return err
+				}
+			}
+			if _, err = b.WriteString("<" + key); err != nil {
+				return err
+			}
+			elen = 0
+			endTag = true
+			break
+		}
+		for _, v := range value.([]string) {
+			if doIndent {
+				p.Indent()
+			}
+			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
+				return err
+			}
+			if doIndent {
+				p.Outdent()
+			}
+		}
+		return nil
+	case nil:
+		// terminate the tag
+		if doIndent {
+			// *s += p.padding
+			if _, err = b.WriteString(p.padding); err != nil {
+				return err
+			}
+		}
+		if _, err = b.WriteString("<" + key); err != nil {
+			return err
+		}
+		endTag, isSimple = true, true
+		break
+	default: // handle anything - even goofy stuff
+		elen = 0
+		switch value.(type) {
+		case string:
+			v := value.(string)
+			if xmlEscapeChars {
+				v = escapeChars(v)
+			}
+			elen = len(v)
+			if elen > 0 {
+				// *s += ">" + v
+				if _, err = b.WriteString(">" + v); err != nil {
+					return err
+				}
+			}
+		case float64, bool, int, int32, int64, float32, json.Number:
+			v := fmt.Sprintf("%v", value)
+			elen = len(v) // always > 0
+			if _, err = b.WriteString(">" + v); err != nil {
+				return err
+			}
+		case []byte: // NOTE: byte is just an alias for uint8
+			// similar to how xml.Marshal handles []byte structure members
+			v := string(value.([]byte))
+			if xmlEscapeChars {
+				v = escapeChars(v)
+			}
+			elen = len(v)
+			if elen > 0 {
+				// *s += ">" + v
+				if _, err = b.WriteString(">" + v); err != nil {
+					return err
+				}
+			}
+		default:
+			if _, err = b.WriteString(">"); err != nil {
+				return err
+			}
+			var v []byte
+			var err error
+			if doIndent {
+				v, err = xml.MarshalIndent(value, p.padding, p.indent)
+			} else {
+				v, err = xml.Marshal(value)
+			}
+			if err != nil {
+				if _, err = b.WriteString(">UNKNOWN"); err != nil {
+					return err
+				}
+			} else {
+				elen = len(v)
+				if elen > 0 {
+					if _, err = b.Write(v); err != nil {
+						return err
+					}
+				}
+			}
+		}
+		isSimple = true
+		endTag = true
+	}
+	if endTag {
+		if doIndent {
+			if !isSimple {
+				if _, err = b.WriteString(p.padding); err != nil {
+					return err
+				}
+			}
+		}
+		if elen > 0 || useGoXmlEmptyElemSyntax {
+			if elen == 0 {
+				if _, err = b.WriteString(">"); err != nil {
+					return err
+				}
+			}
+			if _, err = b.WriteString(`</` + key + ">"); err != nil {
+				return err
+			}
+		} else {
+			if _, err = b.WriteString(`/>`); err != nil {
+				return err
+			}
+		}
+	}
+	if doIndent {
+		if p.cnt > p.start {
+			if _, err = b.WriteString("\n"); err != nil {
+				return err
+			}
+		}
+		p.Outdent()
+	}
+
+	return nil
+}
+
+// ============================ sort interface implementation =================
+
+type attrList [][2]string
+
+func (a attrList) Len() int {
+	return len(a)
+}
+
+func (a attrList) Swap(i, j int) {
+	a[i], a[j] = a[j], a[i]
+}
+
+func (a attrList) Less(i, j int) bool {
+	return a[i][0] <= a[j][0]
+}
+
+type elemList [][2]interface{}
+
+func (e elemList) Len() int {
+	return len(e)
+}
+
+func (e elemList) Swap(i, j int) {
+	e[i], e[j] = e[j], e[i]
+}
+
+func (e elemList) Less(i, j int) bool {
+	return e[i][0].(string) <= e[j][0].(string)
+}

+ 850 - 0
vendor/github.com/clbanning/mxj/xmlseq.go

@@ -0,0 +1,850 @@
+// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding.
+// Also, handles comments, directives and process instructions.
+
+package mxj
+
+import (
+	"bytes"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"sort"
+	"strings"
+)
+
+// MapSeq is like Map but contains seqencing indices to allow recovering the original order of
+// the XML elements when the map[string]interface{} is marshaled. Element attributes are
+// stored as a map["#attr"]map[<attr_key>]map[string]interface{}{"#text":"<value>", "#seq":<attr_index>}
+// value instead of denoting the keys with a prefix character.  Also, comments, directives and
+// process instructions are preserved.
+type MapSeq map[string]interface{}
+
+// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
+// in the XML data stream and the element is not contained in an XML object with a root element.
+var NoRoot = errors.New("no root key")
+var NO_ROOT = NoRoot // maintain backwards compatibility
+
+// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
+
+// NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
+// as map["#seq"]<int value>.
+// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
+// NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml() / msv.XmlIndent().
+//	• attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
+//	• all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
+//	• lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
+//	  include a "#seq" k:v pair based on sequence they are decoded.  Thus, XML like:
+//	      <doc>
+//	         <ltag>value 1</ltag>
+//	         <newtag>value 2</newtag>
+//	         <ltag>value 3</ltag>
+//	      </doc>
+//	  is decoded as:
+//	    doc :
+//	      ltag :[[]interface{}]
+//	        [item: 0]
+//	          #seq :[int] 0
+//	          #text :[string] value 1
+//	        [item: 1]
+//	          #seq :[int] 2
+//	          #text :[string] value 3
+//	      newtag :
+//	        #seq :[int] 1
+//	        #text :[string] value 2
+//	  It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
+//	• comments - "<!--comment-->" -  are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
+//	• directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
+//	• process instructions  - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
+//	  is of map[string]interface{} type with the following keys: #target, #inst, and #seq.
+//	• comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
+//	  map[string]interface{} and the error value 'NoRoot'.
+//	• note: "<![CDATA[" syntax is lost in xml.Decode parser - and is not handled here, either.
+//	   and: "\r\n" is converted to "\n"
+//
+//	NOTES:
+//	   1. The 'xmlVal' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
+//	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	   2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
+//	      re-encode the message in its original structure.
+//	   3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//
+//	NAME SPACES:
+//	   1. Keys in the MapSeq value that are parsed from a <name space prefix>:<local name> tag preserve the
+//	      "<prefix>:" notation rather than stripping it as with NewMapXml().
+//	   2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
+//
+//	ERRORS:
+//	   1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
+//	      "#directive" or #procinst" key.
+func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+	return xmlSeqToMap(xmlVal, r)
+}
+
+// NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
+//	NOTES:
+//	   1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
+//	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	   2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
+//	      re-encode the message in its original structure.
+//	   3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//
+//	ERRORS:
+//	   1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
+//	      "#directive" or #procinst" key.
+func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+
+	// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
+	// will wrap it in a bufio.Reader and seek on the file beyond where the
+	// xml.Decoder parses!
+	if _, ok := xmlReader.(io.ByteReader); !ok {
+		xmlReader = myByteReader(xmlReader) // see code at EOF
+	}
+
+	// build the map
+	return xmlSeqReaderToMap(xmlReader, r)
+}
+
+// NewMapXmlSeqReaderRaw returns the  next XML doc from  an io.Reader as a MapSeq value.
+// Returns MapSeq value, slice with the raw XML, and any error.
+//	NOTES:
+//	   1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
+//	      using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
+//	      See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
+//	      data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
+//	      you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
+//	    2. The 'raw' return value may be larger than the XML text value.
+//	    3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
+//	       extraneous xml.CharData will be ignored unless io.EOF is reached first.
+//	    4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
+//	       re-encode the message in its original structure.
+//	    5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//
+//	ERRORS:
+//	    1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
+//	       "#directive" or #procinst" key.
+func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (MapSeq, []byte, error) {
+	var r bool
+	if len(cast) == 1 {
+		r = cast[0]
+	}
+	// create TeeReader so we can retrieve raw XML
+	buf := make([]byte, 0)
+	wb := bytes.NewBuffer(buf)
+	trdr := myTeeReader(xmlReader, wb)
+
+	m, err := xmlSeqReaderToMap(trdr, r)
+
+	// retrieve the raw XML that was decoded
+	b := wb.Bytes()
+
+	// err may be NoRoot
+	return m, b, err
+}
+
+// xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
+func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
+	// parse the Reader
+	p := xml.NewDecoder(rdr)
+	if CustomDecoder != nil {
+		useCustomDecoder(p)
+	} else {
+		p.CharsetReader = XmlCharsetReader
+	}
+	return xmlSeqToMapParser("", nil, p, r)
+}
+
+// xmlSeqToMap - convert a XML doc into map[string]interface{} value
+func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) {
+	b := bytes.NewReader(doc)
+	p := xml.NewDecoder(b)
+	if CustomDecoder != nil {
+		useCustomDecoder(p)
+	} else {
+		p.CharsetReader = XmlCharsetReader
+	}
+	return xmlSeqToMapParser("", nil, p, r)
+}
+
+// ===================================== where the work happens =============================
+
+// xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly.
+// Add #seq tag value for each element decoded - to be used for Encoding later.
+func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
+	if snakeCaseKeys {
+		skey = strings.Replace(skey, "-", "_", -1)
+	}
+
+	// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
+	var n, na map[string]interface{}
+	var seq int // for including seq num when decoding
+
+	// Allocate maps and load attributes, if any.
+	// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
+	//       to get StartElement then recurse with skey==xml.StartElement.Name.Local
+	//       where we begin allocating map[string]interface{} values 'n' and 'na'.
+	if skey != "" {
+		// 'n' only needs one slot - save call to runtime•hashGrow()
+		// 'na' we don't know
+		n = make(map[string]interface{}, 1)
+		na = make(map[string]interface{})
+		if len(a) > 0 {
+			// xml.Attr is decoded into: map["#attr"]map[<attr_label>]interface{}
+			// where interface{} is map[string]interface{}{"#text":<attr_val>, "#seq":<attr_seq>}
+			aa := make(map[string]interface{}, len(a))
+			for i, v := range a {
+				if snakeCaseKeys {
+					v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
+				}
+				if xmlEscapeCharsDecoder { // per issue#84
+					v.Value = escapeChars(v.Value)
+				}
+				if len(v.Name.Space) > 0 {
+					aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
+				} else {
+					aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
+				}
+			}
+			na["#attr"] = aa
+		}
+	}
+
+	// Return XMPP <stream:stream> message.
+	if handleXMPPStreamTag && skey == "stream:stream" {
+		n[skey] = na
+		return n, nil
+	}
+
+	for {
+		t, err := p.RawToken()
+		if err != nil {
+			if err != io.EOF {
+				return nil, errors.New("xml.Decoder.Token() - " + err.Error())
+			}
+			return nil, err
+		}
+		switch t.(type) {
+		case xml.StartElement:
+			tt := t.(xml.StartElement)
+
+			// First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key.
+			// So when the loop is first entered, the first token is the root tag along
+			// with any attributes, which we process here.
+			//
+			// Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for
+			// processing before getting the next token which is the element value,
+			// which is done above.
+			if skey == "" {
+				if len(tt.Name.Space) > 0 {
+					return xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
+				} else {
+					return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
+				}
+			}
+
+			// If not initializing the map, parse the element.
+			// len(nn) == 1, necessarily - it is just an 'n'.
+			var nn map[string]interface{}
+			if len(tt.Name.Space) > 0 {
+				nn, err = xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
+			} else {
+				nn, err = xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
+			}
+			if err != nil {
+				return nil, err
+			}
+
+			// The nn map[string]interface{} value is a na[nn_key] value.
+			// We need to see if nn_key already exists - means we're parsing a list.
+			// This may require converting na[nn_key] value into []interface{} type.
+			// First, extract the key:val for the map - it's a singleton.
+			var key string
+			var val interface{}
+			for key, val = range nn {
+				break
+			}
+
+			// add "#seq" k:v pair -
+			// Sequence number included even in list elements - this should allow us
+			// to properly resequence even something goofy like:
+			//     <list>item 1</list>
+			//     <subelement>item 2</subelement>
+			//     <list>item 3</list>
+			// where all the "list" subelements are decoded into an array.
+			switch val.(type) {
+			case map[string]interface{}:
+				val.(map[string]interface{})["#seq"] = seq
+				seq++
+			case interface{}: // a non-nil simple element: string, float64, bool
+				v := map[string]interface{}{"#text": val, "#seq": seq}
+				seq++
+				val = v
+			}
+
+			// 'na' holding sub-elements of n.
+			// See if 'key' already exists.
+			// If 'key' exists, then this is a list, if not just add key:val to na.
+			if v, ok := na[key]; ok {
+				var a []interface{}
+				switch v.(type) {
+				case []interface{}:
+					a = v.([]interface{})
+				default: // anything else - note: v.(type) != nil
+					a = []interface{}{v}
+				}
+				a = append(a, val)
+				na[key] = a
+			} else {
+				na[key] = val // save it as a singleton
+			}
+		case xml.EndElement:
+			if skey != "" {
+				tt := t.(xml.EndElement)
+				if snakeCaseKeys {
+					tt.Name.Local = strings.Replace(tt.Name.Local, "-", "_", -1)
+				}
+				var name string
+				if len(tt.Name.Space) > 0 {
+					name = tt.Name.Space + `:` + tt.Name.Local
+				} else {
+					name = tt.Name.Local
+				}
+				if skey != name {
+					return nil, fmt.Errorf("element %s not properly terminated, got %s at #%d",
+						skey, name, p.InputOffset())
+				}
+			}
+			// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
+			if len(n) == 0 {
+				// If len(na)==0 we have an empty element == "";
+				// it has no xml.Attr nor xml.CharData.
+				// Empty element content will be  map["etag"]map["#text"]""
+				// after #seq injection - map["etag"]map["#seq"]seq - after return.
+				if len(na) > 0 {
+					n[skey] = na
+				} else {
+					n[skey] = "" // empty element
+				}
+			}
+			return n, nil
+		case xml.CharData:
+			// clean up possible noise
+			tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
+			if xmlEscapeCharsDecoder { // issue#84
+				tt = escapeChars(tt)
+			}
+			if skey == "" {
+				// per Adrian (http://www.adrianlungu.com/) catch stray text
+				// in decoder stream -
+				// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
+				// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
+				// a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
+				continue
+			}
+			if len(tt) > 0 {
+				// every simple element is a #text and has #seq associated with it
+				na["#text"] = cast(tt, r, "")
+				na["#seq"] = seq
+				seq++
+			}
+		case xml.Comment:
+			if n == nil { // no root 'key'
+				n = map[string]interface{}{"#comment": string(t.(xml.Comment))}
+				return n, NoRoot
+			}
+			cm := make(map[string]interface{}, 2)
+			cm["#text"] = string(t.(xml.Comment))
+			cm["#seq"] = seq
+			seq++
+			na["#comment"] = cm
+		case xml.Directive:
+			if n == nil { // no root 'key'
+				n = map[string]interface{}{"#directive": string(t.(xml.Directive))}
+				return n, NoRoot
+			}
+			dm := make(map[string]interface{}, 2)
+			dm["#text"] = string(t.(xml.Directive))
+			dm["#seq"] = seq
+			seq++
+			na["#directive"] = dm
+		case xml.ProcInst:
+			if n == nil {
+				na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)}
+				n = map[string]interface{}{"#procinst": na}
+				return n, NoRoot
+			}
+			pm := make(map[string]interface{}, 3)
+			pm["#target"] = t.(xml.ProcInst).Target
+			pm["#inst"] = string(t.(xml.ProcInst).Inst)
+			pm["#seq"] = seq
+			seq++
+			na["#procinst"] = pm
+		default:
+			// noop - shouldn't ever get here, now, since we handle all token types
+		}
+	}
+}
+
+// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
+
+// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
+
+// Xml encodes a MapSeq as XML with elements sorted on #seq.  The companion of NewMapXmlSeq().
+// The following rules apply.
+//    - The "#seq" key value is used to seqence the subelements or attributes only.
+//    - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
+//    - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
+//    - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
+//    - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst"
+//      map entries - <?target inst?>.
+//    - Value type encoding:
+//          > string, bool, float64, int, int32, int64, float32: per "%v" formating
+//          > []bool, []uint8: by casting to string
+//          > structures, etc.: handed to xml.Marshal() - if there is an error, the element
+//            value is "UNKNOWN"
+//    - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
+//    - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
+//      Thus, `{ "key":"value" }` encodes as "<key>value</key>".
+func (mv MapSeq) Xml(rootTag ...string) ([]byte, error) {
+	m := map[string]interface{}(mv)
+	var err error
+	s := new(string)
+	p := new(pretty) // just a stub
+
+	if len(m) == 1 && len(rootTag) == 0 {
+		for key, value := range m {
+			// if it's an array, see if all values are map[string]interface{}
+			// we force a new root tag if we'll end up with no key:value in the list
+			// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
+			switch value.(type) {
+			case []interface{}:
+				for _, v := range value.([]interface{}) {
+					switch v.(type) {
+					case map[string]interface{}: // noop
+					default: // anything else
+						err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
+						goto done
+					}
+				}
+			}
+			err = mapToXmlSeqIndent(false, s, key, value, p)
+		}
+	} else if len(rootTag) == 1 {
+		err = mapToXmlSeqIndent(false, s, rootTag[0], m, p)
+	} else {
+		err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
+	}
+done:
+	return []byte(*s), err
+}
+
+// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
+// The names will also provide a key for the number of return arguments.
+
+// XmlWriter Writes the MapSeq value as  XML on the Writer.
+// See MapSeq.Xml() for encoding rules.
+func (mv MapSeq) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
+	x, err := mv.Xml(rootTag...)
+	if err != nil {
+		return err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return err
+}
+
+// XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
+// See Map.XmlSeq() for encoding rules.
+/*
+func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
+	x, err := mv.Xml(rootTag...)
+	if err != nil {
+		return x, err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return x, err
+}
+*/
+
+// XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
+// See MapSeq.Xml() for encoding rules.
+func (mv MapSeq) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
+	x, err := mv.XmlIndent(prefix, indent, rootTag...)
+	if err != nil {
+		return err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return err
+}
+
+// XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
+// See Map.XmlSeq() for encoding rules.
+/*
+func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
+	x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
+	if err != nil {
+		return x, err
+	}
+
+	_, err = xmlWriter.Write(x)
+	return x, err
+}
+*/
+
+// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
+
+// ---------------------- XmlSeqIndent ----------------------------
+
+// XmlIndent encodes a map[string]interface{} as a pretty XML string.
+// See MapSeq.XmlSeq() for encoding rules.
+func (mv MapSeq) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
+	m := map[string]interface{}(mv)
+
+	var err error
+	s := new(string)
+	p := new(pretty)
+	p.indent = indent
+	p.padding = prefix
+
+	if len(m) == 1 && len(rootTag) == 0 {
+		// this can extract the key for the single map element
+		// use it if it isn't a key for a list
+		for key, value := range m {
+			if _, ok := value.([]interface{}); ok {
+				err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
+			} else {
+				err = mapToXmlSeqIndent(true, s, key, value, p)
+			}
+		}
+	} else if len(rootTag) == 1 {
+		err = mapToXmlSeqIndent(true, s, rootTag[0], m, p)
+	} else {
+		err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
+	}
+	return []byte(*s), err
+}
+
+// where the work actually happens
+// returns an error if an attribute is not atomic
+func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
+	var endTag bool
+	var isSimple bool
+	var noEndTag bool
+	var elen int
+	var ss string
+	p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
+
+	switch value.(type) {
+	case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
+		if doIndent {
+			*s += p.padding
+		}
+		if key != "#comment" && key != "#directive" && key != "#procinst" {
+			*s += `<` + key
+		}
+	}
+	switch value.(type) {
+	case map[string]interface{}:
+		val := value.(map[string]interface{})
+
+		if key == "#comment" {
+			*s += `<!--` + val["#text"].(string) + `-->`
+			noEndTag = true
+			break
+		}
+
+		if key == "#directive" {
+			*s += `<!` + val["#text"].(string) + `>`
+			noEndTag = true
+			break
+		}
+
+		if key == "#procinst" {
+			*s += `<?` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>`
+			noEndTag = true
+			break
+		}
+
+		haveAttrs := false
+		// process attributes first
+		if v, ok := val["#attr"].(map[string]interface{}); ok {
+			// First, unroll the map[string]interface{} into a []keyval array.
+			// Then sequence it.
+			kv := make([]keyval, len(v))
+			n := 0
+			for ak, av := range v {
+				kv[n] = keyval{ak, av}
+				n++
+			}
+			sort.Sort(elemListSeq(kv))
+			// Now encode the attributes in original decoding sequence, using keyval array.
+			for _, a := range kv {
+				vv := a.v.(map[string]interface{})
+				switch vv["#text"].(type) {
+				case string:
+					if xmlEscapeChars {
+						ss = escapeChars(vv["#text"].(string))
+					} else {
+						ss = vv["#text"].(string)
+					}
+					*s += ` ` + a.k + `="` + ss + `"`
+				case float64, bool, int, int32, int64, float32:
+					*s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"`
+				case []byte:
+					if xmlEscapeChars {
+						ss = escapeChars(string(vv["#text"].([]byte)))
+					} else {
+						ss = string(vv["#text"].([]byte))
+					}
+					*s += ` ` + a.k + `="` + ss + `"`
+				default:
+					return fmt.Errorf("invalid attribute value for: %s", a.k)
+				}
+			}
+			haveAttrs = true
+		}
+
+		// simple element?
+		// every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
+		_, seqOK := val["#seq"] // have key
+		if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
+			if stmp, ok := v.(string); ok && stmp != "" {
+				if xmlEscapeChars {
+					stmp = escapeChars(stmp)
+				}
+				*s += ">" + stmp
+				endTag = true
+				elen = 1
+			}
+			isSimple = true
+			break
+		} else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK {
+			// here no #text but have #seq or #seq+#attr
+			endTag = false
+			break
+		}
+
+		// we now need to sequence everything except attributes
+		// 'kv' will hold everything that needs to be written
+		kv := make([]keyval, 0)
+		for k, v := range val {
+			if k == "#attr" { // already processed
+				continue
+			}
+			if k == "#seq" { // ignore - just for sorting
+				continue
+			}
+			switch v.(type) {
+			case []interface{}:
+				// unwind the array as separate entries
+				for _, vv := range v.([]interface{}) {
+					kv = append(kv, keyval{k, vv})
+				}
+			default:
+				kv = append(kv, keyval{k, v})
+			}
+		}
+
+		// close tag with possible attributes
+		*s += ">"
+		if doIndent {
+			*s += "\n"
+		}
+		// something more complex
+		p.mapDepth++
+		sort.Sort(elemListSeq(kv))
+		i := 0
+		for _, v := range kv {
+			switch v.v.(type) {
+			case []interface{}:
+			default:
+				if i == 0 && doIndent {
+					p.Indent()
+				}
+			}
+			i++
+			if err := mapToXmlSeqIndent(doIndent, s, v.k, v.v, p); err != nil {
+				return err
+			}
+			switch v.v.(type) {
+			case []interface{}: // handled in []interface{} case
+			default:
+				if doIndent {
+					p.Outdent()
+				}
+			}
+			i--
+		}
+		p.mapDepth--
+		endTag = true
+		elen = 1 // we do have some content other than attrs
+	case []interface{}:
+		for _, v := range value.([]interface{}) {
+			if doIndent {
+				p.Indent()
+			}
+			if err := mapToXmlSeqIndent(doIndent, s, key, v, p); err != nil {
+				return err
+			}
+			if doIndent {
+				p.Outdent()
+			}
+		}
+		return nil
+	case nil:
+		// terminate the tag
+		if doIndent {
+			*s += p.padding
+		}
+		*s += "<" + key
+		endTag, isSimple = true, true
+		break
+	default: // handle anything - even goofy stuff
+		elen = 0
+		switch value.(type) {
+		case string:
+			if xmlEscapeChars {
+				ss = escapeChars(value.(string))
+			} else {
+				ss = value.(string)
+			}
+			elen = len(ss)
+			if elen > 0 {
+				*s += ">" + ss
+			}
+		case float64, bool, int, int32, int64, float32:
+			v := fmt.Sprintf("%v", value)
+			elen = len(v)
+			if elen > 0 {
+				*s += ">" + v
+			}
+		case []byte: // NOTE: byte is just an alias for uint8
+			// similar to how xml.Marshal handles []byte structure members
+			if xmlEscapeChars {
+				ss = escapeChars(string(value.([]byte)))
+			} else {
+				ss = string(value.([]byte))
+			}
+			elen = len(ss)
+			if elen > 0 {
+				*s += ">" + ss
+			}
+		default:
+			var v []byte
+			var err error
+			if doIndent {
+				v, err = xml.MarshalIndent(value, p.padding, p.indent)
+			} else {
+				v, err = xml.Marshal(value)
+			}
+			if err != nil {
+				*s += ">UNKNOWN"
+			} else {
+				elen = len(v)
+				if elen > 0 {
+					*s += string(v)
+				}
+			}
+		}
+		isSimple = true
+		endTag = true
+	}
+	if endTag && !noEndTag {
+		if doIndent {
+			if !isSimple {
+				*s += p.padding
+			}
+		}
+		switch value.(type) {
+		case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
+			if elen > 0 || useGoXmlEmptyElemSyntax {
+				if elen == 0 {
+					*s += ">"
+				}
+				*s += `</` + key + ">"
+			} else {
+				*s += `/>`
+			}
+		}
+	} else if !noEndTag {
+		if useGoXmlEmptyElemSyntax {
+			*s += `</` + key + ">"
+			// *s += "></" + key + ">"
+		} else {
+			*s += "/>"
+		}
+	}
+	if doIndent {
+		if p.cnt > p.start {
+			*s += "\n"
+		}
+		p.Outdent()
+	}
+
+	return nil
+}
+
+// the element sort implementation
+
+type keyval struct {
+	k string
+	v interface{}
+}
+type elemListSeq []keyval
+
+func (e elemListSeq) Len() int {
+	return len(e)
+}
+
+func (e elemListSeq) Swap(i, j int) {
+	e[i], e[j] = e[j], e[i]
+}
+
+func (e elemListSeq) Less(i, j int) bool {
+	var iseq, jseq int
+	var fiseq, fjseq float64
+	var ok bool
+	if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok {
+		if fiseq, ok = e[i].v.(map[string]interface{})["#seq"].(float64); ok {
+			iseq = int(fiseq)
+		} else {
+			iseq = 9999999
+		}
+	}
+
+	if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok {
+		if fjseq, ok = e[j].v.(map[string]interface{})["#seq"].(float64); ok {
+			jseq = int(fjseq)
+		} else {
+			jseq = 9999999
+		}
+	}
+
+	return iseq <= jseq
+}
+
+// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
+
+// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
+// It preserves comments, directives and process instructions,
+func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
+	x, err := NewMapXmlSeq(b)
+	if err != nil {
+		return nil, err
+	}
+	return x.XmlIndent(prefix, indent)
+}

+ 18 - 0
vendor/github.com/clbanning/mxj/xmlseq2.go

@@ -0,0 +1,18 @@
+// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+// ---------------- expose Map methods to MapSeq type ---------------------------
+
+// Pretty print a Map.
+func (msv MapSeq) StringIndent(offset ...int) string {
+	return writeMap(map[string]interface{}(msv), true, true, offset...)
+}
+
+// Pretty print a Map without the value type information - just key:value entries.
+func (msv MapSeq) StringIndentNoTypeInfo(offset ...int) string {
+	return writeMap(map[string]interface{}(msv), false, true, offset...)
+}
+

+ 202 - 0
vendor/github.com/coreos/go-semver/LICENSE

@@ -0,0 +1,202 @@
+
+                                 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.

+ 268 - 0
vendor/github.com/coreos/go-semver/semver/semver.go

@@ -0,0 +1,268 @@
+// Copyright 2013-2015 CoreOS, Inc.
+//
+// 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.
+
+// Semantic Versions http://semver.org
+package semver
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+type Version struct {
+	Major      int64
+	Minor      int64
+	Patch      int64
+	PreRelease PreRelease
+	Metadata   string
+}
+
+type PreRelease string
+
+func splitOff(input *string, delim string) (val string) {
+	parts := strings.SplitN(*input, delim, 2)
+
+	if len(parts) == 2 {
+		*input = parts[0]
+		val = parts[1]
+	}
+
+	return val
+}
+
+func New(version string) *Version {
+	return Must(NewVersion(version))
+}
+
+func NewVersion(version string) (*Version, error) {
+	v := Version{}
+
+	if err := v.Set(version); err != nil {
+		return nil, err
+	}
+
+	return &v, nil
+}
+
+// Must is a helper for wrapping NewVersion and will panic if err is not nil.
+func Must(v *Version, err error) *Version {
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Set parses and updates v from the given version string. Implements flag.Value
+func (v *Version) Set(version string) error {
+	metadata := splitOff(&version, "+")
+	preRelease := PreRelease(splitOff(&version, "-"))
+	dotParts := strings.SplitN(version, ".", 3)
+
+	if len(dotParts) != 3 {
+		return fmt.Errorf("%s is not in dotted-tri format", version)
+	}
+
+	parsed := make([]int64, 3, 3)
+
+	for i, v := range dotParts[:3] {
+		val, err := strconv.ParseInt(v, 10, 64)
+		parsed[i] = val
+		if err != nil {
+			return err
+		}
+	}
+
+	v.Metadata = metadata
+	v.PreRelease = preRelease
+	v.Major = parsed[0]
+	v.Minor = parsed[1]
+	v.Patch = parsed[2]
+	return nil
+}
+
+func (v Version) String() string {
+	var buffer bytes.Buffer
+
+	fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch)
+
+	if v.PreRelease != "" {
+		fmt.Fprintf(&buffer, "-%s", v.PreRelease)
+	}
+
+	if v.Metadata != "" {
+		fmt.Fprintf(&buffer, "+%s", v.Metadata)
+	}
+
+	return buffer.String()
+}
+
+func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	var data string
+	if err := unmarshal(&data); err != nil {
+		return err
+	}
+	return v.Set(data)
+}
+
+func (v Version) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + v.String() + `"`), nil
+}
+
+func (v *Version) UnmarshalJSON(data []byte) error {
+	l := len(data)
+	if l == 0 || string(data) == `""` {
+		return nil
+	}
+	if l < 2 || data[0] != '"' || data[l-1] != '"' {
+		return errors.New("invalid semver string")
+	}
+	return v.Set(string(data[1 : l-1]))
+}
+
+// Compare tests if v is less than, equal to, or greater than versionB,
+// returning -1, 0, or +1 respectively.
+func (v Version) Compare(versionB Version) int {
+	if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
+		return cmp
+	}
+	return preReleaseCompare(v, versionB)
+}
+
+// Equal tests if v is equal to versionB.
+func (v Version) Equal(versionB Version) bool {
+	return v.Compare(versionB) == 0
+}
+
+// LessThan tests if v is less than versionB.
+func (v Version) LessThan(versionB Version) bool {
+	return v.Compare(versionB) < 0
+}
+
+// Slice converts the comparable parts of the semver into a slice of integers.
+func (v Version) Slice() []int64 {
+	return []int64{v.Major, v.Minor, v.Patch}
+}
+
+func (p PreRelease) Slice() []string {
+	preRelease := string(p)
+	return strings.Split(preRelease, ".")
+}
+
+func preReleaseCompare(versionA Version, versionB Version) int {
+	a := versionA.PreRelease
+	b := versionB.PreRelease
+
+	/* Handle the case where if two versions are otherwise equal it is the
+	 * one without a PreRelease that is greater */
+	if len(a) == 0 && (len(b) > 0) {
+		return 1
+	} else if len(b) == 0 && (len(a) > 0) {
+		return -1
+	}
+
+	// If there is a prerelease, check and compare each part.
+	return recursivePreReleaseCompare(a.Slice(), b.Slice())
+}
+
+func recursiveCompare(versionA []int64, versionB []int64) int {
+	if len(versionA) == 0 {
+		return 0
+	}
+
+	a := versionA[0]
+	b := versionB[0]
+
+	if a > b {
+		return 1
+	} else if a < b {
+		return -1
+	}
+
+	return recursiveCompare(versionA[1:], versionB[1:])
+}
+
+func recursivePreReleaseCompare(versionA []string, versionB []string) int {
+	// A larger set of pre-release fields has a higher precedence than a smaller set,
+	// if all of the preceding identifiers are equal.
+	if len(versionA) == 0 {
+		if len(versionB) > 0 {
+			return -1
+		}
+		return 0
+	} else if len(versionB) == 0 {
+		// We're longer than versionB so return 1.
+		return 1
+	}
+
+	a := versionA[0]
+	b := versionB[0]
+
+	aInt := false
+	bInt := false
+
+	aI, err := strconv.Atoi(versionA[0])
+	if err == nil {
+		aInt = true
+	}
+
+	bI, err := strconv.Atoi(versionB[0])
+	if err == nil {
+		bInt = true
+	}
+
+	// Handle Integer Comparison
+	if aInt && bInt {
+		if aI > bI {
+			return 1
+		} else if aI < bI {
+			return -1
+		}
+	}
+
+	// Handle String Comparison
+	if a > b {
+		return 1
+	} else if a < b {
+		return -1
+	}
+
+	return recursivePreReleaseCompare(versionA[1:], versionB[1:])
+}
+
+// BumpMajor increments the Major field by 1 and resets all other fields to their default values
+func (v *Version) BumpMajor() {
+	v.Major += 1
+	v.Minor = 0
+	v.Patch = 0
+	v.PreRelease = PreRelease("")
+	v.Metadata = ""
+}
+
+// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
+func (v *Version) BumpMinor() {
+	v.Minor += 1
+	v.Patch = 0
+	v.PreRelease = PreRelease("")
+	v.Metadata = ""
+}
+
+// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
+func (v *Version) BumpPatch() {
+	v.Patch += 1
+	v.PreRelease = PreRelease("")
+	v.Metadata = ""
+}

+ 38 - 0
vendor/github.com/coreos/go-semver/semver/sort.go

@@ -0,0 +1,38 @@
+// Copyright 2013-2015 CoreOS, Inc.
+//
+// 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 semver
+
+import (
+	"sort"
+)
+
+type Versions []*Version
+
+func (s Versions) Len() int {
+	return len(s)
+}
+
+func (s Versions) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+func (s Versions) Less(i, j int) bool {
+	return s[i].LessThan(*s[j])
+}
+
+// Sort sorts the given slice of Version
+func Sort(versions []*Version) {
+	sort.Sort(Versions(versions))
+}

+ 191 - 0
vendor/github.com/coreos/go-systemd/LICENSE

@@ -0,0 +1,191 @@
+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:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+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
+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.

+ 5 - 0
vendor/github.com/coreos/go-systemd/NOTICE

@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2018 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).

+ 182 - 0
vendor/github.com/coreos/go-systemd/journal/journal.go

@@ -0,0 +1,182 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 journal provides write bindings to the local systemd journal.
+// It is implemented in pure Go and connects to the journal directly over its
+// unix socket.
+//
+// To read from the journal, see the "sdjournal" package, which wraps the
+// sd-journal a C API.
+//
+// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html
+package journal
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net"
+	"os"
+	"strconv"
+	"strings"
+	"syscall"
+)
+
+// Priority of a journal message
+type Priority int
+
+const (
+	PriEmerg Priority = iota
+	PriAlert
+	PriCrit
+	PriErr
+	PriWarning
+	PriNotice
+	PriInfo
+	PriDebug
+)
+
+var conn net.Conn
+
+func init() {
+	var err error
+	conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
+	if err != nil {
+		conn = nil
+	}
+}
+
+// Enabled returns true if the local systemd journal is available for logging
+func Enabled() bool {
+	return conn != nil
+}
+
+// Send a message to the local systemd journal. vars is a map of journald
+// fields to values.  Fields must be composed of uppercase letters, numbers,
+// and underscores, but must not start with an underscore. Within these
+// restrictions, any arbitrary field name may be used.  Some names have special
+// significance: see the journalctl documentation
+// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
+// for more details.  vars may be nil.
+func Send(message string, priority Priority, vars map[string]string) error {
+	if conn == nil {
+		return journalError("could not connect to journald socket")
+	}
+
+	data := new(bytes.Buffer)
+	appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
+	appendVariable(data, "MESSAGE", message)
+	for k, v := range vars {
+		appendVariable(data, k, v)
+	}
+
+	_, err := io.Copy(conn, data)
+	if err != nil && isSocketSpaceError(err) {
+		file, err := tempFd()
+		if err != nil {
+			return journalError(err.Error())
+		}
+		defer file.Close()
+		_, err = io.Copy(file, data)
+		if err != nil {
+			return journalError(err.Error())
+		}
+
+		rights := syscall.UnixRights(int(file.Fd()))
+
+		/* this connection should always be a UnixConn, but better safe than sorry */
+		unixConn, ok := conn.(*net.UnixConn)
+		if !ok {
+			return journalError("can't send file through non-Unix connection")
+		}
+		_, _, err = unixConn.WriteMsgUnix([]byte{}, rights, nil)
+		if err != nil {
+			return journalError(err.Error())
+		}
+	} else if err != nil {
+		return journalError(err.Error())
+	}
+	return nil
+}
+
+// Print prints a message to the local systemd journal using Send().
+func Print(priority Priority, format string, a ...interface{}) error {
+	return Send(fmt.Sprintf(format, a...), priority, nil)
+}
+
+func appendVariable(w io.Writer, name, value string) {
+	if !validVarName(name) {
+		journalError("variable name contains invalid character, ignoring")
+	}
+	if strings.ContainsRune(value, '\n') {
+		/* When the value contains a newline, we write:
+		 * - the variable name, followed by a newline
+		 * - the size (in 64bit little endian format)
+		 * - the data, followed by a newline
+		 */
+		fmt.Fprintln(w, name)
+		binary.Write(w, binary.LittleEndian, uint64(len(value)))
+		fmt.Fprintln(w, value)
+	} else {
+		/* just write the variable and value all on one line */
+		fmt.Fprintf(w, "%s=%s\n", name, value)
+	}
+}
+
+func validVarName(name string) bool {
+	/* The variable name must be in uppercase and consist only of characters,
+	 * numbers and underscores, and may not begin with an underscore. (from the docs)
+	 */
+
+	valid := name[0] != '_'
+	for _, c := range name {
+		valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
+	}
+	return valid
+}
+
+func isSocketSpaceError(err error) bool {
+	opErr, ok := err.(*net.OpError)
+	if !ok {
+		return false
+	}
+
+	sysErr, ok := opErr.Err.(syscall.Errno)
+	if !ok {
+		return false
+	}
+
+	return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
+}
+
+func tempFd() (*os.File, error) {
+	file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
+	if err != nil {
+		return nil, err
+	}
+	err = syscall.Unlink(file.Name())
+	if err != nil {
+		return nil, err
+	}
+	return file, nil
+}
+
+func journalError(s string) error {
+	s = "journal error: " + s
+	fmt.Fprintln(os.Stderr, s)
+	return errors.New(s)
+}

+ 202 - 0
vendor/github.com/coreos/pkg/LICENSE

@@ -0,0 +1,202 @@
+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.
+

+ 5 - 0
vendor/github.com/coreos/pkg/NOTICE

@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2014 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).

+ 157 - 0
vendor/github.com/coreos/pkg/capnslog/formatters.go

@@ -0,0 +1,157 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"log"
+	"runtime"
+	"strings"
+	"time"
+)
+
+type Formatter interface {
+	Format(pkg string, level LogLevel, depth int, entries ...interface{})
+	Flush()
+}
+
+func NewStringFormatter(w io.Writer) Formatter {
+	return &StringFormatter{
+		w: bufio.NewWriter(w),
+	}
+}
+
+type StringFormatter struct {
+	w *bufio.Writer
+}
+
+func (s *StringFormatter) Format(pkg string, l LogLevel, i int, entries ...interface{}) {
+	now := time.Now().UTC()
+	s.w.WriteString(now.Format(time.RFC3339))
+	s.w.WriteByte(' ')
+	writeEntries(s.w, pkg, l, i, entries...)
+	s.Flush()
+}
+
+func writeEntries(w *bufio.Writer, pkg string, _ LogLevel, _ int, entries ...interface{}) {
+	if pkg != "" {
+		w.WriteString(pkg + ": ")
+	}
+	str := fmt.Sprint(entries...)
+	endsInNL := strings.HasSuffix(str, "\n")
+	w.WriteString(str)
+	if !endsInNL {
+		w.WriteString("\n")
+	}
+}
+
+func (s *StringFormatter) Flush() {
+	s.w.Flush()
+}
+
+func NewPrettyFormatter(w io.Writer, debug bool) Formatter {
+	return &PrettyFormatter{
+		w:     bufio.NewWriter(w),
+		debug: debug,
+	}
+}
+
+type PrettyFormatter struct {
+	w     *bufio.Writer
+	debug bool
+}
+
+func (c *PrettyFormatter) Format(pkg string, l LogLevel, depth int, entries ...interface{}) {
+	now := time.Now()
+	ts := now.Format("2006-01-02 15:04:05")
+	c.w.WriteString(ts)
+	ms := now.Nanosecond() / 1000
+	c.w.WriteString(fmt.Sprintf(".%06d", ms))
+	if c.debug {
+		_, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
+		if !ok {
+			file = "???"
+			line = 1
+		} else {
+			slash := strings.LastIndex(file, "/")
+			if slash >= 0 {
+				file = file[slash+1:]
+			}
+		}
+		if line < 0 {
+			line = 0 // not a real line number
+		}
+		c.w.WriteString(fmt.Sprintf(" [%s:%d]", file, line))
+	}
+	c.w.WriteString(fmt.Sprint(" ", l.Char(), " | "))
+	writeEntries(c.w, pkg, l, depth, entries...)
+	c.Flush()
+}
+
+func (c *PrettyFormatter) Flush() {
+	c.w.Flush()
+}
+
+// LogFormatter emulates the form of the traditional built-in logger.
+type LogFormatter struct {
+	logger *log.Logger
+	prefix string
+}
+
+// NewLogFormatter is a helper to produce a new LogFormatter struct. It uses the
+// golang log package to actually do the logging work so that logs look similar.
+func NewLogFormatter(w io.Writer, prefix string, flag int) Formatter {
+	return &LogFormatter{
+		logger: log.New(w, "", flag), // don't use prefix here
+		prefix: prefix,               // save it instead
+	}
+}
+
+// Format builds a log message for the LogFormatter. The LogLevel is ignored.
+func (lf *LogFormatter) Format(pkg string, _ LogLevel, _ int, entries ...interface{}) {
+	str := fmt.Sprint(entries...)
+	prefix := lf.prefix
+	if pkg != "" {
+		prefix = fmt.Sprintf("%s%s: ", prefix, pkg)
+	}
+	lf.logger.Output(5, fmt.Sprintf("%s%v", prefix, str)) // call depth is 5
+}
+
+// Flush is included so that the interface is complete, but is a no-op.
+func (lf *LogFormatter) Flush() {
+	// noop
+}
+
+// NilFormatter is a no-op log formatter that does nothing.
+type NilFormatter struct {
+}
+
+// NewNilFormatter is a helper to produce a new LogFormatter struct. It logs no
+// messages so that you can cause part of your logging to be silent.
+func NewNilFormatter() Formatter {
+	return &NilFormatter{}
+}
+
+// Format does nothing.
+func (_ *NilFormatter) Format(_ string, _ LogLevel, _ int, _ ...interface{}) {
+	// noop
+}
+
+// Flush is included so that the interface is complete, but is a no-op.
+func (_ *NilFormatter) Flush() {
+	// noop
+}

+ 96 - 0
vendor/github.com/coreos/pkg/capnslog/glog_formatter.go

@@ -0,0 +1,96 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import (
+	"bufio"
+	"bytes"
+	"io"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var pid = os.Getpid()
+
+type GlogFormatter struct {
+	StringFormatter
+}
+
+func NewGlogFormatter(w io.Writer) *GlogFormatter {
+	g := &GlogFormatter{}
+	g.w = bufio.NewWriter(w)
+	return g
+}
+
+func (g GlogFormatter) Format(pkg string, level LogLevel, depth int, entries ...interface{}) {
+	g.w.Write(GlogHeader(level, depth+1))
+	g.StringFormatter.Format(pkg, level, depth+1, entries...)
+}
+
+func GlogHeader(level LogLevel, depth int) []byte {
+	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+	now := time.Now().UTC()
+	_, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
+	if !ok {
+		file = "???"
+		line = 1
+	} else {
+		slash := strings.LastIndex(file, "/")
+		if slash >= 0 {
+			file = file[slash+1:]
+		}
+	}
+	if line < 0 {
+		line = 0 // not a real line number
+	}
+	buf := &bytes.Buffer{}
+	buf.Grow(30)
+	_, month, day := now.Date()
+	hour, minute, second := now.Clock()
+	buf.WriteString(level.Char())
+	twoDigits(buf, int(month))
+	twoDigits(buf, day)
+	buf.WriteByte(' ')
+	twoDigits(buf, hour)
+	buf.WriteByte(':')
+	twoDigits(buf, minute)
+	buf.WriteByte(':')
+	twoDigits(buf, second)
+	buf.WriteByte('.')
+	buf.WriteString(strconv.Itoa(now.Nanosecond() / 1000))
+	buf.WriteByte('Z')
+	buf.WriteByte(' ')
+	buf.WriteString(strconv.Itoa(pid))
+	buf.WriteByte(' ')
+	buf.WriteString(file)
+	buf.WriteByte(':')
+	buf.WriteString(strconv.Itoa(line))
+	buf.WriteByte(']')
+	buf.WriteByte(' ')
+	return buf.Bytes()
+}
+
+const digits = "0123456789"
+
+func twoDigits(b *bytes.Buffer, d int) {
+	c2 := digits[d%10]
+	d /= 10
+	c1 := digits[d%10]
+	b.WriteByte(c1)
+	b.WriteByte(c2)
+}

+ 49 - 0
vendor/github.com/coreos/pkg/capnslog/init.go

@@ -0,0 +1,49 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+	"io"
+	"os"
+	"syscall"
+)
+
+// Here's where the opinionation comes in. We need some sensible defaults,
+// especially after taking over the log package. Your project (whatever it may
+// be) may see things differently. That's okay; there should be no defaults in
+// the main package that cannot be controlled or overridden programatically,
+// otherwise it's a bug. Doing so is creating your own init_log.go file much
+// like this one.
+
+func init() {
+	initHijack()
+
+	// Go `log` pacakge uses os.Stderr.
+	SetFormatter(NewDefaultFormatter(os.Stderr))
+	SetGlobalLogLevel(INFO)
+}
+
+func NewDefaultFormatter(out io.Writer) Formatter {
+	if syscall.Getppid() == 1 {
+		// We're running under init, which may be systemd.
+		f, err := NewJournaldFormatter()
+		if err == nil {
+			return f
+		}
+	}
+	return NewPrettyFormatter(out, false)
+}

+ 25 - 0
vendor/github.com/coreos/pkg/capnslog/init_windows.go

@@ -0,0 +1,25 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import "os"
+
+func init() {
+	initHijack()
+
+	// Go `log` package uses os.Stderr.
+	SetFormatter(NewPrettyFormatter(os.Stderr, false))
+	SetGlobalLogLevel(INFO)
+}

+ 68 - 0
vendor/github.com/coreos/pkg/capnslog/journald_formatter.go

@@ -0,0 +1,68 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/coreos/go-systemd/journal"
+)
+
+func NewJournaldFormatter() (Formatter, error) {
+	if !journal.Enabled() {
+		return nil, errors.New("No systemd detected")
+	}
+	return &journaldFormatter{}, nil
+}
+
+type journaldFormatter struct{}
+
+func (j *journaldFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
+	var pri journal.Priority
+	switch l {
+	case CRITICAL:
+		pri = journal.PriCrit
+	case ERROR:
+		pri = journal.PriErr
+	case WARNING:
+		pri = journal.PriWarning
+	case NOTICE:
+		pri = journal.PriNotice
+	case INFO:
+		pri = journal.PriInfo
+	case DEBUG:
+		pri = journal.PriDebug
+	case TRACE:
+		pri = journal.PriDebug
+	default:
+		panic("Unhandled loglevel")
+	}
+	msg := fmt.Sprint(entries...)
+	tags := map[string]string{
+		"PACKAGE":           pkg,
+		"SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
+	}
+	err := journal.Send(msg, pri, tags)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+	}
+}
+
+func (j *journaldFormatter) Flush() {}

+ 39 - 0
vendor/github.com/coreos/pkg/capnslog/log_hijack.go

@@ -0,0 +1,39 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import (
+	"log"
+)
+
+func initHijack() {
+	pkg := NewPackageLogger("log", "")
+	w := packageWriter{pkg}
+	log.SetFlags(0)
+	log.SetPrefix("")
+	log.SetOutput(w)
+}
+
+type packageWriter struct {
+	pl *PackageLogger
+}
+
+func (p packageWriter) Write(b []byte) (int, error) {
+	if p.pl.level < INFO {
+		return 0, nil
+	}
+	p.pl.internalLog(calldepth+2, INFO, string(b))
+	return len(b), nil
+}

+ 240 - 0
vendor/github.com/coreos/pkg/capnslog/logmap.go

@@ -0,0 +1,240 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import (
+	"errors"
+	"strings"
+	"sync"
+)
+
+// LogLevel is the set of all log levels.
+type LogLevel int8
+
+const (
+	// CRITICAL is the lowest log level; only errors which will end the program will be propagated.
+	CRITICAL LogLevel = iota - 1
+	// ERROR is for errors that are not fatal but lead to troubling behavior.
+	ERROR
+	// WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
+	WARNING
+	// NOTICE is for normal but significant conditions.
+	NOTICE
+	// INFO is a log level for common, everyday log updates.
+	INFO
+	// DEBUG is the default hidden level for more verbose updates about internal processes.
+	DEBUG
+	// TRACE is for (potentially) call by call tracing of programs.
+	TRACE
+)
+
+// Char returns a single-character representation of the log level.
+func (l LogLevel) Char() string {
+	switch l {
+	case CRITICAL:
+		return "C"
+	case ERROR:
+		return "E"
+	case WARNING:
+		return "W"
+	case NOTICE:
+		return "N"
+	case INFO:
+		return "I"
+	case DEBUG:
+		return "D"
+	case TRACE:
+		return "T"
+	default:
+		panic("Unhandled loglevel")
+	}
+}
+
+// String returns a multi-character representation of the log level.
+func (l LogLevel) String() string {
+	switch l {
+	case CRITICAL:
+		return "CRITICAL"
+	case ERROR:
+		return "ERROR"
+	case WARNING:
+		return "WARNING"
+	case NOTICE:
+		return "NOTICE"
+	case INFO:
+		return "INFO"
+	case DEBUG:
+		return "DEBUG"
+	case TRACE:
+		return "TRACE"
+	default:
+		panic("Unhandled loglevel")
+	}
+}
+
+// Update using the given string value. Fulfills the flag.Value interface.
+func (l *LogLevel) Set(s string) error {
+	value, err := ParseLevel(s)
+	if err != nil {
+		return err
+	}
+
+	*l = value
+	return nil
+}
+
+// ParseLevel translates some potential loglevel strings into their corresponding levels.
+func ParseLevel(s string) (LogLevel, error) {
+	switch s {
+	case "CRITICAL", "C":
+		return CRITICAL, nil
+	case "ERROR", "0", "E":
+		return ERROR, nil
+	case "WARNING", "1", "W":
+		return WARNING, nil
+	case "NOTICE", "2", "N":
+		return NOTICE, nil
+	case "INFO", "3", "I":
+		return INFO, nil
+	case "DEBUG", "4", "D":
+		return DEBUG, nil
+	case "TRACE", "5", "T":
+		return TRACE, nil
+	}
+	return CRITICAL, errors.New("couldn't parse log level " + s)
+}
+
+type RepoLogger map[string]*PackageLogger
+
+type loggerStruct struct {
+	sync.Mutex
+	repoMap   map[string]RepoLogger
+	formatter Formatter
+}
+
+// logger is the global logger
+var logger = new(loggerStruct)
+
+// SetGlobalLogLevel sets the log level for all packages in all repositories
+// registered with capnslog.
+func SetGlobalLogLevel(l LogLevel) {
+	logger.Lock()
+	defer logger.Unlock()
+	for _, r := range logger.repoMap {
+		r.setRepoLogLevelInternal(l)
+	}
+}
+
+// GetRepoLogger may return the handle to the repository's set of packages' loggers.
+func GetRepoLogger(repo string) (RepoLogger, error) {
+	logger.Lock()
+	defer logger.Unlock()
+	r, ok := logger.repoMap[repo]
+	if !ok {
+		return nil, errors.New("no packages registered for repo " + repo)
+	}
+	return r, nil
+}
+
+// MustRepoLogger returns the handle to the repository's packages' loggers.
+func MustRepoLogger(repo string) RepoLogger {
+	r, err := GetRepoLogger(repo)
+	if err != nil {
+		panic(err)
+	}
+	return r
+}
+
+// SetRepoLogLevel sets the log level for all packages in the repository.
+func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
+	logger.Lock()
+	defer logger.Unlock()
+	r.setRepoLogLevelInternal(l)
+}
+
+func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
+	for _, v := range r {
+		v.level = l
+	}
+}
+
+// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
+// order, and returns a map of the results, for use in SetLogLevel.
+func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
+	setlist := strings.Split(conf, ",")
+	out := make(map[string]LogLevel)
+	for _, setstring := range setlist {
+		setting := strings.Split(setstring, "=")
+		if len(setting) != 2 {
+			return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
+		}
+		l, err := ParseLevel(setting[1])
+		if err != nil {
+			return nil, err
+		}
+		out[setting[0]] = l
+	}
+	return out, nil
+}
+
+// SetLogLevel takes a map of package names within a repository to their desired
+// loglevel, and sets the levels appropriately. Unknown packages are ignored.
+// "*" is a special package name that corresponds to all packages, and will be
+// processed first.
+func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
+	logger.Lock()
+	defer logger.Unlock()
+	if l, ok := m["*"]; ok {
+		r.setRepoLogLevelInternal(l)
+	}
+	for k, v := range m {
+		l, ok := r[k]
+		if !ok {
+			continue
+		}
+		l.level = v
+	}
+}
+
+// SetFormatter sets the formatting function for all logs.
+func SetFormatter(f Formatter) {
+	logger.Lock()
+	defer logger.Unlock()
+	logger.formatter = f
+}
+
+// NewPackageLogger creates a package logger object.
+// This should be defined as a global var in your package, referencing your repo.
+func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
+	logger.Lock()
+	defer logger.Unlock()
+	if logger.repoMap == nil {
+		logger.repoMap = make(map[string]RepoLogger)
+	}
+	r, rok := logger.repoMap[repo]
+	if !rok {
+		logger.repoMap[repo] = make(RepoLogger)
+		r = logger.repoMap[repo]
+	}
+	p, pok := r[pkg]
+	if !pok {
+		r[pkg] = &PackageLogger{
+			pkg:   pkg,
+			level: INFO,
+		}
+		p = r[pkg]
+	}
+	return
+}

+ 177 - 0
vendor/github.com/coreos/pkg/capnslog/pkg_logger.go

@@ -0,0 +1,177 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 capnslog
+
+import (
+	"fmt"
+	"os"
+)
+
+type PackageLogger struct {
+	pkg   string
+	level LogLevel
+}
+
+const calldepth = 2
+
+func (p *PackageLogger) internalLog(depth int, inLevel LogLevel, entries ...interface{}) {
+	logger.Lock()
+	defer logger.Unlock()
+	if inLevel != CRITICAL && p.level < inLevel {
+		return
+	}
+	if logger.formatter != nil {
+		logger.formatter.Format(p.pkg, inLevel, depth+1, entries...)
+	}
+}
+
+func (p *PackageLogger) LevelAt(l LogLevel) bool {
+	logger.Lock()
+	defer logger.Unlock()
+	return p.level >= l
+}
+
+// Log a formatted string at any level between ERROR and TRACE
+func (p *PackageLogger) Logf(l LogLevel, format string, args ...interface{}) {
+	p.internalLog(calldepth, l, fmt.Sprintf(format, args...))
+}
+
+// Log a message at any level between ERROR and TRACE
+func (p *PackageLogger) Log(l LogLevel, args ...interface{}) {
+	p.internalLog(calldepth, l, fmt.Sprint(args...))
+}
+
+// log stdlib compatibility
+
+func (p *PackageLogger) Println(args ...interface{}) {
+	p.internalLog(calldepth, INFO, fmt.Sprintln(args...))
+}
+
+func (p *PackageLogger) Printf(format string, args ...interface{}) {
+	p.Logf(INFO, format, args...)
+}
+
+func (p *PackageLogger) Print(args ...interface{}) {
+	p.internalLog(calldepth, INFO, fmt.Sprint(args...))
+}
+
+// Panic and fatal
+
+func (p *PackageLogger) Panicf(format string, args ...interface{}) {
+	s := fmt.Sprintf(format, args...)
+	p.internalLog(calldepth, CRITICAL, s)
+	panic(s)
+}
+
+func (p *PackageLogger) Panic(args ...interface{}) {
+	s := fmt.Sprint(args...)
+	p.internalLog(calldepth, CRITICAL, s)
+	panic(s)
+}
+
+func (p *PackageLogger) Fatalf(format string, args ...interface{}) {
+	p.Logf(CRITICAL, format, args...)
+	os.Exit(1)
+}
+
+func (p *PackageLogger) Fatal(args ...interface{}) {
+	s := fmt.Sprint(args...)
+	p.internalLog(calldepth, CRITICAL, s)
+	os.Exit(1)
+}
+
+func (p *PackageLogger) Fatalln(args ...interface{}) {
+	s := fmt.Sprintln(args...)
+	p.internalLog(calldepth, CRITICAL, s)
+	os.Exit(1)
+}
+
+// Error Functions
+
+func (p *PackageLogger) Errorf(format string, args ...interface{}) {
+	p.Logf(ERROR, format, args...)
+}
+
+func (p *PackageLogger) Error(entries ...interface{}) {
+	p.internalLog(calldepth, ERROR, entries...)
+}
+
+// Warning Functions
+
+func (p *PackageLogger) Warningf(format string, args ...interface{}) {
+	p.Logf(WARNING, format, args...)
+}
+
+func (p *PackageLogger) Warning(entries ...interface{}) {
+	p.internalLog(calldepth, WARNING, entries...)
+}
+
+// Notice Functions
+
+func (p *PackageLogger) Noticef(format string, args ...interface{}) {
+	p.Logf(NOTICE, format, args...)
+}
+
+func (p *PackageLogger) Notice(entries ...interface{}) {
+	p.internalLog(calldepth, NOTICE, entries...)
+}
+
+// Info Functions
+
+func (p *PackageLogger) Infof(format string, args ...interface{}) {
+	p.Logf(INFO, format, args...)
+}
+
+func (p *PackageLogger) Info(entries ...interface{}) {
+	p.internalLog(calldepth, INFO, entries...)
+}
+
+// Debug Functions
+
+func (p *PackageLogger) Debugf(format string, args ...interface{}) {
+	if p.level < DEBUG {
+		return
+	}
+	p.Logf(DEBUG, format, args...)
+}
+
+func (p *PackageLogger) Debug(entries ...interface{}) {
+	if p.level < DEBUG {
+		return
+	}
+	p.internalLog(calldepth, DEBUG, entries...)
+}
+
+// Trace Functions
+
+func (p *PackageLogger) Tracef(format string, args ...interface{}) {
+	if p.level < TRACE {
+		return
+	}
+	p.Logf(TRACE, format, args...)
+}
+
+func (p *PackageLogger) Trace(entries ...interface{}) {
+	if p.level < TRACE {
+		return
+	}
+	p.internalLog(calldepth, TRACE, entries...)
+}
+
+func (p *PackageLogger) Flush() {
+	logger.Lock()
+	defer logger.Unlock()
+	logger.formatter.Flush()
+}

+ 65 - 0
vendor/github.com/coreos/pkg/capnslog/syslog_formatter.go

@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+	"fmt"
+	"log/syslog"
+)
+
+func NewSyslogFormatter(w *syslog.Writer) Formatter {
+	return &syslogFormatter{w}
+}
+
+func NewDefaultSyslogFormatter(tag string) (Formatter, error) {
+	w, err := syslog.New(syslog.LOG_DEBUG, tag)
+	if err != nil {
+		return nil, err
+	}
+	return NewSyslogFormatter(w), nil
+}
+
+type syslogFormatter struct {
+	w *syslog.Writer
+}
+
+func (s *syslogFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
+	for _, entry := range entries {
+		str := fmt.Sprint(entry)
+		switch l {
+		case CRITICAL:
+			s.w.Crit(str)
+		case ERROR:
+			s.w.Err(str)
+		case WARNING:
+			s.w.Warning(str)
+		case NOTICE:
+			s.w.Notice(str)
+		case INFO:
+			s.w.Info(str)
+		case DEBUG:
+			s.w.Debug(str)
+		case TRACE:
+			s.w.Debug(str)
+		default:
+			panic("Unhandled loglevel")
+		}
+	}
+}
+
+func (s *syslogFormatter) Flush() {
+}

+ 52 - 0
vendor/github.com/fsnotify/fsnotify/AUTHORS

@@ -0,0 +1,52 @@
+# Names should be added to this file as
+#	Name or Organization <email address>
+# The email address is not required for organizations.
+
+# You can update this list using the following command:
+#
+#   $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
+
+# Please keep the list sorted.
+
+Aaron L <aaron@bettercoder.net>
+Adrien Bustany <adrien@bustany.org>
+Amit Krishnan <amit.krishnan@oracle.com>
+Anmol Sethi <me@anmol.io>
+Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
+Bruno Bigras <bigras.bruno@gmail.com>
+Caleb Spare <cespare@gmail.com>
+Case Nelson <case@teammating.com>
+Chris Howey <chris@howey.me> <howeyc@gmail.com>
+Christoffer Buchholz <christoffer.buchholz@gmail.com>
+Daniel Wagner-Hall <dawagner@gmail.com>
+Dave Cheney <dave@cheney.net>
+Evan Phoenix <evan@fallingsnow.net>
+Francisco Souza <f@souza.cc>
+Hari haran <hariharan.uno@gmail.com>
+John C Barstow
+Kelvin Fo <vmirage@gmail.com>
+Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
+Matt Layher <mdlayher@gmail.com>
+Nathan Youngman <git@nathany.com>
+Nickolai Zeldovich <nickolai@csail.mit.edu>
+Patrick <patrick@dropbox.com>
+Paul Hammond <paul@paulhammond.org>
+Pawel Knap <pawelknap88@gmail.com>
+Pieter Droogendijk <pieter@binky.org.uk>
+Pursuit92 <JoshChase@techpursuit.net>
+Riku Voipio <riku.voipio@linaro.org>
+Rob Figueiredo <robfig@gmail.com>
+Rodrigo Chiossi <rodrigochiossi@gmail.com>
+Slawek Ligus <root@ooz.ie>
+Soge Zhang <zhssoge@gmail.com>
+Tiffany Jernigan <tiffany.jernigan@intel.com>
+Tilak Sharma <tilaks@google.com>
+Tom Payne <twpayne@gmail.com>
+Travis Cline <travis.cline@gmail.com>
+Tudor Golubenco <tudor.g@gmail.com>
+Vahe Khachikyan <vahe@live.ca>
+Yukang <moorekang@gmail.com>
+bronze1man <bronze1man@gmail.com>
+debrando <denis.brandolini@gmail.com>
+henrikedwards <henrik.edwards@gmail.com>
+铁哥 <guotie.9@gmail.com>

+ 317 - 0
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md

@@ -0,0 +1,317 @@
+# Changelog
+
+## v1.4.7 / 2018-01-09
+
+* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
+* Tests: Fix missing verb on format string (thanks @rchiossi)
+* Linux: Fix deadlock in Remove (thanks @aarondl)
+* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
+* Docs: Moved FAQ into the README (thanks @vahe)
+* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
+* Docs: replace references to OS X with macOS
+
+## v1.4.2 / 2016-10-10
+
+* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
+
+## v1.4.1 / 2016-10-04
+
+* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
+
+## v1.4.0 / 2016-10-01
+
+* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
+
+## v1.3.1 / 2016-06-28
+
+* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
+
+## v1.3.0 / 2016-04-19
+
+* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
+
+## v1.2.10 / 2016-03-02
+
+* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
+
+## v1.2.9 / 2016-01-13
+
+kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
+
+## v1.2.8 / 2015-12-17
+
+* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
+* inotify: fix race in test
+* enable race detection for continuous integration (Linux, Mac, Windows)
+
+## v1.2.5 / 2015-10-17
+
+* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
+* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
+* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
+* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
+
+## v1.2.1 / 2015-10-14
+
+* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
+
+## v1.2.0 / 2015-02-08
+
+* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
+* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
+* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
+
+## v1.1.1 / 2015-02-05
+
+* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
+
+## v1.1.0 / 2014-12-12
+
+* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
+    * add low-level functions
+    * only need to store flags on directories
+    * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
+    * done can be an unbuffered channel
+    * remove calls to os.NewSyscallError
+* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
+* kqueue: fix regression in  rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
+* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
+
+## v1.0.4 / 2014-09-07
+
+* kqueue: add dragonfly to the build tags.
+* Rename source code files, rearrange code so exported APIs are at the top.
+* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
+
+## v1.0.3 / 2014-08-19
+
+* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
+
+## v1.0.2 / 2014-08-17
+
+* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
+* [Fix] Make ./path and path equivalent. (thanks @zhsso)
+
+## v1.0.0 / 2014-08-15
+
+* [API] Remove AddWatch on Windows, use Add.
+* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
+* Minor updates based on feedback from golint.
+
+## dev / 2014-07-09
+
+* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
+* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
+
+## dev / 2014-07-04
+
+* kqueue: fix incorrect mutex used in Close()
+* Update example to demonstrate usage of Op.
+
+## dev / 2014-06-28
+
+* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
+* Fix for String() method on Event (thanks Alex Brainman)
+* Don't build on Plan 9 or Solaris (thanks @4ad)
+
+## dev / 2014-06-21
+
+* Events channel of type Event rather than *Event.
+* [internal] use syscall constants directly for inotify and kqueue.
+* [internal] kqueue: rename events to kevents and fileEvent to event.
+
+## dev / 2014-06-19
+
+* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
+* [internal] remove cookie from Event struct (unused).
+* [internal] Event struct has the same definition across every OS.
+* [internal] remove internal watch and removeWatch methods.
+
+## dev / 2014-06-12
+
+* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
+* [API] Pluralized channel names: Events and Errors.
+* [API] Renamed FileEvent struct to Event.
+* [API] Op constants replace methods like IsCreate().
+
+## dev / 2014-06-12
+
+* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
+
+## dev / 2014-05-23
+
+* [API] Remove current implementation of WatchFlags.
+    * current implementation doesn't take advantage of OS for efficiency
+    * provides little benefit over filtering events as they are received, but has  extra bookkeeping and mutexes
+    * no tests for the current implementation
+    * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
+
+## v0.9.3 / 2014-12-31
+
+* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
+
+## v0.9.2 / 2014-08-17
+
+* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
+
+## v0.9.1 / 2014-06-12
+
+* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
+
+## v0.9.0 / 2014-01-17
+
+* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
+* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
+* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
+
+## v0.8.12 / 2013-11-13
+
+* [API] Remove FD_SET and friends from Linux adapter
+
+## v0.8.11 / 2013-11-02
+
+* [Doc] Add Changelog [#72][] (thanks @nathany)
+* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
+
+## v0.8.10 / 2013-10-19
+
+* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
+* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
+* [Doc] specify OS-specific limits in README (thanks @debrando)
+
+## v0.8.9 / 2013-09-08
+
+* [Doc] Contributing (thanks @nathany)
+* [Doc] update package path in example code [#63][] (thanks @paulhammond)
+* [Doc] GoCI badge in README (Linux only) [#60][]
+* [Doc] Cross-platform testing with Vagrant  [#59][] (thanks @nathany)
+
+## v0.8.8 / 2013-06-17
+
+* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
+
+## v0.8.7 / 2013-06-03
+
+* [API] Make syscall flags internal
+* [Fix] inotify: ignore event changes
+* [Fix] race in symlink test [#45][] (reported by @srid)
+* [Fix] tests on Windows
+* lower case error messages
+
+## v0.8.6 / 2013-05-23
+
+* kqueue: Use EVT_ONLY flag on Darwin
+* [Doc] Update README with full example
+
+## v0.8.5 / 2013-05-09
+
+* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
+
+## v0.8.4 / 2013-04-07
+
+* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
+
+## v0.8.3 / 2013-03-13
+
+* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
+* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
+
+## v0.8.2 / 2013-02-07
+
+* [Doc] add Authors
+* [Fix] fix data races for map access [#29][] (thanks @fsouza)
+
+## v0.8.1 / 2013-01-09
+
+* [Fix] Windows path separators
+* [Doc] BSD License
+
+## v0.8.0 / 2012-11-09
+
+* kqueue: directory watching improvements (thanks @vmirage)
+* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
+* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
+
+## v0.7.4 / 2012-10-09
+
+* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
+* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
+* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
+* [Fix] kqueue: modify after recreation of file
+
+## v0.7.3 / 2012-09-27
+
+* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
+* [Fix] kqueue: no longer get duplicate CREATE events
+
+## v0.7.2 / 2012-09-01
+
+* kqueue: events for created directories
+
+## v0.7.1 / 2012-07-14
+
+* [Fix] for renaming files
+
+## v0.7.0 / 2012-07-02
+
+* [Feature] FSNotify flags
+* [Fix] inotify: Added file name back to event path
+
+## v0.6.0 / 2012-06-06
+
+* kqueue: watch files after directory created (thanks @tmc)
+
+## v0.5.1 / 2012-05-22
+
+* [Fix] inotify: remove all watches before Close()
+
+## v0.5.0 / 2012-05-03
+
+* [API] kqueue: return errors during watch instead of sending over channel
+* kqueue: match symlink behavior on Linux
+* inotify: add `DELETE_SELF` (requested by @taralx)
+* [Fix] kqueue: handle EINTR (reported by @robfig)
+* [Doc] Godoc example [#1][] (thanks @davecheney)
+
+## v0.4.0 / 2012-03-30
+
+* Go 1 released: build with go tool
+* [Feature] Windows support using winfsnotify
+* Windows does not have attribute change notifications
+* Roll attribute notifications into IsModify
+
+## v0.3.0 / 2012-02-19
+
+* kqueue: add files when watch directory
+
+## v0.2.0 / 2011-12-30
+
+* update to latest Go weekly code
+
+## v0.1.0 / 2011-10-19
+
+* kqueue: add watch on file creation to match inotify
+* kqueue: create file event
+* inotify: ignore `IN_IGNORED` events
+* event String()
+* linux: common FileEvent functions
+* initial commit
+
+[#79]: https://github.com/howeyc/fsnotify/pull/79
+[#77]: https://github.com/howeyc/fsnotify/pull/77
+[#72]: https://github.com/howeyc/fsnotify/issues/72
+[#71]: https://github.com/howeyc/fsnotify/issues/71
+[#70]: https://github.com/howeyc/fsnotify/issues/70
+[#63]: https://github.com/howeyc/fsnotify/issues/63
+[#62]: https://github.com/howeyc/fsnotify/issues/62
+[#60]: https://github.com/howeyc/fsnotify/issues/60
+[#59]: https://github.com/howeyc/fsnotify/issues/59
+[#49]: https://github.com/howeyc/fsnotify/issues/49
+[#45]: https://github.com/howeyc/fsnotify/issues/45
+[#40]: https://github.com/howeyc/fsnotify/issues/40
+[#36]: https://github.com/howeyc/fsnotify/issues/36
+[#33]: https://github.com/howeyc/fsnotify/issues/33
+[#29]: https://github.com/howeyc/fsnotify/issues/29
+[#25]: https://github.com/howeyc/fsnotify/issues/25
+[#24]: https://github.com/howeyc/fsnotify/issues/24
+[#21]: https://github.com/howeyc/fsnotify/issues/21

+ 77 - 0
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md

@@ -0,0 +1,77 @@
+# Contributing
+
+## Issues
+
+* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
+* Please indicate the platform you are using fsnotify on.
+* A code example to reproduce the problem is appreciated.
+
+## Pull Requests
+
+### Contributor License Agreement
+
+fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
+
+Please indicate that you have signed the CLA in your pull request.
+
+### How fsnotify is Developed
+
+* Development is done on feature branches.
+* Tests are run on BSD, Linux, macOS and Windows.
+* Pull requests are reviewed and [applied to master][am] using [hub][].
+  * Maintainers may modify or squash commits rather than asking contributors to.
+* To issue a new release, the maintainers will:
+  * Update the CHANGELOG
+  * Tag a version, which will become available through gopkg.in.
+ 
+### How to Fork
+
+For smooth sailing, always use the original import path. Installing with `go get` makes this easy. 
+
+1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Ensure everything works and the tests pass (see below)
+4. Commit your changes (`git commit -am 'Add some feature'`)
+
+Contribute upstream:
+
+1. Fork fsnotify on GitHub
+2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
+3. Push to the branch (`git push fork my-new-feature`)
+4. Create a new Pull Request on GitHub
+
+This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
+
+### Testing
+
+fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
+
+Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
+
+To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
+
+* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
+* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
+* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
+* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
+* When you're done, you will want to halt or destroy the Vagrant boxes.
+
+Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
+
+Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
+
+### Maintainers
+
+Help maintaining fsnotify is welcome. To be a maintainer:
+
+* Submit a pull request and sign the CLA as above.
+* You must be able to run the test suite on Mac, Windows, Linux and BSD.
+
+To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
+
+All code changes should be internal pull requests.
+
+Releases are tagged using [Semantic Versioning](http://semver.org/).
+
+[hub]: https://github.com/github/hub
+[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs

+ 28 - 0
vendor/github.com/fsnotify/fsnotify/LICENSE

@@ -0,0 +1,28 @@
+Copyright (c) 2012 The Go Authors. All rights reserved.
+Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 130 - 0
vendor/github.com/fsnotify/fsnotify/README.md

@@ -0,0 +1,130 @@
+# File system notifications for Go
+
+[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify)
+
+fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
+
+```console
+go get -u golang.org/x/sys/...
+```
+
+Cross platform: Windows, Linux, BSD and macOS.
+
+| Adapter               | OS                               | Status                                                                                                                          |
+| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| inotify               | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
+| kqueue                | BSD, macOS, iOS\*                | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
+| ReadDirectoryChangesW | Windows                          | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
+| FSEvents              | macOS                            | [Planned](https://github.com/fsnotify/fsnotify/issues/11)                                                                       |
+| FEN                   | Solaris 11                       | [In Progress](https://github.com/fsnotify/fsnotify/issues/12)                                                                   |
+| fanotify              | Linux 2.6.37+                    | [Planned](https://github.com/fsnotify/fsnotify/issues/114)                                                                      |
+| USN Journals          | Windows                          | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)                                                                         |
+| Polling               | *All*                            | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)                                                                          |
+
+\* Android and iOS are untested.
+
+Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
+
+## API stability
+
+fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). 
+
+All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
+
+Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
+
+## Usage
+
+```go
+package main
+
+import (
+	"log"
+
+	"github.com/fsnotify/fsnotify"
+)
+
+func main() {
+	watcher, err := fsnotify.NewWatcher()
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer watcher.Close()
+
+	done := make(chan bool)
+	go func() {
+		for {
+			select {
+			case event, ok := <-watcher.Events:
+				if !ok {
+					return
+				}
+				log.Println("event:", event)
+				if event.Op&fsnotify.Write == fsnotify.Write {
+					log.Println("modified file:", event.Name)
+				}
+			case err, ok := <-watcher.Errors:
+				if !ok {
+					return
+				}
+				log.Println("error:", err)
+			}
+		}
+	}()
+
+	err = watcher.Add("/tmp/foo")
+	if err != nil {
+		log.Fatal(err)
+	}
+	<-done
+}
+```
+
+## Contributing
+
+Please refer to [CONTRIBUTING][] before opening an issue or pull request.
+
+## Example
+
+See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
+
+## FAQ
+
+**When a file is moved to another directory is it still being watched?**
+
+No (it shouldn't be, unless you are watching where it was moved to).
+
+**When I watch a directory, are all subdirectories watched as well?**
+
+No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
+
+**Do I have to watch the Error and Event channels in a separate goroutine?**
+
+As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
+
+**Why am I receiving multiple events for the same file on OS X?**
+
+Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
+
+**How many files can be watched at once?**
+
+There are OS-specific limits as to how many watches can be created:
+* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
+* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
+
+**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
+
+fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
+
+[#62]: https://github.com/howeyc/fsnotify/issues/62
+[#18]: https://github.com/fsnotify/fsnotify/issues/18
+[#11]: https://github.com/fsnotify/fsnotify/issues/11
+[#7]: https://github.com/howeyc/fsnotify/issues/7
+
+[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
+
+## Related Projects
+
+* [notify](https://github.com/rjeczalik/notify)
+* [fsevents](https://github.com/fsnotify/fsevents)
+

+ 37 - 0
vendor/github.com/fsnotify/fsnotify/fen.go

@@ -0,0 +1,37 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package fsnotify
+
+import (
+	"errors"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+	Events chan Event
+	Errors chan error
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+	return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+	return nil
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+	return nil
+}

+ 68 - 0
vendor/github.com/fsnotify/fsnotify/fsnotify.go

@@ -0,0 +1,68 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+// Package fsnotify provides a platform-independent interface for file system notifications.
+package fsnotify
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+)
+
+// Event represents a single file system notification.
+type Event struct {
+	Name string // Relative path to the file or directory.
+	Op   Op     // File operation that triggered the event.
+}
+
+// Op describes a set of file operations.
+type Op uint32
+
+// These are the generalized file operations that can trigger a notification.
+const (
+	Create Op = 1 << iota
+	Write
+	Remove
+	Rename
+	Chmod
+)
+
+func (op Op) String() string {
+	// Use a buffer for efficient string concatenation
+	var buffer bytes.Buffer
+
+	if op&Create == Create {
+		buffer.WriteString("|CREATE")
+	}
+	if op&Remove == Remove {
+		buffer.WriteString("|REMOVE")
+	}
+	if op&Write == Write {
+		buffer.WriteString("|WRITE")
+	}
+	if op&Rename == Rename {
+		buffer.WriteString("|RENAME")
+	}
+	if op&Chmod == Chmod {
+		buffer.WriteString("|CHMOD")
+	}
+	if buffer.Len() == 0 {
+		return ""
+	}
+	return buffer.String()[1:] // Strip leading pipe
+}
+
+// String returns a string representation of the event in the form
+// "file: REMOVE|WRITE|..."
+func (e Event) String() string {
+	return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
+}
+
+// Common errors that can be reported by a watcher
+var (
+	ErrEventOverflow = errors.New("fsnotify queue overflow")
+)

+ 5 - 0
vendor/github.com/fsnotify/fsnotify/go.mod

@@ -0,0 +1,5 @@
+module github.com/fsnotify/fsnotify
+
+go 1.13
+
+require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9

+ 2 - 0
vendor/github.com/fsnotify/fsnotify/go.sum

@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

+ 337 - 0
vendor/github.com/fsnotify/fsnotify/inotify.go

@@ -0,0 +1,337 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package fsnotify
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"unsafe"
+
+	"golang.org/x/sys/unix"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+	Events   chan Event
+	Errors   chan error
+	mu       sync.Mutex // Map access
+	fd       int
+	poller   *fdPoller
+	watches  map[string]*watch // Map of inotify watches (key: path)
+	paths    map[int]string    // Map of watched paths (key: watch descriptor)
+	done     chan struct{}     // Channel for sending a "quit message" to the reader goroutine
+	doneResp chan struct{}     // Channel to respond to Close
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+	// Create inotify fd
+	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
+	if fd == -1 {
+		return nil, errno
+	}
+	// Create epoll
+	poller, err := newFdPoller(fd)
+	if err != nil {
+		unix.Close(fd)
+		return nil, err
+	}
+	w := &Watcher{
+		fd:       fd,
+		poller:   poller,
+		watches:  make(map[string]*watch),
+		paths:    make(map[int]string),
+		Events:   make(chan Event),
+		Errors:   make(chan error),
+		done:     make(chan struct{}),
+		doneResp: make(chan struct{}),
+	}
+
+	go w.readEvents()
+	return w, nil
+}
+
+func (w *Watcher) isClosed() bool {
+	select {
+	case <-w.done:
+		return true
+	default:
+		return false
+	}
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+	if w.isClosed() {
+		return nil
+	}
+
+	// Send 'close' signal to goroutine, and set the Watcher to closed.
+	close(w.done)
+
+	// Wake up goroutine
+	w.poller.wake()
+
+	// Wait for goroutine to close
+	<-w.doneResp
+
+	return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+	name = filepath.Clean(name)
+	if w.isClosed() {
+		return errors.New("inotify instance already closed")
+	}
+
+	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
+		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
+		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
+
+	var flags uint32 = agnosticEvents
+
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	watchEntry := w.watches[name]
+	if watchEntry != nil {
+		flags |= watchEntry.flags | unix.IN_MASK_ADD
+	}
+	wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
+	if wd == -1 {
+		return errno
+	}
+
+	if watchEntry == nil {
+		w.watches[name] = &watch{wd: uint32(wd), flags: flags}
+		w.paths[wd] = name
+	} else {
+		watchEntry.wd = uint32(wd)
+		watchEntry.flags = flags
+	}
+
+	return nil
+}
+
+// Remove stops watching the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+	name = filepath.Clean(name)
+
+	// Fetch the watch.
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	watch, ok := w.watches[name]
+
+	// Remove it from inotify.
+	if !ok {
+		return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
+	}
+
+	// We successfully removed the watch if InotifyRmWatch doesn't return an
+	// error, we need to clean up our internal state to ensure it matches
+	// inotify's kernel state.
+	delete(w.paths, int(watch.wd))
+	delete(w.watches, name)
+
+	// inotify_rm_watch will return EINVAL if the file has been deleted;
+	// the inotify will already have been removed.
+	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
+	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
+	// so that EINVAL means that the wd is being rm_watch()ed or its file removed
+	// by another thread and we have not received IN_IGNORE event.
+	success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
+	if success == -1 {
+		// TODO: Perhaps it's not helpful to return an error here in every case.
+		// the only two possible errors are:
+		// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
+		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
+		// Watch descriptors are invalidated when they are removed explicitly or implicitly;
+		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
+		return errno
+	}
+
+	return nil
+}
+
+type watch struct {
+	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
+	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
+}
+
+// readEvents reads from the inotify file descriptor, converts the
+// received events into Event objects and sends them via the Events channel
+func (w *Watcher) readEvents() {
+	var (
+		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
+		n     int                                  // Number of bytes read with read()
+		errno error                                // Syscall errno
+		ok    bool                                 // For poller.wait
+	)
+
+	defer close(w.doneResp)
+	defer close(w.Errors)
+	defer close(w.Events)
+	defer unix.Close(w.fd)
+	defer w.poller.close()
+
+	for {
+		// See if we have been closed.
+		if w.isClosed() {
+			return
+		}
+
+		ok, errno = w.poller.wait()
+		if errno != nil {
+			select {
+			case w.Errors <- errno:
+			case <-w.done:
+				return
+			}
+			continue
+		}
+
+		if !ok {
+			continue
+		}
+
+		n, errno = unix.Read(w.fd, buf[:])
+		// If a signal interrupted execution, see if we've been asked to close, and try again.
+		// http://man7.org/linux/man-pages/man7/signal.7.html :
+		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
+		if errno == unix.EINTR {
+			continue
+		}
+
+		// unix.Read might have been woken up by Close. If so, we're done.
+		if w.isClosed() {
+			return
+		}
+
+		if n < unix.SizeofInotifyEvent {
+			var err error
+			if n == 0 {
+				// If EOF is received. This should really never happen.
+				err = io.EOF
+			} else if n < 0 {
+				// If an error occurred while reading.
+				err = errno
+			} else {
+				// Read was too short.
+				err = errors.New("notify: short read in readEvents()")
+			}
+			select {
+			case w.Errors <- err:
+			case <-w.done:
+				return
+			}
+			continue
+		}
+
+		var offset uint32
+		// We don't know how many events we just read into the buffer
+		// While the offset points to at least one whole event...
+		for offset <= uint32(n-unix.SizeofInotifyEvent) {
+			// Point "raw" to the event in the buffer
+			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+
+			mask := uint32(raw.Mask)
+			nameLen := uint32(raw.Len)
+
+			if mask&unix.IN_Q_OVERFLOW != 0 {
+				select {
+				case w.Errors <- ErrEventOverflow:
+				case <-w.done:
+					return
+				}
+			}
+
+			// If the event happened to the watched directory or the watched file, the kernel
+			// doesn't append the filename to the event, but we would like to always fill the
+			// the "Name" field with a valid filename. We retrieve the path of the watch from
+			// the "paths" map.
+			w.mu.Lock()
+			name, ok := w.paths[int(raw.Wd)]
+			// IN_DELETE_SELF occurs when the file/directory being watched is removed.
+			// This is a sign to clean up the maps, otherwise we are no longer in sync
+			// with the inotify kernel state which has already deleted the watch
+			// automatically.
+			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
+				delete(w.paths, int(raw.Wd))
+				delete(w.watches, name)
+			}
+			w.mu.Unlock()
+
+			if nameLen > 0 {
+				// Point "bytes" at the first byte of the filename
+				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
+				// The filename is padded with NULL bytes. TrimRight() gets rid of those.
+				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
+			}
+
+			event := newEvent(name, mask)
+
+			// Send the events that are not ignored on the events channel
+			if !event.ignoreLinux(mask) {
+				select {
+				case w.Events <- event:
+				case <-w.done:
+					return
+				}
+			}
+
+			// Move to the next event in the buffer
+			offset += unix.SizeofInotifyEvent + nameLen
+		}
+	}
+}
+
+// Certain types of events can be "ignored" and not sent over the Events
+// channel. Such as events marked ignore by the kernel, or MODIFY events
+// against files that do not exist.
+func (e *Event) ignoreLinux(mask uint32) bool {
+	// Ignore anything the inotify API says to ignore
+	if mask&unix.IN_IGNORED == unix.IN_IGNORED {
+		return true
+	}
+
+	// If the event is not a DELETE or RENAME, the file must exist.
+	// Otherwise the event is ignored.
+	// *Note*: this was put in place because it was seen that a MODIFY
+	// event was sent after the DELETE. This ignores that MODIFY and
+	// assumes a DELETE will come or has come if the file doesn't exist.
+	if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
+		_, statErr := os.Lstat(e.Name)
+		return os.IsNotExist(statErr)
+	}
+	return false
+}
+
+// newEvent returns an platform-independent Event based on an inotify mask.
+func newEvent(name string, mask uint32) Event {
+	e := Event{Name: name}
+	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
+		e.Op |= Create
+	}
+	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
+		e.Op |= Remove
+	}
+	if mask&unix.IN_MODIFY == unix.IN_MODIFY {
+		e.Op |= Write
+	}
+	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
+		e.Op |= Rename
+	}
+	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
+		e.Op |= Chmod
+	}
+	return e
+}

+ 187 - 0
vendor/github.com/fsnotify/fsnotify/inotify_poller.go

@@ -0,0 +1,187 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package fsnotify
+
+import (
+	"errors"
+
+	"golang.org/x/sys/unix"
+)
+
+type fdPoller struct {
+	fd   int    // File descriptor (as returned by the inotify_init() syscall)
+	epfd int    // Epoll file descriptor
+	pipe [2]int // Pipe for waking up
+}
+
+func emptyPoller(fd int) *fdPoller {
+	poller := new(fdPoller)
+	poller.fd = fd
+	poller.epfd = -1
+	poller.pipe[0] = -1
+	poller.pipe[1] = -1
+	return poller
+}
+
+// Create a new inotify poller.
+// This creates an inotify handler, and an epoll handler.
+func newFdPoller(fd int) (*fdPoller, error) {
+	var errno error
+	poller := emptyPoller(fd)
+	defer func() {
+		if errno != nil {
+			poller.close()
+		}
+	}()
+	poller.fd = fd
+
+	// Create epoll fd
+	poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
+	if poller.epfd == -1 {
+		return nil, errno
+	}
+	// Create pipe; pipe[0] is the read end, pipe[1] the write end.
+	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
+	if errno != nil {
+		return nil, errno
+	}
+
+	// Register inotify fd with epoll
+	event := unix.EpollEvent{
+		Fd:     int32(poller.fd),
+		Events: unix.EPOLLIN,
+	}
+	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
+	if errno != nil {
+		return nil, errno
+	}
+
+	// Register pipe fd with epoll
+	event = unix.EpollEvent{
+		Fd:     int32(poller.pipe[0]),
+		Events: unix.EPOLLIN,
+	}
+	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
+	if errno != nil {
+		return nil, errno
+	}
+
+	return poller, nil
+}
+
+// Wait using epoll.
+// Returns true if something is ready to be read,
+// false if there is not.
+func (poller *fdPoller) wait() (bool, error) {
+	// 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
+	// I don't know whether epoll_wait returns the number of events returned,
+	// or the total number of events ready.
+	// I decided to catch both by making the buffer one larger than the maximum.
+	events := make([]unix.EpollEvent, 7)
+	for {
+		n, errno := unix.EpollWait(poller.epfd, events, -1)
+		if n == -1 {
+			if errno == unix.EINTR {
+				continue
+			}
+			return false, errno
+		}
+		if n == 0 {
+			// If there are no events, try again.
+			continue
+		}
+		if n > 6 {
+			// This should never happen. More events were returned than should be possible.
+			return false, errors.New("epoll_wait returned more events than I know what to do with")
+		}
+		ready := events[:n]
+		epollhup := false
+		epollerr := false
+		epollin := false
+		for _, event := range ready {
+			if event.Fd == int32(poller.fd) {
+				if event.Events&unix.EPOLLHUP != 0 {
+					// This should not happen, but if it does, treat it as a wakeup.
+					epollhup = true
+				}
+				if event.Events&unix.EPOLLERR != 0 {
+					// If an error is waiting on the file descriptor, we should pretend
+					// something is ready to read, and let unix.Read pick up the error.
+					epollerr = true
+				}
+				if event.Events&unix.EPOLLIN != 0 {
+					// There is data to read.
+					epollin = true
+				}
+			}
+			if event.Fd == int32(poller.pipe[0]) {
+				if event.Events&unix.EPOLLHUP != 0 {
+					// Write pipe descriptor was closed, by us. This means we're closing down the
+					// watcher, and we should wake up.
+				}
+				if event.Events&unix.EPOLLERR != 0 {
+					// If an error is waiting on the pipe file descriptor.
+					// This is an absolute mystery, and should never ever happen.
+					return false, errors.New("Error on the pipe descriptor.")
+				}
+				if event.Events&unix.EPOLLIN != 0 {
+					// This is a regular wakeup, so we have to clear the buffer.
+					err := poller.clearWake()
+					if err != nil {
+						return false, err
+					}
+				}
+			}
+		}
+
+		if epollhup || epollerr || epollin {
+			return true, nil
+		}
+		return false, nil
+	}
+}
+
+// Close the write end of the poller.
+func (poller *fdPoller) wake() error {
+	buf := make([]byte, 1)
+	n, errno := unix.Write(poller.pipe[1], buf)
+	if n == -1 {
+		if errno == unix.EAGAIN {
+			// Buffer is full, poller will wake.
+			return nil
+		}
+		return errno
+	}
+	return nil
+}
+
+func (poller *fdPoller) clearWake() error {
+	// You have to be woken up a LOT in order to get to 100!
+	buf := make([]byte, 100)
+	n, errno := unix.Read(poller.pipe[0], buf)
+	if n == -1 {
+		if errno == unix.EAGAIN {
+			// Buffer is empty, someone else cleared our wake.
+			return nil
+		}
+		return errno
+	}
+	return nil
+}
+
+// Close all poller file descriptors, but not the one passed to it.
+func (poller *fdPoller) close() {
+	if poller.pipe[1] != -1 {
+		unix.Close(poller.pipe[1])
+	}
+	if poller.pipe[0] != -1 {
+		unix.Close(poller.pipe[0])
+	}
+	if poller.epfd != -1 {
+		unix.Close(poller.epfd)
+	}
+}

+ 521 - 0
vendor/github.com/fsnotify/fsnotify/kqueue.go

@@ -0,0 +1,521 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd openbsd netbsd dragonfly darwin
+
+package fsnotify
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sync"
+	"time"
+
+	"golang.org/x/sys/unix"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+	Events chan Event
+	Errors chan error
+	done   chan struct{} // Channel for sending a "quit message" to the reader goroutine
+
+	kq int // File descriptor (as returned by the kqueue() syscall).
+
+	mu              sync.Mutex        // Protects access to watcher data
+	watches         map[string]int    // Map of watched file descriptors (key: path).
+	externalWatches map[string]bool   // Map of watches added by user of the library.
+	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue.
+	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events.
+	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events).
+	isClosed        bool              // Set to true when Close() is first called
+}
+
+type pathInfo struct {
+	name  string
+	isDir bool
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+	kq, err := kqueue()
+	if err != nil {
+		return nil, err
+	}
+
+	w := &Watcher{
+		kq:              kq,
+		watches:         make(map[string]int),
+		dirFlags:        make(map[string]uint32),
+		paths:           make(map[int]pathInfo),
+		fileExists:      make(map[string]bool),
+		externalWatches: make(map[string]bool),
+		Events:          make(chan Event),
+		Errors:          make(chan error),
+		done:            make(chan struct{}),
+	}
+
+	go w.readEvents()
+	return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+	w.mu.Lock()
+	if w.isClosed {
+		w.mu.Unlock()
+		return nil
+	}
+	w.isClosed = true
+
+	// copy paths to remove while locked
+	var pathsToRemove = make([]string, 0, len(w.watches))
+	for name := range w.watches {
+		pathsToRemove = append(pathsToRemove, name)
+	}
+	w.mu.Unlock()
+	// unlock before calling Remove, which also locks
+
+	for _, name := range pathsToRemove {
+		w.Remove(name)
+	}
+
+	// send a "quit" message to the reader goroutine
+	close(w.done)
+
+	return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+	w.mu.Lock()
+	w.externalWatches[name] = true
+	w.mu.Unlock()
+	_, err := w.addWatch(name, noteAllEvents)
+	return err
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+	name = filepath.Clean(name)
+	w.mu.Lock()
+	watchfd, ok := w.watches[name]
+	w.mu.Unlock()
+	if !ok {
+		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
+	}
+
+	const registerRemove = unix.EV_DELETE
+	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
+		return err
+	}
+
+	unix.Close(watchfd)
+
+	w.mu.Lock()
+	isDir := w.paths[watchfd].isDir
+	delete(w.watches, name)
+	delete(w.paths, watchfd)
+	delete(w.dirFlags, name)
+	w.mu.Unlock()
+
+	// Find all watched paths that are in this directory that are not external.
+	if isDir {
+		var pathsToRemove []string
+		w.mu.Lock()
+		for _, path := range w.paths {
+			wdir, _ := filepath.Split(path.name)
+			if filepath.Clean(wdir) == name {
+				if !w.externalWatches[path.name] {
+					pathsToRemove = append(pathsToRemove, path.name)
+				}
+			}
+		}
+		w.mu.Unlock()
+		for _, name := range pathsToRemove {
+			// Since these are internal, not much sense in propagating error
+			// to the user, as that will just confuse them with an error about
+			// a path they did not explicitly watch themselves.
+			w.Remove(name)
+		}
+	}
+
+	return nil
+}
+
+// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
+const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
+
+// keventWaitTime to block on each read from kevent
+var keventWaitTime = durationToTimespec(100 * time.Millisecond)
+
+// addWatch adds name to the watched file set.
+// The flags are interpreted as described in kevent(2).
+// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
+func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
+	var isDir bool
+	// Make ./name and name equivalent
+	name = filepath.Clean(name)
+
+	w.mu.Lock()
+	if w.isClosed {
+		w.mu.Unlock()
+		return "", errors.New("kevent instance already closed")
+	}
+	watchfd, alreadyWatching := w.watches[name]
+	// We already have a watch, but we can still override flags.
+	if alreadyWatching {
+		isDir = w.paths[watchfd].isDir
+	}
+	w.mu.Unlock()
+
+	if !alreadyWatching {
+		fi, err := os.Lstat(name)
+		if err != nil {
+			return "", err
+		}
+
+		// Don't watch sockets.
+		if fi.Mode()&os.ModeSocket == os.ModeSocket {
+			return "", nil
+		}
+
+		// Don't watch named pipes.
+		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
+			return "", nil
+		}
+
+		// Follow Symlinks
+		// Unfortunately, Linux can add bogus symlinks to watch list without
+		// issue, and Windows can't do symlinks period (AFAIK). To  maintain
+		// consistency, we will act like everything is fine. There will simply
+		// be no file events for broken symlinks.
+		// Hence the returns of nil on errors.
+		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
+			name, err = filepath.EvalSymlinks(name)
+			if err != nil {
+				return "", nil
+			}
+
+			w.mu.Lock()
+			_, alreadyWatching = w.watches[name]
+			w.mu.Unlock()
+
+			if alreadyWatching {
+				return name, nil
+			}
+
+			fi, err = os.Lstat(name)
+			if err != nil {
+				return "", nil
+			}
+		}
+
+		watchfd, err = unix.Open(name, openMode, 0700)
+		if watchfd == -1 {
+			return "", err
+		}
+
+		isDir = fi.IsDir()
+	}
+
+	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
+	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
+		unix.Close(watchfd)
+		return "", err
+	}
+
+	if !alreadyWatching {
+		w.mu.Lock()
+		w.watches[name] = watchfd
+		w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
+		w.mu.Unlock()
+	}
+
+	if isDir {
+		// Watch the directory if it has not been watched before,
+		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
+		w.mu.Lock()
+
+		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
+			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
+		// Store flags so this watch can be updated later
+		w.dirFlags[name] = flags
+		w.mu.Unlock()
+
+		if watchDir {
+			if err := w.watchDirectoryFiles(name); err != nil {
+				return "", err
+			}
+		}
+	}
+	return name, nil
+}
+
+// readEvents reads from kqueue and converts the received kevents into
+// Event values that it sends down the Events channel.
+func (w *Watcher) readEvents() {
+	eventBuffer := make([]unix.Kevent_t, 10)
+
+loop:
+	for {
+		// See if there is a message on the "done" channel
+		select {
+		case <-w.done:
+			break loop
+		default:
+		}
+
+		// Get new events
+		kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
+		// EINTR is okay, the syscall was interrupted before timeout expired.
+		if err != nil && err != unix.EINTR {
+			select {
+			case w.Errors <- err:
+			case <-w.done:
+				break loop
+			}
+			continue
+		}
+
+		// Flush the events we received to the Events channel
+		for len(kevents) > 0 {
+			kevent := &kevents[0]
+			watchfd := int(kevent.Ident)
+			mask := uint32(kevent.Fflags)
+			w.mu.Lock()
+			path := w.paths[watchfd]
+			w.mu.Unlock()
+			event := newEvent(path.name, mask)
+
+			if path.isDir && !(event.Op&Remove == Remove) {
+				// Double check to make sure the directory exists. This can happen when
+				// we do a rm -fr on a recursively watched folders and we receive a
+				// modification event first but the folder has been deleted and later
+				// receive the delete event
+				if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
+					// mark is as delete event
+					event.Op |= Remove
+				}
+			}
+
+			if event.Op&Rename == Rename || event.Op&Remove == Remove {
+				w.Remove(event.Name)
+				w.mu.Lock()
+				delete(w.fileExists, event.Name)
+				w.mu.Unlock()
+			}
+
+			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
+				w.sendDirectoryChangeEvents(event.Name)
+			} else {
+				// Send the event on the Events channel.
+				select {
+				case w.Events <- event:
+				case <-w.done:
+					break loop
+				}
+			}
+
+			if event.Op&Remove == Remove {
+				// Look for a file that may have overwritten this.
+				// For example, mv f1 f2 will delete f2, then create f2.
+				if path.isDir {
+					fileDir := filepath.Clean(event.Name)
+					w.mu.Lock()
+					_, found := w.watches[fileDir]
+					w.mu.Unlock()
+					if found {
+						// make sure the directory exists before we watch for changes. When we
+						// do a recursive watch and perform rm -fr, the parent directory might
+						// have gone missing, ignore the missing directory and let the
+						// upcoming delete event remove the watch from the parent directory.
+						if _, err := os.Lstat(fileDir); err == nil {
+							w.sendDirectoryChangeEvents(fileDir)
+						}
+					}
+				} else {
+					filePath := filepath.Clean(event.Name)
+					if fileInfo, err := os.Lstat(filePath); err == nil {
+						w.sendFileCreatedEventIfNew(filePath, fileInfo)
+					}
+				}
+			}
+
+			// Move to next event
+			kevents = kevents[1:]
+		}
+	}
+
+	// cleanup
+	err := unix.Close(w.kq)
+	if err != nil {
+		// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
+		select {
+		case w.Errors <- err:
+		default:
+		}
+	}
+	close(w.Events)
+	close(w.Errors)
+}
+
+// newEvent returns an platform-independent Event based on kqueue Fflags.
+func newEvent(name string, mask uint32) Event {
+	e := Event{Name: name}
+	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
+		e.Op |= Remove
+	}
+	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
+		e.Op |= Write
+	}
+	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
+		e.Op |= Rename
+	}
+	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
+		e.Op |= Chmod
+	}
+	return e
+}
+
+func newCreateEvent(name string) Event {
+	return Event{Name: name, Op: Create}
+}
+
+// watchDirectoryFiles to mimic inotify when adding a watch on a directory
+func (w *Watcher) watchDirectoryFiles(dirPath string) error {
+	// Get all files
+	files, err := ioutil.ReadDir(dirPath)
+	if err != nil {
+		return err
+	}
+
+	for _, fileInfo := range files {
+		filePath := filepath.Join(dirPath, fileInfo.Name())
+		filePath, err = w.internalWatch(filePath, fileInfo)
+		if err != nil {
+			return err
+		}
+
+		w.mu.Lock()
+		w.fileExists[filePath] = true
+		w.mu.Unlock()
+	}
+
+	return nil
+}
+
+// sendDirectoryEvents searches the directory for newly created files
+// and sends them over the event channel. This functionality is to have
+// the BSD version of fsnotify match Linux inotify which provides a
+// create event for files created in a watched directory.
+func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
+	// Get all files
+	files, err := ioutil.ReadDir(dirPath)
+	if err != nil {
+		select {
+		case w.Errors <- err:
+		case <-w.done:
+			return
+		}
+	}
+
+	// Search for new files
+	for _, fileInfo := range files {
+		filePath := filepath.Join(dirPath, fileInfo.Name())
+		err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
+
+		if err != nil {
+			return
+		}
+	}
+}
+
+// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
+func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
+	w.mu.Lock()
+	_, doesExist := w.fileExists[filePath]
+	w.mu.Unlock()
+	if !doesExist {
+		// Send create event
+		select {
+		case w.Events <- newCreateEvent(filePath):
+		case <-w.done:
+			return
+		}
+	}
+
+	// like watchDirectoryFiles (but without doing another ReadDir)
+	filePath, err = w.internalWatch(filePath, fileInfo)
+	if err != nil {
+		return err
+	}
+
+	w.mu.Lock()
+	w.fileExists[filePath] = true
+	w.mu.Unlock()
+
+	return nil
+}
+
+func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
+	if fileInfo.IsDir() {
+		// mimic Linux providing delete events for subdirectories
+		// but preserve the flags used if currently watching subdirectory
+		w.mu.Lock()
+		flags := w.dirFlags[name]
+		w.mu.Unlock()
+
+		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
+		return w.addWatch(name, flags)
+	}
+
+	// watch file to mimic Linux inotify
+	return w.addWatch(name, noteAllEvents)
+}
+
+// kqueue creates a new kernel event queue and returns a descriptor.
+func kqueue() (kq int, err error) {
+	kq, err = unix.Kqueue()
+	if kq == -1 {
+		return kq, err
+	}
+	return kq, nil
+}
+
+// register events with the queue
+func register(kq int, fds []int, flags int, fflags uint32) error {
+	changes := make([]unix.Kevent_t, len(fds))
+
+	for i, fd := range fds {
+		// SetKevent converts int to the platform-specific types:
+		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
+		changes[i].Fflags = fflags
+	}
+
+	// register the events
+	success, err := unix.Kevent(kq, changes, nil, nil)
+	if success == -1 {
+		return err
+	}
+	return nil
+}
+
+// read retrieves pending events, or waits until an event occurs.
+// A timeout of nil blocks indefinitely, while 0 polls the queue.
+func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
+	n, err := unix.Kevent(kq, nil, events, timeout)
+	if err != nil {
+		return nil, err
+	}
+	return events[0:n], nil
+}
+
+// durationToTimespec prepares a timeout value
+func durationToTimespec(d time.Duration) unix.Timespec {
+	return unix.NsecToTimespec(d.Nanoseconds())
+}

+ 11 - 0
vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go

@@ -0,0 +1,11 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd openbsd netbsd dragonfly
+
+package fsnotify
+
+import "golang.org/x/sys/unix"
+
+const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC

+ 12 - 0
vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go

@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin
+
+package fsnotify
+
+import "golang.org/x/sys/unix"
+
+// note: this constant is not defined on BSD
+const openMode = unix.O_EVTONLY | unix.O_CLOEXEC

+ 561 - 0
vendor/github.com/fsnotify/fsnotify/windows.go

@@ -0,0 +1,561 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+package fsnotify
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"sync"
+	"syscall"
+	"unsafe"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+	Events   chan Event
+	Errors   chan error
+	isClosed bool           // Set to true when Close() is first called
+	mu       sync.Mutex     // Map access
+	port     syscall.Handle // Handle to completion port
+	watches  watchMap       // Map of watches (key: i-number)
+	input    chan *input    // Inputs to the reader are sent on this channel
+	quit     chan chan<- error
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+	port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
+	if e != nil {
+		return nil, os.NewSyscallError("CreateIoCompletionPort", e)
+	}
+	w := &Watcher{
+		port:    port,
+		watches: make(watchMap),
+		input:   make(chan *input, 1),
+		Events:  make(chan Event, 50),
+		Errors:  make(chan error),
+		quit:    make(chan chan<- error, 1),
+	}
+	go w.readEvents()
+	return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+	if w.isClosed {
+		return nil
+	}
+	w.isClosed = true
+
+	// Send "quit" message to the reader goroutine
+	ch := make(chan error)
+	w.quit <- ch
+	if err := w.wakeupReader(); err != nil {
+		return err
+	}
+	return <-ch
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+	if w.isClosed {
+		return errors.New("watcher already closed")
+	}
+	in := &input{
+		op:    opAddWatch,
+		path:  filepath.Clean(name),
+		flags: sysFSALLEVENTS,
+		reply: make(chan error),
+	}
+	w.input <- in
+	if err := w.wakeupReader(); err != nil {
+		return err
+	}
+	return <-in.reply
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+	in := &input{
+		op:    opRemoveWatch,
+		path:  filepath.Clean(name),
+		reply: make(chan error),
+	}
+	w.input <- in
+	if err := w.wakeupReader(); err != nil {
+		return err
+	}
+	return <-in.reply
+}
+
+const (
+	// Options for AddWatch
+	sysFSONESHOT = 0x80000000
+	sysFSONLYDIR = 0x1000000
+
+	// Events
+	sysFSACCESS     = 0x1
+	sysFSALLEVENTS  = 0xfff
+	sysFSATTRIB     = 0x4
+	sysFSCLOSE      = 0x18
+	sysFSCREATE     = 0x100
+	sysFSDELETE     = 0x200
+	sysFSDELETESELF = 0x400
+	sysFSMODIFY     = 0x2
+	sysFSMOVE       = 0xc0
+	sysFSMOVEDFROM  = 0x40
+	sysFSMOVEDTO    = 0x80
+	sysFSMOVESELF   = 0x800
+
+	// Special events
+	sysFSIGNORED   = 0x8000
+	sysFSQOVERFLOW = 0x4000
+)
+
+func newEvent(name string, mask uint32) Event {
+	e := Event{Name: name}
+	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
+		e.Op |= Create
+	}
+	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
+		e.Op |= Remove
+	}
+	if mask&sysFSMODIFY == sysFSMODIFY {
+		e.Op |= Write
+	}
+	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
+		e.Op |= Rename
+	}
+	if mask&sysFSATTRIB == sysFSATTRIB {
+		e.Op |= Chmod
+	}
+	return e
+}
+
+const (
+	opAddWatch = iota
+	opRemoveWatch
+)
+
+const (
+	provisional uint64 = 1 << (32 + iota)
+)
+
+type input struct {
+	op    int
+	path  string
+	flags uint32
+	reply chan error
+}
+
+type inode struct {
+	handle syscall.Handle
+	volume uint32
+	index  uint64
+}
+
+type watch struct {
+	ov     syscall.Overlapped
+	ino    *inode            // i-number
+	path   string            // Directory path
+	mask   uint64            // Directory itself is being watched with these notify flags
+	names  map[string]uint64 // Map of names being watched and their notify flags
+	rename string            // Remembers the old name while renaming a file
+	buf    [4096]byte
+}
+
+type indexMap map[uint64]*watch
+type watchMap map[uint32]indexMap
+
+func (w *Watcher) wakeupReader() error {
+	e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
+	if e != nil {
+		return os.NewSyscallError("PostQueuedCompletionStatus", e)
+	}
+	return nil
+}
+
+func getDir(pathname string) (dir string, err error) {
+	attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
+	if e != nil {
+		return "", os.NewSyscallError("GetFileAttributes", e)
+	}
+	if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
+		dir = pathname
+	} else {
+		dir, _ = filepath.Split(pathname)
+		dir = filepath.Clean(dir)
+	}
+	return
+}
+
+func getIno(path string) (ino *inode, err error) {
+	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
+		syscall.FILE_LIST_DIRECTORY,
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
+		nil, syscall.OPEN_EXISTING,
+		syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
+	if e != nil {
+		return nil, os.NewSyscallError("CreateFile", e)
+	}
+	var fi syscall.ByHandleFileInformation
+	if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
+		syscall.CloseHandle(h)
+		return nil, os.NewSyscallError("GetFileInformationByHandle", e)
+	}
+	ino = &inode{
+		handle: h,
+		volume: fi.VolumeSerialNumber,
+		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
+	}
+	return ino, nil
+}
+
+// Must run within the I/O thread.
+func (m watchMap) get(ino *inode) *watch {
+	if i := m[ino.volume]; i != nil {
+		return i[ino.index]
+	}
+	return nil
+}
+
+// Must run within the I/O thread.
+func (m watchMap) set(ino *inode, watch *watch) {
+	i := m[ino.volume]
+	if i == nil {
+		i = make(indexMap)
+		m[ino.volume] = i
+	}
+	i[ino.index] = watch
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) addWatch(pathname string, flags uint64) error {
+	dir, err := getDir(pathname)
+	if err != nil {
+		return err
+	}
+	if flags&sysFSONLYDIR != 0 && pathname != dir {
+		return nil
+	}
+	ino, err := getIno(dir)
+	if err != nil {
+		return err
+	}
+	w.mu.Lock()
+	watchEntry := w.watches.get(ino)
+	w.mu.Unlock()
+	if watchEntry == nil {
+		if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
+			syscall.CloseHandle(ino.handle)
+			return os.NewSyscallError("CreateIoCompletionPort", e)
+		}
+		watchEntry = &watch{
+			ino:   ino,
+			path:  dir,
+			names: make(map[string]uint64),
+		}
+		w.mu.Lock()
+		w.watches.set(ino, watchEntry)
+		w.mu.Unlock()
+		flags |= provisional
+	} else {
+		syscall.CloseHandle(ino.handle)
+	}
+	if pathname == dir {
+		watchEntry.mask |= flags
+	} else {
+		watchEntry.names[filepath.Base(pathname)] |= flags
+	}
+	if err = w.startRead(watchEntry); err != nil {
+		return err
+	}
+	if pathname == dir {
+		watchEntry.mask &= ^provisional
+	} else {
+		watchEntry.names[filepath.Base(pathname)] &= ^provisional
+	}
+	return nil
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) remWatch(pathname string) error {
+	dir, err := getDir(pathname)
+	if err != nil {
+		return err
+	}
+	ino, err := getIno(dir)
+	if err != nil {
+		return err
+	}
+	w.mu.Lock()
+	watch := w.watches.get(ino)
+	w.mu.Unlock()
+	if watch == nil {
+		return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
+	}
+	if pathname == dir {
+		w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
+		watch.mask = 0
+	} else {
+		name := filepath.Base(pathname)
+		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
+		delete(watch.names, name)
+	}
+	return w.startRead(watch)
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) deleteWatch(watch *watch) {
+	for name, mask := range watch.names {
+		if mask&provisional == 0 {
+			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
+		}
+		delete(watch.names, name)
+	}
+	if watch.mask != 0 {
+		if watch.mask&provisional == 0 {
+			w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
+		}
+		watch.mask = 0
+	}
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) startRead(watch *watch) error {
+	if e := syscall.CancelIo(watch.ino.handle); e != nil {
+		w.Errors <- os.NewSyscallError("CancelIo", e)
+		w.deleteWatch(watch)
+	}
+	mask := toWindowsFlags(watch.mask)
+	for _, m := range watch.names {
+		mask |= toWindowsFlags(m)
+	}
+	if mask == 0 {
+		if e := syscall.CloseHandle(watch.ino.handle); e != nil {
+			w.Errors <- os.NewSyscallError("CloseHandle", e)
+		}
+		w.mu.Lock()
+		delete(w.watches[watch.ino.volume], watch.ino.index)
+		w.mu.Unlock()
+		return nil
+	}
+	e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
+		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
+	if e != nil {
+		err := os.NewSyscallError("ReadDirectoryChanges", e)
+		if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
+			// Watched directory was probably removed
+			if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
+				if watch.mask&sysFSONESHOT != 0 {
+					watch.mask = 0
+				}
+			}
+			err = nil
+		}
+		w.deleteWatch(watch)
+		w.startRead(watch)
+		return err
+	}
+	return nil
+}
+
+// readEvents reads from the I/O completion port, converts the
+// received events into Event objects and sends them via the Events channel.
+// Entry point to the I/O thread.
+func (w *Watcher) readEvents() {
+	var (
+		n, key uint32
+		ov     *syscall.Overlapped
+	)
+	runtime.LockOSThread()
+
+	for {
+		e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
+		watch := (*watch)(unsafe.Pointer(ov))
+
+		if watch == nil {
+			select {
+			case ch := <-w.quit:
+				w.mu.Lock()
+				var indexes []indexMap
+				for _, index := range w.watches {
+					indexes = append(indexes, index)
+				}
+				w.mu.Unlock()
+				for _, index := range indexes {
+					for _, watch := range index {
+						w.deleteWatch(watch)
+						w.startRead(watch)
+					}
+				}
+				var err error
+				if e := syscall.CloseHandle(w.port); e != nil {
+					err = os.NewSyscallError("CloseHandle", e)
+				}
+				close(w.Events)
+				close(w.Errors)
+				ch <- err
+				return
+			case in := <-w.input:
+				switch in.op {
+				case opAddWatch:
+					in.reply <- w.addWatch(in.path, uint64(in.flags))
+				case opRemoveWatch:
+					in.reply <- w.remWatch(in.path)
+				}
+			default:
+			}
+			continue
+		}
+
+		switch e {
+		case syscall.ERROR_MORE_DATA:
+			if watch == nil {
+				w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
+			} else {
+				// The i/o succeeded but the buffer is full.
+				// In theory we should be building up a full packet.
+				// In practice we can get away with just carrying on.
+				n = uint32(unsafe.Sizeof(watch.buf))
+			}
+		case syscall.ERROR_ACCESS_DENIED:
+			// Watched directory was probably removed
+			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
+			w.deleteWatch(watch)
+			w.startRead(watch)
+			continue
+		case syscall.ERROR_OPERATION_ABORTED:
+			// CancelIo was called on this handle
+			continue
+		default:
+			w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
+			continue
+		case nil:
+		}
+
+		var offset uint32
+		for {
+			if n == 0 {
+				w.Events <- newEvent("", sysFSQOVERFLOW)
+				w.Errors <- errors.New("short read in readEvents()")
+				break
+			}
+
+			// Point "raw" to the event in the buffer
+			raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
+			buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
+			name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
+			fullname := filepath.Join(watch.path, name)
+
+			var mask uint64
+			switch raw.Action {
+			case syscall.FILE_ACTION_REMOVED:
+				mask = sysFSDELETESELF
+			case syscall.FILE_ACTION_MODIFIED:
+				mask = sysFSMODIFY
+			case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+				watch.rename = name
+			case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+				if watch.names[watch.rename] != 0 {
+					watch.names[name] |= watch.names[watch.rename]
+					delete(watch.names, watch.rename)
+					mask = sysFSMOVESELF
+				}
+			}
+
+			sendNameEvent := func() {
+				if w.sendEvent(fullname, watch.names[name]&mask) {
+					if watch.names[name]&sysFSONESHOT != 0 {
+						delete(watch.names, name)
+					}
+				}
+			}
+			if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
+				sendNameEvent()
+			}
+			if raw.Action == syscall.FILE_ACTION_REMOVED {
+				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
+				delete(watch.names, name)
+			}
+			if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
+				if watch.mask&sysFSONESHOT != 0 {
+					watch.mask = 0
+				}
+			}
+			if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
+				fullname = filepath.Join(watch.path, watch.rename)
+				sendNameEvent()
+			}
+
+			// Move to the next event in the buffer
+			if raw.NextEntryOffset == 0 {
+				break
+			}
+			offset += raw.NextEntryOffset
+
+			// Error!
+			if offset >= n {
+				w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
+				break
+			}
+		}
+
+		if err := w.startRead(watch); err != nil {
+			w.Errors <- err
+		}
+	}
+}
+
+func (w *Watcher) sendEvent(name string, mask uint64) bool {
+	if mask == 0 {
+		return false
+	}
+	event := newEvent(name, uint32(mask))
+	select {
+	case ch := <-w.quit:
+		w.quit <- ch
+	case w.Events <- event:
+	}
+	return true
+}
+
+func toWindowsFlags(mask uint64) uint32 {
+	var m uint32
+	if mask&sysFSACCESS != 0 {
+		m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
+	}
+	if mask&sysFSMODIFY != 0 {
+		m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
+	}
+	if mask&sysFSATTRIB != 0 {
+		m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
+	}
+	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
+		m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
+	}
+	return m
+}
+
+func toFSnotifyFlags(action uint32) uint64 {
+	switch action {
+	case syscall.FILE_ACTION_ADDED:
+		return sysFSCREATE
+	case syscall.FILE_ACTION_REMOVED:
+		return sysFSDELETE
+	case syscall.FILE_ACTION_MODIFIED:
+		return sysFSMODIFY
+	case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+		return sysFSMOVEDFROM
+	case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+		return sysFSMOVEDTO
+	}
+	return 0
+}

+ 21 - 0
vendor/github.com/gogf/gf/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.

+ 113 - 0
vendor/github.com/gogf/gf/README.MD

@@ -0,0 +1,113 @@
+# GoFrame
+
+[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
+[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
+[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
+[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
+[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
+[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
+
+English | [简体中文](README_ZH.MD)
+
+`GF(GoFrame)` is a modular, powerful, high-performance and enterprise-class application development framework 
+of Golang. Providing a series of core components and dozens of practical modules, such as: 
+cache, logging, containers, timer, resource, validator, database orm, etc. 
+Supporting web server integrated with router, cookie, session, middleware, logger, configure, 
+template, https, hooks, rewrites and many more features. 
+
+> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
+
+# Installation
+```
+go get -u -v github.com/gogf/gf
+```
+suggested using `go.mod`:
+```
+require github.com/gogf/gf latest
+```
+
+# Limitation
+```
+golang version >= 1.11
+```
+
+# Architecture
+<div align=center>
+<img src="https://itician.org/download/attachments/1114119/arch.png"/>
+</div>
+
+# Packages
+1. **Primary Package**
+
+    The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible. 
+
+1. **Community Package**
+
+    The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies. 
+
+# Performance
+
+The `Web` component performance of `GoFrame`, please refer to third-party project: https://github.com/the-benchmarker/web-frameworks
+
+
+
+# Documentation
+
+* 中文官网: https://goframe.org
+* GoDoc API: https://godoc.org/github.com/gogf/gf
+
+
+# Discussion
+- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
+- WX Group:Add friend`389961817` in WeChat, commenting `GF`
+- Issues:https://github.com/gogf/gf/issues
+
+> It's recommended learning `GoFrame` through its awesome source codes and API reference.
+
+# License
+
+`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
+
+# Part Of Users
+
+- [Tencent](https://www.tencent.com/)
+- [ZTE](https://www.zte.com.cn/china/)
+- [Ant Financial Services](https://www.antfin.com/)
+- [MedLinker](https://www.medlinker.com/)
+- [KuCoin](https://www.kucoin.io/)
+- [LeYouJia](https://www.leyoujia.com/)
+- [IGG](https://igg.com)
+- [XiMaLaYa](https://www.ximalaya.com)
+- [ZYBang](https://www.zybang.com/)
+
+> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://itician.org/pages/viewpage.action?pageId=1114415).
+
+
+# Contributors
+This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
+<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
+
+
+# Donators
+
+If you love `GF`, why not [buy developer a cup of coffee](https://itician.org/pages/viewpage.action?pageId=1115633)?
+
+# Sponsors
+We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
+
+
+
+# Thanks
+<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/jetbrains.png?version=1&modificationDate=1608649325806&api=v2" height="120" alt="JetBrains"/></a>
+<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/u%3D605052180%2C3099422872%26fm%3D11%26gp%3D0.jpg?version=1&modificationDate=1608649343601&api=v2" height="120" alt="Atlassian"/></a>
+
+
+
+
+
+
+
+
+
+
+

+ 117 - 0
vendor/github.com/gogf/gf/README_ZH.MD

@@ -0,0 +1,117 @@
+# GoFrame
+[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) 
+[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) 
+[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
+[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
+[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
+[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
+
+[English](README.MD) | 简体中文
+
+`GF(Go Frame)`是一款模块化、高性能、企业级的Go基础开发框架。
+实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
+如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
+配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
+并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
+支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
+
+> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
+
+# 特点
+* 模块化、松耦合设计;
+* 模块丰富、开箱即用;
+* 简便易用、易于维护;
+* 高代码质量、高单元测试覆盖率;
+* 社区活跃,大牛谦逊低调脾气好;
+* 详尽的开发文档及示例;
+* 完善的本地中文化支持;
+* 设计为团队及企业使用;
+
+# 地址
+- **主库**:https://github.com/gogf/gf 
+- **码云**:https://gitee.com/johng/gf 
+
+# 安装
+```html
+go get -u -v github.com/gogf/gf
+```
+推荐使用 `go.mod`:
+```
+require github.com/gogf/gf latest
+```
+
+# 限制
+```shell
+golang版本 >= 1.11
+```
+
+
+
+# 架构
+<div align=center>
+<img src="https://itician.org/download/attachments/1114119/arch.png"/>
+</div>
+
+# 模块
+
+1. **核心模块**
+
+    `GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
+
+1. **社区模块**
+
+    社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
+
+
+# 性能
+大家较为感兴趣的`Web`组件性能测试,请参考第三方性能测试评估:https://github.com/the-benchmarker/web-frameworks
+
+# 文档
+
+开发文档:https://goframe.org
+
+接口文档:https://godoc.org/github.com/gogf/gf
+
+# 帮助
+- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
+- WX交流群:微信添加`389961817`备注`GF`
+- 主库ISSUE:https://github.com/gogf/gf/issues
+
+> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
+
+# 协议
+
+`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
+
+# 用户
+
+- [腾讯科技](https://www.tencent.com/)
+- [中兴科技](https://www.zte.com.cn/china/)
+- [蚂蚁金服](https://www.antfin.com/)
+- [医联科技](https://www.medlinker.com/)
+- [库币科技](https://www.kucoin.io/)
+- [乐有家](https://www.leyoujia.com/)
+- [IGG](https://igg.com)
+- [喜马拉雅](https://www.ximalaya.com)
+- [作业帮](https://www.zybang.com/)
+
+> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://itician.org/pages/viewpage.action?pageId=1114415) 留言。
+
+# 贡献
+
+感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
+<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
+
+
+# 捐赠
+
+如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://itician.org/pages/viewpage.action?pageId=1115633) 吧!
+请在捐赠时备注您的`github`/`gitee`账号名称。
+
+# 赞助
+
+赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
+
+# 感谢
+<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/jetbrains.png?version=1&modificationDate=1608649325806&api=v2" height="120" alt="JetBrains"/></a>
+<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/u%3D605052180%2C3099422872%26fm%3D11%26gp%3D0.jpg?version=1&modificationDate=1608649343601&api=v2" height="120" alt="Atlassian"/></a>

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

@@ -0,0 +1,8 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
+package garray

+ 74 - 0
vendor/github.com/gogf/gf/container/garray/garray_func.go

@@ -0,0 +1,74 @@
+// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import "strings"
+
+// apiInterfaces is used for type assert api for Interfaces.
+type apiInterfaces interface {
+	Interfaces() []interface{}
+}
+
+// defaultComparatorInt for int comparison.
+func defaultComparatorInt(a, b int) int {
+	if a < b {
+		return -1
+	}
+	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)
+}

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

@@ -0,0 +1,800 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/internal/empty"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/text/gstr"
+	"math"
+	"sort"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// Array is a golang array with rich features.
+// It contains a concurrent-safe/unsafe switch, which should be set
+// when its initialization and cannot be changed then.
+type Array struct {
+	mu    rwmutex.RWMutex
+	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...)
+}
+
+// See New.
+func NewArray(safe ...bool) *Array {
+	return NewArraySize(0, 0, safe...)
+}
+
+// NewArraySize create and returns an array with given size and cap.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewArraySize(size int, cap int, safe ...bool) *Array {
+	return &Array{
+		mu:    rwmutex.Create(safe...),
+		array: make([]interface{}, size, cap),
+	}
+}
+
+// NewArrayRange creates and returns a array by a range from <start> to <end>
+// with step value <step>.
+func NewArrayRange(start, end, step int, safe ...bool) *Array {
+	if step == 0 {
+		panic(fmt.Sprintf(`invalid step value: %d`, step))
+	}
+	slice := make([]interface{}, (end-start+1)/step)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice[index] = i
+		index++
+	}
+	return NewArrayFrom(slice, safe...)
+}
+
+// See NewArrayFrom.
+func NewFrom(array []interface{}, safe ...bool) *Array {
+	return NewArrayFrom(array, safe...)
+}
+
+// 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,
+	}
+}
+
+// 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 errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	a.array[index] = value
+	return nil
+}
+
+// SetArray sets the underlying slice array with the given <array>.
+func (a *Array) SetArray(array []interface{}) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	a.array = array
+	return a
+}
+
+// Replace replaces the array items by given <array> from the beginning of array.
+func (a *Array) Replace(array []interface{}) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	max := len(array)
+	if max > len(a.array) {
+		max = len(a.array)
+	}
+	for i := 0; i < max; i++ {
+		a.array[i] = array[i]
+	}
+	return a
+}
+
+// Sum returns the sum of values in an array.
+func (a *Array) Sum() (sum int) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		sum += gconv.Int(v)
+	}
+	return
+}
+
+// SortFunc sorts the array by custom function <less>.
+func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	sort.Slice(a.array, func(i, j int) bool {
+		return less(a.array[i], a.array[j])
+	})
+	return a
+}
+
+// InsertBefore inserts the <value> to the front of <index>.
+func (a *Array) InsertBefore(index int, value interface{}) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]interface{}{}, a.array[index:]...)
+	a.array = append(a.array[0:index], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the <value> to the back of <index>.
+func (a *Array) InsertAfter(index int, value interface{}) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]interface{}{}, a.array[index+1:]...)
+	a.array = append(a.array[0:index+1], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *Array) Remove(index int) (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return nil, false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *Array) RemoveValue(value interface{}) bool {
+	if i := a.Search(value); i != -1 {
+		a.Remove(i)
+		return true
+	}
+	return false
+}
+
+// PushLeft pushes one or multiple items to the beginning of array.
+func (a *Array) PushLeft(value ...interface{}) *Array {
+	a.mu.Lock()
+	a.array = append(value, a.array...)
+	a.mu.Unlock()
+	return a
+}
+
+// PushRight pushes one or multiple items to the end of array.
+// It equals to Append.
+func (a *Array) PushRight(value ...interface{}) *Array {
+	a.mu.Lock()
+	a.array = append(a.array, value...)
+	a.mu.Unlock()
+	return a
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *Array) PopRand() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+func (a *Array) PopRands(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]interface{}, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *Array) PopLeft() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return nil, false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *Array) PopRight() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return nil, false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+func (a *Array) PopLefts(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+func (a *Array) PopRights(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *Array) Range(start int, end ...int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]interface{})(nil)
+	if a.mu.IsSafe() {
+		array = make([]interface{}, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *Array) SubSlice(offset int, length ...int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]interface{}, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// See PushRight.
+func (a *Array) Append(value ...interface{}) *Array {
+	a.PushRight(value...)
+	return a
+}
+
+// Len returns the length of array.
+func (a *Array) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *Array) Slice() []interface{} {
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array := make([]interface{}, len(a.array))
+		copy(array, a.array)
+		return array
+	} else {
+		return a.array
+	}
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *Array) Interfaces() []interface{} {
+	return a.Slice()
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *Array) Clone() (newArray *Array) {
+	a.mu.RLock()
+	array := make([]interface{}, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewArrayFrom(array, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *Array) Clear() *Array {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]interface{}, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// Contains checks whether a value exists in the array.
+func (a *Array) Contains(value interface{}) bool {
+	return a.Search(value) != -1
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *Array) Search(value interface{}) int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return -1
+	}
+	result := -1
+	for index, v := range a.array {
+		if v == value {
+			result = index
+			break
+		}
+	}
+	return result
+}
+
+// Unique uniques the array, clear repeated items.
+// Example: [1,1,2,3,2] -> [1,2,3]
+func (a *Array) Unique() *Array {
+	a.mu.Lock()
+	for i := 0; i < len(a.array)-1; i++ {
+		for j := i + 1; j < len(a.array); {
+			if a.array[i] == a.array[j] {
+				a.array = append(a.array[:j], a.array[j+1:]...)
+			} else {
+				j++
+			}
+		}
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *Array) LockFunc(f func(array []interface{})) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *Array) RLockFunc(f func(array []interface{})) *Array {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *Array) Merge(array interface{}) *Array {
+	return a.Append(gconv.Interfaces(array)...)
+}
+
+// Fill fills an array with num entries of the value <value>,
+// keys starting at the <startIndex> parameter.
+func (a *Array) Fill(startIndex int, num int, value interface{}) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if startIndex < 0 || startIndex > len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
+	}
+	for i := startIndex; i < startIndex+num; i++ {
+		if i > len(a.array)-1 {
+			a.array = append(a.array, value)
+		} else {
+			a.array[i] = value
+		}
+	}
+	return nil
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *Array) Chunk(size int) [][]interface{} {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]interface{}
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Pad pads array to the specified length with <value>.
+// If size is positive then the array is padded on the right, or negative on the left.
+// If the absolute value of <size> is less than or equal to the length of the array
+// then no padding takes place.
+func (a *Array) Pad(size int, val interface{}) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+		return a
+	}
+	n := size
+	if size < 0 {
+		n = -size
+	}
+	n -= len(a.array)
+	tmp := make([]interface{}, n)
+	for i := 0; i < n; i++ {
+		tmp[i] = val
+	}
+	if size > 0 {
+		a.array = append(a.array, tmp...)
+	} else {
+		a.array = append(tmp, a.array...)
+	}
+	return a
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *Array) Rand() (value interface{}, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return nil, false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *Array) Rands(size int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]interface{}, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Shuffle randomly shuffles the array.
+func (a *Array) Shuffle() *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range grand.Perm(len(a.array)) {
+		a.array[i], a.array[v] = a.array[v], a.array[i]
+	}
+	return a
+}
+
+// Reverse makes array with elements in reverse order.
+func (a *Array) Reverse() *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
+		a.array[i], a.array[j] = a.array[j], a.array[i]
+	}
+	return a
+}
+
+// Join joins array elements with a string <glue>.
+func (a *Array) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(gconv.String(v))
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *Array) CountValues() map[interface{}]int {
+	m := make(map[interface{}]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *Array) Iterator(f func(k int, v interface{}) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *Array) String() string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	s := ""
+	for k, v := range a.array {
+		s = gconv.String(v)
+		if gstr.IsNumeric(s) {
+			buffer.WriteString(s)
+		} else {
+			buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
+		}
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a Array) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (a *Array) UnmarshalJSON(b []byte) error {
+	if a.array == nil {
+		a.array = make([]interface{}, 0)
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceAny(value)
+	}
+	return nil
+}
+
+// FilterNil removes all nil value of the array.
+func (a *Array) FilterNil() *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if empty.IsNil(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// FilterEmpty removes all empty value of the array.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (a *Array) FilterEmpty() *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if empty.IsEmpty(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *Array) IsEmpty() bool {
+	return a.Len() == 0
+}

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

@@ -0,0 +1,784 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/internal/json"
+	"math"
+	"sort"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// IntArray is a golang int array with rich features.
+// It contains a concurrent-safe/unsafe switch, which should be set
+// when its initialization and cannot be changed then.
+type IntArray struct {
+	mu    rwmutex.RWMutex
+	array []int
+}
+
+// NewIntArray creates and returns an empty array.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewIntArray(safe ...bool) *IntArray {
+	return NewIntArraySize(0, 0, safe...)
+}
+
+// NewIntArraySize create and returns an array with given size and cap.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
+	return &IntArray{
+		mu:    rwmutex.Create(safe...),
+		array: make([]int, size, cap),
+	}
+}
+
+// NewIntArrayRange creates and returns a array by a range from <start> to <end>
+// with step value <step>.
+func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
+	if step == 0 {
+		panic(fmt.Sprintf(`invalid step value: %d`, step))
+	}
+	slice := make([]int, (end-start+1)/step)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice[index] = i
+		index++
+	}
+	return NewIntArrayFrom(slice, safe...)
+}
+
+// NewIntArrayFrom creates and returns an array with given slice <array>.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
+	return &IntArray{
+		mu:    rwmutex.Create(safe...),
+		array: array,
+	}
+}
+
+// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
+	newArray := make([]int, len(array))
+	copy(newArray, array)
+	return &IntArray{
+		mu:    rwmutex.Create(safe...),
+		array: newArray,
+	}
+}
+
+// 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 errors.New(fmt.Sprintf("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 {
+			if a.array[i] < a.array[j] {
+				return false
+			}
+			return true
+		})
+	} else {
+		sort.Ints(a.array)
+	}
+	return a
+}
+
+// SortFunc sorts the array by custom function <less>.
+func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	sort.Slice(a.array, func(i, j int) bool {
+		return less(a.array[i], a.array[j])
+	})
+	return a
+}
+
+// InsertBefore inserts the <value> to the front of <index>.
+func (a *IntArray) InsertBefore(index int, value int) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]int{}, a.array[index:]...)
+	a.array = append(a.array[0:index], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the <value> to the back of <index>.
+func (a *IntArray) InsertAfter(index int, value int) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]int{}, a.array[index+1:]...)
+	a.array = append(a.array[0:index+1], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *IntArray) Remove(index int) (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return 0, false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *IntArray) RemoveValue(value int) bool {
+	if i := a.Search(value); i != -1 {
+		_, found := a.Remove(i)
+		return found
+	}
+	return false
+}
+
+// PushLeft pushes one or multiple items to the beginning of array.
+func (a *IntArray) PushLeft(value ...int) *IntArray {
+	a.mu.Lock()
+	a.array = append(value, a.array...)
+	a.mu.Unlock()
+	return a
+}
+
+// PushRight pushes one or multiple items to the end of array.
+// It equals to Append.
+func (a *IntArray) PushRight(value ...int) *IntArray {
+	a.mu.Lock()
+	a.array = append(a.array, value...)
+	a.mu.Unlock()
+	return a
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *IntArray) PopLeft() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return 0, false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *IntArray) PopRight() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return 0, false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *IntArray) PopRand() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *IntArray) PopRands(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]int, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *IntArray) PopLefts(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *IntArray) PopRights(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *IntArray) Range(start int, end ...int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]int)(nil)
+	if a.mu.IsSafe() {
+		array = make([]int, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *IntArray) SubSlice(offset int, length ...int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]int, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// See PushRight.
+func (a *IntArray) Append(value ...int) *IntArray {
+	a.mu.Lock()
+	a.array = append(a.array, value...)
+	a.mu.Unlock()
+	return a
+}
+
+// Len returns the length of array.
+func (a *IntArray) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *IntArray) Slice() []int {
+	array := ([]int)(nil)
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array = make([]int, len(a.array))
+		copy(array, a.array)
+	} else {
+		array = a.array
+	}
+	return array
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *IntArray) Interfaces() []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	array := make([]interface{}, len(a.array))
+	for k, v := range a.array {
+		array[k] = v
+	}
+	return array
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *IntArray) Clone() (newArray *IntArray) {
+	a.mu.RLock()
+	array := make([]int, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewIntArrayFrom(array, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *IntArray) Clear() *IntArray {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]int, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// Contains checks whether a value exists in the array.
+func (a *IntArray) Contains(value int) bool {
+	return a.Search(value) != -1
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *IntArray) Search(value int) int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return -1
+	}
+	result := -1
+	for index, v := range a.array {
+		if v == value {
+			result = index
+			break
+		}
+	}
+	return result
+}
+
+// Unique uniques the array, clear repeated items.
+// Example: [1,1,2,3,2] -> [1,2,3]
+func (a *IntArray) Unique() *IntArray {
+	a.mu.Lock()
+	for i := 0; i < len(a.array)-1; i++ {
+		for j := i + 1; j < len(a.array); {
+			if a.array[i] == a.array[j] {
+				a.array = append(a.array[:j], a.array[j+1:]...)
+			} else {
+				j++
+			}
+		}
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *IntArray) Merge(array interface{}) *IntArray {
+	return a.Append(gconv.Ints(array)...)
+}
+
+// Fill fills an array with num entries of the value <value>,
+// keys starting at the <startIndex> parameter.
+func (a *IntArray) Fill(startIndex int, num int, value int) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if startIndex < 0 || startIndex > len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
+	}
+	for i := startIndex; i < startIndex+num; i++ {
+		if i > len(a.array)-1 {
+			a.array = append(a.array, value)
+		} else {
+			a.array[i] = value
+		}
+	}
+	return nil
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *IntArray) Chunk(size int) [][]int {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]int
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Pad pads array to the specified length with <value>.
+// If size is positive then the array is padded on the right, or negative on the left.
+// If the absolute value of <size> is less than or equal to the length of the array
+// then no padding takes place.
+func (a *IntArray) Pad(size int, value int) *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+		return a
+	}
+	n := size
+	if size < 0 {
+		n = -size
+	}
+	n -= len(a.array)
+	tmp := make([]int, n)
+	for i := 0; i < n; i++ {
+		tmp[i] = value
+	}
+	if size > 0 {
+		a.array = append(a.array, tmp...)
+	} else {
+		a.array = append(tmp, a.array...)
+	}
+	return a
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *IntArray) Rand() (value int, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return 0, false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *IntArray) Rands(size int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]int, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Shuffle randomly shuffles the array.
+func (a *IntArray) Shuffle() *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range grand.Perm(len(a.array)) {
+		a.array[i], a.array[v] = a.array[v], a.array[i]
+	}
+	return a
+}
+
+// Reverse makes array with elements in reverse order.
+func (a *IntArray) Reverse() *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
+		a.array[i], a.array[j] = a.array[j], a.array[i]
+	}
+	return a
+}
+
+// Join joins array elements with a string <glue>.
+func (a *IntArray) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(gconv.String(v))
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *IntArray) CountValues() map[int]int {
+	m := make(map[int]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *IntArray) Iterator(f func(k int, v int) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *IntArray) String() string {
+	return "[" + a.Join(",") + "]"
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a IntArray) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (a *IntArray) UnmarshalJSON(b []byte) error {
+	if a.array == nil {
+		a.array = make([]int, 0)
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceInt(value)
+	}
+	return nil
+}
+
+// FilterEmpty removes all zero value of the array.
+func (a *IntArray) FilterEmpty() *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if a.array[i] == 0 {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *IntArray) Walk(f func(value int) int) *IntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *IntArray) IsEmpty() bool {
+	return a.Len() == 0
+}

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

@@ -0,0 +1,799 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/text/gstr"
+	"math"
+	"sort"
+	"strings"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// StrArray is a golang string array with rich features.
+// It contains a concurrent-safe/unsafe switch, which should be set
+// when its initialization and cannot be changed then.
+type StrArray struct {
+	mu    rwmutex.RWMutex
+	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,
+	}
+}
+
+// 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 errors.New(fmt.Sprintf("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 {
+			if strings.Compare(a.array[i], a.array[j]) < 0 {
+				return false
+			}
+			return true
+		})
+	} else {
+		sort.Strings(a.array)
+	}
+	return a
+}
+
+// SortFunc sorts the array by custom function <less>.
+func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	sort.Slice(a.array, func(i, j int) bool {
+		return less(a.array[i], a.array[j])
+	})
+	return a
+}
+
+// InsertBefore inserts the <value> to the front of <index>.
+func (a *StrArray) InsertBefore(index int, value string) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]string{}, a.array[index:]...)
+	a.array = append(a.array[0:index], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// InsertAfter inserts the <value> to the back of <index>.
+func (a *StrArray) InsertAfter(index int, value string) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if index < 0 || index >= len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
+	}
+	rear := append([]string{}, a.array[index+1:]...)
+	a.array = append(a.array[0:index+1], value)
+	a.array = append(a.array, rear...)
+	return nil
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *StrArray) Remove(index int) (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return "", false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *StrArray) RemoveValue(value string) bool {
+	if i := a.Search(value); i != -1 {
+		_, found := a.Remove(i)
+		return found
+	}
+	return false
+}
+
+// PushLeft pushes one or multiple items to the beginning of array.
+func (a *StrArray) PushLeft(value ...string) *StrArray {
+	a.mu.Lock()
+	a.array = append(value, a.array...)
+	a.mu.Unlock()
+	return a
+}
+
+// PushRight pushes one or multiple items to the end of array.
+// It equals to Append.
+func (a *StrArray) PushRight(value ...string) *StrArray {
+	a.mu.Lock()
+	a.array = append(a.array, value...)
+	a.mu.Unlock()
+	return a
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *StrArray) PopLeft() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return "", false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *StrArray) PopRight() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return "", false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *StrArray) PopRand() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *StrArray) PopRands(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]string, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *StrArray) PopLefts(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *StrArray) PopRights(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *StrArray) Range(start int, end ...int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]string)(nil)
+	if a.mu.IsSafe() {
+		array = make([]string, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *StrArray) SubSlice(offset int, length ...int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]string, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// See PushRight.
+func (a *StrArray) Append(value ...string) *StrArray {
+	a.mu.Lock()
+	a.array = append(a.array, value...)
+	a.mu.Unlock()
+	return a
+}
+
+// Len returns the length of array.
+func (a *StrArray) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *StrArray) Slice() []string {
+	array := ([]string)(nil)
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array = make([]string, len(a.array))
+		copy(array, a.array)
+	} else {
+		array = a.array
+	}
+	return array
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *StrArray) Interfaces() []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	array := make([]interface{}, len(a.array))
+	for k, v := range a.array {
+		array[k] = v
+	}
+	return array
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *StrArray) Clone() (newArray *StrArray) {
+	a.mu.RLock()
+	array := make([]string, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewStrArrayFrom(array, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *StrArray) Clear() *StrArray {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]string, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// Contains checks whether a value exists in the array.
+func (a *StrArray) Contains(value string) bool {
+	return a.Search(value) != -1
+}
+
+// ContainsI checks whether a value exists in the array with case-insensitively.
+// Note that it internally iterates the whole array to do the comparison with case-insensitively.
+func (a *StrArray) ContainsI(value string) bool {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return false
+	}
+	for _, v := range a.array {
+		if strings.EqualFold(v, value) {
+			return true
+		}
+	}
+	return false
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *StrArray) Search(value string) int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return -1
+	}
+	result := -1
+	for index, v := range a.array {
+		if strings.Compare(v, value) == 0 {
+			result = index
+			break
+		}
+	}
+	return result
+}
+
+// Unique uniques the array, clear repeated items.
+// Example: [1,1,2,3,2] -> [1,2,3]
+func (a *StrArray) Unique() *StrArray {
+	a.mu.Lock()
+	for i := 0; i < len(a.array)-1; i++ {
+		for j := i + 1; j < len(a.array); {
+			if a.array[i] == a.array[j] {
+				a.array = append(a.array[:j], a.array[j+1:]...)
+			} else {
+				j++
+			}
+		}
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *StrArray) LockFunc(f func(array []string)) *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *StrArray) Merge(array interface{}) *StrArray {
+	return a.Append(gconv.Strings(array)...)
+}
+
+// Fill fills an array with num entries of the value <value>,
+// keys starting at the <startIndex> parameter.
+func (a *StrArray) Fill(startIndex int, num int, value string) error {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if startIndex < 0 || startIndex > len(a.array) {
+		return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
+	}
+	for i := startIndex; i < startIndex+num; i++ {
+		if i > len(a.array)-1 {
+			a.array = append(a.array, value)
+		} else {
+			a.array[i] = value
+		}
+	}
+	return nil
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *StrArray) Chunk(size int) [][]string {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]string
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Pad pads array to the specified length with <value>.
+// If size is positive then the array is padded on the right, or negative on the left.
+// If the absolute value of <size> is less than or equal to the length of the array
+// then no padding takes place.
+func (a *StrArray) Pad(size int, value string) *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
+		return a
+	}
+	n := size
+	if size < 0 {
+		n = -size
+	}
+	n -= len(a.array)
+	tmp := make([]string, n)
+	for i := 0; i < n; i++ {
+		tmp[i] = value
+	}
+	if size > 0 {
+		a.array = append(a.array, tmp...)
+	} else {
+		a.array = append(tmp, a.array...)
+	}
+	return a
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *StrArray) Rand() (value string, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return "", false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *StrArray) Rands(size int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]string, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Shuffle randomly shuffles the array.
+func (a *StrArray) Shuffle() *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range grand.Perm(len(a.array)) {
+		a.array[i], a.array[v] = a.array[v], a.array[i]
+	}
+	return a
+}
+
+// Reverse makes array with elements in reverse order.
+func (a *StrArray) Reverse() *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
+		a.array[i], a.array[j] = a.array[j], a.array[i]
+	}
+	return a
+}
+
+// Join joins array elements with a string <glue>.
+func (a *StrArray) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(v)
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *StrArray) CountValues() map[string]int {
+	m := make(map[string]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *StrArray) Iterator(f func(k int, v string) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *StrArray) String() string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	for k, v := range a.array {
+		buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a StrArray) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (a *StrArray) UnmarshalJSON(b []byte) error {
+	if a.array == nil {
+		a.array = make([]string, 0)
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceStr(value)
+	}
+	return nil
+}
+
+// FilterEmpty removes all empty string value of the array.
+func (a *StrArray) FilterEmpty() *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if a.array[i] == "" {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *StrArray) Walk(f func(value string) string) *StrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *StrArray) IsEmpty() bool {
+	return a.Len() == 0
+}

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

@@ -0,0 +1,790 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"fmt"
+	"github.com/gogf/gf/internal/empty"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gutil"
+	"math"
+	"sort"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// SortedArray is a golang sorted array with rich features.
+// It is using increasing order in default, which can be changed by
+// setting it a custom comparator.
+// It contains a concurrent-safe/unsafe switch, which should be set
+// when its initialization and cannot be changed then.
+type SortedArray struct {
+	mu         rwmutex.RWMutex
+	array      []interface{}
+	unique     bool                       // Whether enable unique feature(false)
+	comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
+}
+
+// NewSortedArray creates and returns an empty sorted array.
+// The parameter <safe> is used to specify whether using array in concurrent-safety, which is false in default.
+// The parameter <comparator> used to compare values to sort in array,
+// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
+// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
+// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
+func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	return NewSortedArraySize(0, comparator, safe...)
+}
+
+// NewSortedArraySize create and returns an sorted array with given size and cap.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	return &SortedArray{
+		mu:         rwmutex.Create(safe...),
+		array:      make([]interface{}, 0, cap),
+		comparator: comparator,
+	}
+}
+
+// NewSortedArrayRange creates and returns a array by a range from <start> to <end>
+// with step value <step>.
+func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	if step == 0 {
+		panic(fmt.Sprintf(`invalid step value: %d`, step))
+	}
+	slice := make([]interface{}, (end-start+1)/step)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice[index] = i
+		index++
+	}
+	return NewSortedArrayFrom(slice, comparator, safe...)
+}
+
+// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	a := NewSortedArraySize(0, comparator, safe...)
+	a.array = array
+	sort.Slice(a.array, func(i, j int) bool {
+		return a.getComparator()(a.array[i], a.array[j]) < 0
+	})
+	return a
+}
+
+// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
+	newArray := make([]interface{}, len(array))
+	copy(newArray, array)
+	return NewSortedArrayFrom(newArray, comparator, safe...)
+}
+
+// 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++
+		}
+		rear := append([]interface{}{}, 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 *SortedArray) Get(index int) (value interface{}, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if index < 0 || index >= len(a.array) {
+		return nil, false
+	}
+	return a.array[index], true
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return nil, false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *SortedArray) RemoveValue(value interface{}) bool {
+	if i := a.Search(value); i != -1 {
+		a.Remove(i)
+		return true
+	}
+	return false
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedArray) PopLeft() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return nil, false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedArray) PopRight() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return nil, false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedArray) PopRand() (value interface{}, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+func (a *SortedArray) PopRands(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]interface{}, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+func (a *SortedArray) PopLefts(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+func (a *SortedArray) PopRights(size int) []interface{} {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *SortedArray) Range(start int, end ...int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]interface{})(nil)
+	if a.mu.IsSafe() {
+		array = make([]interface{}, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *SortedArray) SubSlice(offset int, length ...int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]interface{}, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// Sum returns the sum of values in an array.
+func (a *SortedArray) Sum() (sum int) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		sum += gconv.Int(v)
+	}
+	return
+}
+
+// Len returns the length of array.
+func (a *SortedArray) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *SortedArray) Slice() []interface{} {
+	var array []interface{}
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array = make([]interface{}, len(a.array))
+		copy(array, a.array)
+	} else {
+		array = a.array
+	}
+	return array
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *SortedArray) Interfaces() []interface{} {
+	return a.Slice()
+}
+
+// Contains checks whether a value exists in the array.
+func (a *SortedArray) Contains(value interface{}) bool {
+	return a.Search(value) != -1
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *SortedArray) Search(value interface{}) (index int) {
+	if i, r := a.binSearch(value, true); r == 0 {
+		return i
+	}
+	return -1
+}
+
+// Binary search.
+// It returns the last compared index and the result.
+// If <result> equals to 0, it means the value at <index> is equals to <value>.
+// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
+// If <result> greater than 0, it means the value at <index> is greater than <value>.
+func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
+	if lock {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+	}
+	if len(a.array) == 0 {
+		return -1, -2
+	}
+	min := 0
+	max := len(a.array) - 1
+	mid := 0
+	cmp := -2
+	for min <= max {
+		mid = min + 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 *SortedArray) SetUnique(unique bool) *SortedArray {
+	oldUnique := a.unique
+	a.unique = unique
+	if unique && oldUnique != unique {
+		a.Unique()
+	}
+	return a
+}
+
+// Unique uniques the array, clear repeated items.
+func (a *SortedArray) Unique() *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	i := 0
+	for {
+		if i == len(a.array)-1 {
+			break
+		}
+		if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
+			a.array = append(a.array[:i+1], a.array[i+1+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *SortedArray) Clone() (newArray *SortedArray) {
+	a.mu.RLock()
+	array := make([]interface{}, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewSortedArrayFrom(array, a.comparator, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *SortedArray) Clear() *SortedArray {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]interface{}, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+
+	// Keep the array always sorted.
+	defer sort.Slice(a.array, func(i, j int) bool {
+		return a.getComparator()(a.array[i], a.array[j]) < 0
+	})
+
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *SortedArray) Merge(array interface{}) *SortedArray {
+	return a.Add(gconv.Interfaces(array)...)
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *SortedArray) Chunk(size int) [][]interface{} {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]interface{}
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *SortedArray) Rand() (value interface{}, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return nil, false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *SortedArray) Rands(size int) []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]interface{}, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Join joins array elements with a string <glue>.
+func (a *SortedArray) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(gconv.String(v))
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *SortedArray) CountValues() map[interface{}]int {
+	m := make(map[interface{}]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *SortedArray) String() string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	s := ""
+	for k, v := range a.array {
+		s = gconv.String(v)
+		if gstr.IsNumeric(s) {
+			buffer.WriteString(s)
+		} else {
+			buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
+		}
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a SortedArray) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+// Note that the comparator is set as string comparator in default.
+func (a *SortedArray) UnmarshalJSON(b []byte) error {
+	if a.comparator == nil {
+		a.array = make([]interface{}, 0)
+		a.comparator = gutil.ComparatorString
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceAny(value)
+	}
+	if a.comparator != nil && a.array != nil {
+		sort.Slice(a.array, func(i, j int) bool {
+			return a.comparator(a.array[i], a.array[j]) < 0
+		})
+	}
+	return err
+}
+
+// FilterNil removes all nil value of the array.
+func (a *SortedArray) FilterNil() *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if empty.IsNil(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	for i := len(a.array) - 1; i >= 0; {
+		if empty.IsNil(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	return a
+}
+
+// FilterEmpty removes all empty value of the array.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (a *SortedArray) FilterEmpty() *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if empty.IsEmpty(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	for i := len(a.array) - 1; i >= 0; {
+		if empty.IsEmpty(a.array[i]) {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	// Keep the array always sorted.
+	defer sort.Slice(a.array, func(i, j int) bool {
+		return a.getComparator()(a.array[i], a.array[j]) < 0
+	})
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *SortedArray) IsEmpty() bool {
+	return a.Len() == 0
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it panics.
+func (a *SortedArray) getComparator() func(a, b interface{}) int {
+	if a.comparator == nil {
+		panic("comparator is missing for sorted array")
+	}
+	return a.comparator
+}

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

@@ -0,0 +1,736 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"fmt"
+	"github.com/gogf/gf/internal/json"
+	"math"
+	"sort"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// SortedIntArray is a golang sorted int array with rich features.
+// It is using increasing order in default, which can be changed by
+// setting it a custom comparator.
+// It contains a concurrent-safe/unsafe switch, which should be set
+// when its initialization and cannot be changed then.
+type SortedIntArray struct {
+	mu         rwmutex.RWMutex
+	array      []int
+	unique     bool               // Whether enable unique feature(false)
+	comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
+}
+
+// NewSortedIntArray creates and returns an empty sorted array.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewSortedIntArray(safe ...bool) *SortedIntArray {
+	return NewSortedIntArraySize(0, safe...)
+}
+
+// NewSortedIntArrayComparator creates and returns an empty sorted array with specified comparator.
+// The parameter <safe> is used to specify whether using array in concurrent-safety which is false in default.
+func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *SortedIntArray {
+	array := NewSortedIntArray(safe...)
+	array.comparator = comparator
+	return array
+}
+
+// NewSortedIntArraySize create and returns an sorted array with given size and cap.
+// The parameter <safe> is used to specify whether using array in concurrent-safety,
+// which is false in default.
+func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
+	return &SortedIntArray{
+		mu:         rwmutex.Create(safe...),
+		array:      make([]int, 0, cap),
+		comparator: defaultComparatorInt,
+	}
+}
+
+// NewSortedIntArrayRange creates and returns a array by a range from <start> to <end>
+// with step value <step>.
+func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray {
+	if step == 0 {
+		panic(fmt.Sprintf(`invalid step value: %d`, step))
+	}
+	slice := make([]int, (end-start+1)/step)
+	index := 0
+	for i := start; i <= end; i += step {
+		slice[index] = i
+		index++
+	}
+	return NewSortedIntArrayFrom(slice, safe...)
+}
+
+// NewIntArrayFrom 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...)
+}
+
+// SetArray sets the underlying slice array with the given <array>.
+func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	a.array = array
+	quickSortInt(a.array, a.getComparator())
+	return a
+}
+
+// Sort sorts the array in increasing order.
+// The parameter <reverse> controls whether sort
+// in increasing order(default) or decreasing order.
+func (a *SortedIntArray) Sort() *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	quickSortInt(a.array, a.getComparator())
+	return a
+}
+
+// Add adds one or multiple values to sorted array, the array always keeps sorted.
+// It's alias of function Append, see Append.
+func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
+	return a.Append(values...)
+}
+
+// Append adds one or multiple values to sorted array, the array always keeps sorted.
+func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
+	if len(values) == 0 {
+		return a
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		index, cmp := a.binSearch(value, false)
+		if a.unique && cmp == 0 {
+			continue
+		}
+		if index < 0 {
+			a.array = append(a.array, value)
+			continue
+		}
+		if cmp > 0 {
+			index++
+		}
+		rear := append([]int{}, a.array[index:]...)
+		a.array = append(a.array[0:index], value)
+		a.array = append(a.array, rear...)
+	}
+	return a
+}
+
+// Get returns the value by the specified index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *SortedIntArray) Get(index int) (value int, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if index < 0 || index >= len(a.array) {
+		return 0, false
+	}
+	return a.array[index], true
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *SortedIntArray) Remove(index int) (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return 0, false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *SortedIntArray) RemoveValue(value int) bool {
+	if i := a.Search(value); i != -1 {
+		_, found := a.Remove(i)
+		return found
+	}
+	return false
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedIntArray) PopLeft() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return 0, false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedIntArray) PopRight() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return 0, false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedIntArray) PopRand() (value int, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedIntArray) PopRands(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]int, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedIntArray) PopLefts(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedIntArray) PopRights(size int) []int {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *SortedIntArray) Range(start int, end ...int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]int)(nil)
+	if a.mu.IsSafe() {
+		array = make([]int, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *SortedIntArray) SubSlice(offset int, length ...int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]int, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// Len returns the length of array.
+func (a *SortedIntArray) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Sum returns the sum of values in an array.
+func (a *SortedIntArray) Sum() (sum int) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		sum += v
+	}
+	return
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *SortedIntArray) Slice() []int {
+	array := ([]int)(nil)
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array = make([]int, len(a.array))
+		copy(array, a.array)
+	} else {
+		array = a.array
+	}
+	return array
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *SortedIntArray) Interfaces() []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	array := make([]interface{}, len(a.array))
+	for k, v := range a.array {
+		array[k] = v
+	}
+	return array
+}
+
+// Contains checks whether a value exists in the array.
+func (a *SortedIntArray) Contains(value int) bool {
+	return a.Search(value) != -1
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *SortedIntArray) Search(value int) (index int) {
+	if i, r := a.binSearch(value, true); r == 0 {
+		return i
+	}
+	return -1
+}
+
+// Binary search.
+// It returns the last compared index and the result.
+// If <result> equals to 0, it means the value at <index> is equals to <value>.
+// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
+// If <result> greater than 0, it means the value at <index> is greater than <value>.
+func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
+	if lock {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+	}
+	if len(a.array) == 0 {
+		return -1, -2
+	}
+	min := 0
+	max := len(a.array) - 1
+	mid := 0
+	cmp := -2
+	for min <= max {
+		mid = min + int((max-min)/2)
+		cmp = a.getComparator()(value, a.array[mid])
+		switch {
+		case cmp < 0:
+			max = mid - 1
+		case cmp > 0:
+			min = mid + 1
+		default:
+			return mid, cmp
+		}
+	}
+	return mid, cmp
+}
+
+// SetUnique sets unique mark to the array,
+// which means it does not contain any repeated items.
+// It also do unique check, remove all repeated items.
+func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
+	oldUnique := a.unique
+	a.unique = unique
+	if unique && oldUnique != unique {
+		a.Unique()
+	}
+	return a
+}
+
+// Unique uniques the array, clear repeated items.
+func (a *SortedIntArray) Unique() *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	i := 0
+	for {
+		if i == len(a.array)-1 {
+			break
+		}
+		if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
+			a.array = append(a.array[:i+1], a.array[i+1+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
+	a.mu.RLock()
+	array := make([]int, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewSortedIntArrayFrom(array, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *SortedIntArray) Clear() *SortedIntArray {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]int, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
+	return a.Add(gconv.Ints(array)...)
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *SortedIntArray) Chunk(size int) [][]int {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]int
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *SortedIntArray) Rand() (value int, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return 0, false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *SortedIntArray) Rands(size int) []int {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]int, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Join joins array elements with a string <glue>.
+func (a *SortedIntArray) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(gconv.String(v))
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *SortedIntArray) CountValues() map[int]int {
+	m := make(map[int]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *SortedIntArray) String() string {
+	return "[" + a.Join(",") + "]"
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a SortedIntArray) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
+	if a.comparator == nil {
+		a.array = make([]int, 0)
+		a.comparator = defaultComparatorInt
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceInt(value)
+	}
+	if a.array != nil {
+		sort.Ints(a.array)
+	}
+	return err
+}
+
+// FilterEmpty removes all zero value of the array.
+func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if a.array[i] == 0 {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	for i := len(a.array) - 1; i >= 0; {
+		if a.array[i] == 0 {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+
+	// Keep the array always sorted.
+	defer quickSortInt(a.array, a.getComparator())
+
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *SortedIntArray) IsEmpty() bool {
+	return a.Len() == 0
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it returns a default comparator.
+func (a *SortedIntArray) getComparator() func(a, b int) int {
+	if a.comparator == nil {
+		return defaultComparatorInt
+	}
+	return a.comparator
+}

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

@@ -0,0 +1,749 @@
+// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package garray
+
+import (
+	"bytes"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/text/gstr"
+	"math"
+	"sort"
+	"strings"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+)
+
+// SortedStrArray is a golang sorted string array with rich features.
+// It 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
+}
+
+// Sort sorts the array in increasing order.
+// The parameter <reverse> controls whether sort
+// in increasing order(default) or decreasing order.
+func (a *SortedStrArray) Sort() *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	quickSortStr(a.array, a.getComparator())
+	return a
+}
+
+// Add adds one or multiple values to sorted array, the array always keeps sorted.
+// It's alias of function Append, see Append.
+func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
+	return a.Append(values...)
+}
+
+// Append adds one or multiple values to sorted array, the array always keeps sorted.
+func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
+	if len(values) == 0 {
+		return a
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for _, value := range values {
+		index, cmp := a.binSearch(value, false)
+		if a.unique && cmp == 0 {
+			continue
+		}
+		if index < 0 {
+			a.array = append(a.array, value)
+			continue
+		}
+		if cmp > 0 {
+			index++
+		}
+		rear := append([]string{}, a.array[index:]...)
+		a.array = append(a.array[0:index], value)
+		a.array = append(a.array, rear...)
+	}
+	return a
+}
+
+// Get returns the value by the specified index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *SortedStrArray) Get(index int) (value string, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if index < 0 || index >= len(a.array) {
+		return "", false
+	}
+	return a.array[index], true
+}
+
+// Remove removes an item by index.
+// If the given <index> is out of range of the array, the <found> is false.
+func (a *SortedStrArray) Remove(index int) (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(index)
+}
+
+// doRemoveWithoutLock removes an item by index without lock.
+func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
+	if index < 0 || index >= len(a.array) {
+		return "", false
+	}
+	// Determine array boundaries when deleting to improve deletion efficiency.
+	if index == 0 {
+		value := a.array[0]
+		a.array = a.array[1:]
+		return value, true
+	} else if index == len(a.array)-1 {
+		value := a.array[index]
+		a.array = a.array[:index]
+		return value, true
+	}
+	// If it is a non-boundary delete,
+	// it will involve the creation of an array,
+	// then the deletion is less efficient.
+	value = a.array[index]
+	a.array = append(a.array[:index], a.array[index+1:]...)
+	return value, true
+}
+
+// RemoveValue removes an item by value.
+// It returns true if value is found in the array, or else false if not found.
+func (a *SortedStrArray) RemoveValue(value string) bool {
+	if i := a.Search(value); i != -1 {
+		a.Remove(i)
+		return true
+	}
+	return false
+}
+
+// PopLeft pops and returns an item from the beginning of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedStrArray) PopLeft() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return "", false
+	}
+	value = a.array[0]
+	a.array = a.array[1:]
+	return value, true
+}
+
+// PopRight pops and returns an item from the end of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedStrArray) PopRight() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	index := len(a.array) - 1
+	if index < 0 {
+		return "", false
+	}
+	value = a.array[index]
+	a.array = a.array[:index]
+	return value, true
+}
+
+// PopRand randomly pops and return an item out of array.
+// Note that if the array is empty, the <found> is false.
+func (a *SortedStrArray) PopRand() (value string, found bool) {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+}
+
+// PopRands randomly pops and returns <size> items out of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedStrArray) PopRands(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		size = len(a.array)
+	}
+	array := make([]string, size)
+	for i := 0; i < size; i++ {
+		array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
+	}
+	return array
+}
+
+// PopLefts pops and returns <size> items from the beginning of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedStrArray) PopLefts(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	if size >= len(a.array) {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[0:size]
+	a.array = a.array[size:]
+	return value
+}
+
+// PopRights pops and returns <size> items from the end of array.
+// If the given <size> is greater than size of the array, it returns all elements of the array.
+// Note that if given <size> <= 0 or the array is empty, it returns nil.
+func (a *SortedStrArray) PopRights(size int) []string {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	index := len(a.array) - size
+	if index <= 0 {
+		array := a.array
+		a.array = a.array[:0]
+		return array
+	}
+	value := a.array[index:]
+	a.array = a.array[:index]
+	return value
+}
+
+// Range picks and returns items by range, like array[start:end].
+// Notice, if in concurrent-safe usage, it returns a copy of slice;
+// else a pointer to the underlying data.
+//
+// If <end> is negative, then the offset will start from the end of array.
+// If <end> is omitted, then the sequence will have everything from start up
+// until the end of the array.
+func (a *SortedStrArray) Range(start int, end ...int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	offsetEnd := len(a.array)
+	if len(end) > 0 && end[0] < offsetEnd {
+		offsetEnd = end[0]
+	}
+	if start > offsetEnd {
+		return nil
+	}
+	if start < 0 {
+		start = 0
+	}
+	array := ([]string)(nil)
+	if a.mu.IsSafe() {
+		array = make([]string, offsetEnd-start)
+		copy(array, a.array[start:offsetEnd])
+	} else {
+		array = a.array[start:offsetEnd]
+	}
+	return array
+}
+
+// SubSlice returns a slice of elements from the array as specified
+// by the <offset> and <size> parameters.
+// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
+//
+// If offset is non-negative, the sequence will start at that offset in the array.
+// If offset is negative, the sequence will start that far from the end of the array.
+//
+// If length is given and is positive, then the sequence will have up to that many elements in it.
+// If the array is shorter than the length, then only the available array elements will be present.
+// If length is given and is negative then the sequence will stop that many elements from the end of the array.
+// If it is omitted, then the sequence will have everything from offset up until the end of the array.
+//
+// Any possibility crossing the left border of array, it will fail.
+func (a *SortedStrArray) SubSlice(offset int, length ...int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	size := len(a.array)
+	if len(length) > 0 {
+		size = length[0]
+	}
+	if offset > len(a.array) {
+		return nil
+	}
+	if offset < 0 {
+		offset = len(a.array) + offset
+		if offset < 0 {
+			return nil
+		}
+	}
+	if size < 0 {
+		offset += size
+		size = -size
+		if offset < 0 {
+			return nil
+		}
+	}
+	end := offset + size
+	if end > len(a.array) {
+		end = len(a.array)
+		size = len(a.array) - offset
+	}
+	if a.mu.IsSafe() {
+		s := make([]string, size)
+		copy(s, a.array[offset:])
+		return s
+	} else {
+		return a.array[offset:end]
+	}
+}
+
+// Sum returns the sum of values in an array.
+func (a *SortedStrArray) Sum() (sum int) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		sum += gconv.Int(v)
+	}
+	return
+}
+
+// Len returns the length of array.
+func (a *SortedStrArray) Len() int {
+	a.mu.RLock()
+	length := len(a.array)
+	a.mu.RUnlock()
+	return length
+}
+
+// Slice returns the underlying data of array.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (a *SortedStrArray) Slice() []string {
+	array := ([]string)(nil)
+	if a.mu.IsSafe() {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+		array = make([]string, len(a.array))
+		copy(array, a.array)
+	} else {
+		array = a.array
+	}
+	return array
+}
+
+// Interfaces returns current array as []interface{}.
+func (a *SortedStrArray) Interfaces() []interface{} {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	array := make([]interface{}, len(a.array))
+	for k, v := range a.array {
+		array[k] = v
+	}
+	return array
+}
+
+// Contains checks whether a value exists in the array.
+func (a *SortedStrArray) Contains(value string) bool {
+	return a.Search(value) != -1
+}
+
+// ContainsI checks whether a value exists in the array with case-insensitively.
+// Note that it internally iterates the whole array to do the comparison with case-insensitively.
+func (a *SortedStrArray) ContainsI(value string) bool {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return false
+	}
+	for _, v := range a.array {
+		if strings.EqualFold(v, value) {
+			return true
+		}
+	}
+	return false
+}
+
+// Search searches array by <value>, returns the index of <value>,
+// or returns -1 if not exists.
+func (a *SortedStrArray) Search(value string) (index int) {
+	if i, r := a.binSearch(value, true); r == 0 {
+		return i
+	}
+	return -1
+}
+
+// Binary search.
+// It returns the last compared index and the result.
+// If <result> equals to 0, it means the value at <index> is equals to <value>.
+// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
+// If <result> greater than 0, it means the value at <index> is greater than <value>.
+func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
+	if lock {
+		a.mu.RLock()
+		defer a.mu.RUnlock()
+	}
+	if len(a.array) == 0 {
+		return -1, -2
+	}
+	min := 0
+	max := len(a.array) - 1
+	mid := 0
+	cmp := -2
+	for min <= max {
+		mid = min + int((max-min)/2)
+		cmp = a.getComparator()(value, a.array[mid])
+		switch {
+		case cmp < 0:
+			max = mid - 1
+		case cmp > 0:
+			min = mid + 1
+		default:
+			return mid, cmp
+		}
+	}
+	return mid, cmp
+}
+
+// SetUnique sets unique mark to the array,
+// which means it does not contain any repeated items.
+// It also do unique check, remove all repeated items.
+func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
+	oldUnique := a.unique
+	a.unique = unique
+	if unique && oldUnique != unique {
+		a.Unique()
+	}
+	return a
+}
+
+// Unique uniques the array, clear repeated items.
+func (a *SortedStrArray) Unique() *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if len(a.array) == 0 {
+		return a
+	}
+	i := 0
+	for {
+		if i == len(a.array)-1 {
+			break
+		}
+		if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
+			a.array = append(a.array[:i+1], a.array[i+1+1:]...)
+		} else {
+			i++
+		}
+	}
+	return a
+}
+
+// Clone returns a new array, which is a copy of current array.
+func (a *SortedStrArray) Clone() (newArray *SortedStrArray) {
+	a.mu.RLock()
+	array := make([]string, len(a.array))
+	copy(array, a.array)
+	a.mu.RUnlock()
+	return NewSortedStrArrayFrom(array, a.mu.IsSafe())
+}
+
+// Clear deletes all items of current array.
+func (a *SortedStrArray) Clear() *SortedStrArray {
+	a.mu.Lock()
+	if len(a.array) > 0 {
+		a.array = make([]string, 0)
+	}
+	a.mu.Unlock()
+	return a
+}
+
+// LockFunc locks writing by callback function <f>.
+func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	f(a.array)
+	return a
+}
+
+// RLockFunc locks reading by callback function <f>.
+func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	f(a.array)
+	return a
+}
+
+// Merge merges <array> into current array.
+// The parameter <array> can be any garray or slice type.
+// The difference between Merge and Append is Append supports only specified slice type,
+// but Merge supports more parameter types.
+func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
+	return a.Add(gconv.Strings(array)...)
+}
+
+// Chunk splits an array into multiple arrays,
+// the size of each array is determined by <size>.
+// The last chunk may contain less than size elements.
+func (a *SortedStrArray) Chunk(size int) [][]string {
+	if size < 1 {
+		return nil
+	}
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	length := len(a.array)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]string
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, a.array[i*size:end])
+		i++
+	}
+	return n
+}
+
+// Rand randomly returns one item from array(no deleting).
+func (a *SortedStrArray) Rand() (value string, found bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return "", false
+	}
+	return a.array[grand.Intn(len(a.array))], true
+}
+
+// Rands randomly returns <size> items from array(no deleting).
+func (a *SortedStrArray) Rands(size int) []string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if size <= 0 || len(a.array) == 0 {
+		return nil
+	}
+	array := make([]string, size)
+	for i := 0; i < size; i++ {
+		array[i] = a.array[grand.Intn(len(a.array))]
+	}
+	return array
+}
+
+// Join joins array elements with a string <glue>.
+func (a *SortedStrArray) Join(glue string) string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	if len(a.array) == 0 {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	for k, v := range a.array {
+		buffer.WriteString(v)
+		if k != len(a.array)-1 {
+			buffer.WriteString(glue)
+		}
+	}
+	return buffer.String()
+}
+
+// CountValues counts the number of occurrences of all values in the array.
+func (a *SortedStrArray) CountValues() map[string]int {
+	m := make(map[string]int)
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for _, v := range a.array {
+		m[v]++
+	}
+	return m
+}
+
+// Iterator is alias of IteratorAsc.
+func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
+	a.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for k, v := range a.array {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	for i := len(a.array) - 1; i >= 0; i-- {
+		if !f(i, a.array[i]) {
+			break
+		}
+	}
+}
+
+// String returns current array as a string, which implements like json.Marshal does.
+func (a *SortedStrArray) String() string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	buffer := bytes.NewBuffer(nil)
+	buffer.WriteByte('[')
+	for k, v := range a.array {
+		buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
+		if k != len(a.array)-1 {
+			buffer.WriteByte(',')
+		}
+	}
+	buffer.WriteByte(']')
+	return buffer.String()
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+// Note that do not use pointer as its receiver here.
+func (a SortedStrArray) MarshalJSON() ([]byte, error) {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+	return json.Marshal(a.array)
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
+	if a.comparator == nil {
+		a.array = make([]string, 0)
+		a.comparator = defaultComparatorStr
+	}
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &a.array)
+	default:
+		a.array = gconv.SliceStr(value)
+	}
+	if a.array != nil {
+		sort.Strings(a.array)
+	}
+	return err
+}
+
+// FilterEmpty removes all empty string value of the array.
+func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+	for i := 0; i < len(a.array); {
+		if a.array[i] == "" {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	for i := len(a.array) - 1; i >= 0; {
+		if a.array[i] == "" {
+			a.array = append(a.array[:i], a.array[i+1:]...)
+		} else {
+			break
+		}
+	}
+	return a
+}
+
+// Walk applies a user supplied function <f> to every item of array.
+func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
+	a.mu.Lock()
+	defer a.mu.Unlock()
+
+	// Keep the array always sorted.
+	defer quickSortStr(a.array, a.getComparator())
+
+	for i, v := range a.array {
+		a.array[i] = f(v)
+	}
+	return a
+}
+
+// IsEmpty checks whether the array is empty.
+func (a *SortedStrArray) IsEmpty() bool {
+	return a.Len() == 0
+}
+
+// getComparator returns the comparator if it's previously set,
+// or else it returns a default comparator.
+func (a *SortedStrArray) getComparator() func(a, b string) int {
+	if a.comparator == nil {
+		return defaultComparatorStr
+	}
+	return a.comparator
+}

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

@@ -0,0 +1,545 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with 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/internal/json"
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+type (
+	// List is a doubly linked list containing a concurrent-safe/unsafe switch.
+	// The switch should be set when its initialization and cannot be changed then.
+	List struct {
+		mu   rwmutex.RWMutex
+		list *list.List
+	}
+	// Element the item type of the list.
+	Element = list.Element
+)
+
+// New creates and returns a new empty doubly linked list.
+func New(safe ...bool) *List {
+	return &List{
+		mu:   rwmutex.Create(safe...),
+		list: list.New(),
+	}
+}
+
+// NewFrom creates and returns a list from a copy of given slice <array>.
+// The parameter <safe> is used to specify whether using list in concurrent-safety,
+// which is false in default.
+func NewFrom(array []interface{}, safe ...bool) *List {
+	l := list.New()
+	for _, v := range array {
+		l.PushBack(v)
+	}
+	return &List{
+		mu:   rwmutex.Create(safe...),
+		list: l,
+	}
+}
+
+// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
+func (l *List) PushFront(v interface{}) (e *Element) {
+	l.mu.Lock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	e = l.list.PushFront(v)
+	l.mu.Unlock()
+	return
+}
+
+// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
+func (l *List) PushBack(v interface{}) (e *Element) {
+	l.mu.Lock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	e = l.list.PushBack(v)
+	l.mu.Unlock()
+	return
+}
+
+// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
+func (l *List) PushFronts(values []interface{}) {
+	l.mu.Lock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	for _, v := range values {
+		l.list.PushFront(v)
+	}
+	l.mu.Unlock()
+}
+
+// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
+func (l *List) PushBacks(values []interface{}) {
+	l.mu.Lock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	for _, v := range values {
+		l.list.PushBack(v)
+	}
+	l.mu.Unlock()
+}
+
+// PopBack removes the element from back of <l> and returns the value of the element.
+func (l *List) PopBack() (value interface{}) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+		return
+	}
+	if e := l.list.Back(); e != nil {
+		value = l.list.Remove(e)
+	}
+	return
+}
+
+// PopFront removes the element from front of <l> and returns the value of the element.
+func (l *List) PopFront() (value interface{}) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+		return
+	}
+	if e := l.list.Front(); e != nil {
+		value = l.list.Remove(e)
+	}
+	return
+}
+
+// PopBacks removes <max> elements from back of <l>
+// and returns values of the removed elements as slice.
+func (l *List) PopBacks(max int) (values []interface{}) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		if max > 0 && max < length {
+			length = max
+		}
+		values = make([]interface{}, length)
+		for i := 0; i < length; i++ {
+			values[i] = l.list.Remove(l.list.Back())
+		}
+	}
+	return
+}
+
+// PopFronts removes <max> elements from front of <l>
+// and returns values of the removed elements as slice.
+func (l *List) PopFronts(max int) (values []interface{}) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		if max > 0 && max < length {
+			length = max
+		}
+		values = make([]interface{}, length)
+		for i := 0; i < length; i++ {
+			values[i] = l.list.Remove(l.list.Front())
+		}
+	}
+	return
+}
+
+// PopBackAll removes all elements from back of <l>
+// and returns values of the removed elements as slice.
+func (l *List) PopBackAll() []interface{} {
+	return l.PopBacks(-1)
+}
+
+// PopFrontAll removes all elements from front of <l>
+// and returns values of the removed elements as slice.
+func (l *List) PopFrontAll() []interface{} {
+	return l.PopFronts(-1)
+}
+
+// FrontAll copies and returns values of all elements from front of <l> as slice.
+func (l *List) FrontAll() (values []interface{}) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		values = make([]interface{}, length)
+		for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
+			values[i] = e.Value
+		}
+	}
+	return
+}
+
+// BackAll copies and returns values of all elements from back of <l> as slice.
+func (l *List) BackAll() (values []interface{}) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		values = make([]interface{}, length)
+		for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
+			values[i] = e.Value
+		}
+	}
+	return
+}
+
+// FrontValue returns value of the first element of <l> or nil if the list is empty.
+func (l *List) FrontValue() (value interface{}) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	if e := l.list.Front(); e != nil {
+		value = e.Value
+	}
+	return
+}
+
+// BackValue returns value of the last element of <l> or nil if the list is empty.
+func (l *List) BackValue() (value interface{}) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	if e := l.list.Back(); e != nil {
+		value = e.Value
+	}
+	return
+}
+
+// Front returns the first element of list <l> or nil if the list is empty.
+func (l *List) Front() (e *Element) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	e = l.list.Front()
+	return
+}
+
+// Back returns the last element of list <l> or nil if the list is empty.
+func (l *List) Back() (e *Element) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	e = l.list.Back()
+	return
+}
+
+// Len returns the number of elements of list <l>.
+// The complexity is O(1).
+func (l *List) Len() (length int) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	length = l.list.Len()
+	return
+}
+
+// Size is alias of Len.
+func (l *List) Size() int {
+	return l.Len()
+}
+
+// MoveBefore moves element <e> to its new position before <p>.
+// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
+// The element and <p> must not be nil.
+func (l *List) MoveBefore(e, p *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.MoveBefore(e, p)
+}
+
+// MoveAfter moves element <e> to its new position after <p>.
+// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
+// The element and <p> must not be nil.
+func (l *List) MoveAfter(e, p *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.MoveAfter(e, p)
+}
+
+// MoveToFront moves element <e> to the front of list <l>.
+// If <e> is not an element of <l>, the list is not modified.
+// The element must not be nil.
+func (l *List) MoveToFront(e *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.MoveToFront(e)
+}
+
+// MoveToBack moves element <e> to the back of list <l>.
+// If <e> is not an element of <l>, the list is not modified.
+// The element must not be nil.
+func (l *List) MoveToBack(e *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.MoveToBack(e)
+}
+
+// PushBackList inserts a copy of an other list at the back of list <l>.
+// The lists <l> and <other> may be the same, but they must not be nil.
+func (l *List) PushBackList(other *List) {
+	if l != other {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.PushBackList(other.list)
+}
+
+// PushFrontList inserts a copy of an other list at the front of list <l>.
+// The lists <l> and <other> may be the same, but they must not be nil.
+func (l *List) PushFrontList(other *List) {
+	if l != other {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	l.list.PushFrontList(other.list)
+}
+
+// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
+// If <p> is not an element of <l>, the list is not modified.
+// The <p> must not be nil.
+func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	e = l.list.InsertAfter(v, p)
+	return
+}
+
+// InsertBefore inserts a new element <e> with value <v> immediately before <p> and returns <e>.
+// If <p> is not an element of <l>, the list is not modified.
+// The <p> must not be nil.
+func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	e = l.list.InsertBefore(v, p)
+	return
+}
+
+// Remove removes <e> from <l> if <e> is an element of list <l>.
+// It returns the element value e.Value.
+// The element must not be nil.
+func (l *List) Remove(e *Element) (value interface{}) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	value = l.list.Remove(e)
+	return
+}
+
+// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
+func (l *List) Removes(es []*Element) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	for _, e := range es {
+		l.list.Remove(e)
+	}
+	return
+}
+
+// RemoveAll removes all elements from list <l>.
+func (l *List) RemoveAll() {
+	l.mu.Lock()
+	l.list = list.New()
+	l.mu.Unlock()
+}
+
+// See RemoveAll().
+func (l *List) Clear() {
+	l.RemoveAll()
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (l *List) RLockFunc(f func(list *list.List)) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list != nil {
+		f(l.list)
+	}
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (l *List) LockFunc(f func(list *list.List)) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	f(l.list)
+}
+
+// Iterator is alias of IteratorAsc.
+func (l *List) Iterator(f func(e *Element) bool) {
+	l.IteratorAsc(f)
+}
+
+// IteratorAsc iterates the list readonly in ascending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (l *List) IteratorAsc(f func(e *Element) bool) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
+			if !f(e) {
+				break
+			}
+		}
+	}
+}
+
+// IteratorDesc iterates the list readonly in descending order with given callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (l *List) IteratorDesc(f func(e *Element) bool) {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return
+	}
+	length := l.list.Len()
+	if length > 0 {
+		for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
+			if !f(e) {
+				break
+			}
+		}
+	}
+}
+
+// Join joins list elements with a string <glue>.
+func (l *List) Join(glue string) string {
+	l.mu.RLock()
+	defer l.mu.RUnlock()
+	if l.list == nil {
+		return ""
+	}
+	buffer := bytes.NewBuffer(nil)
+	length := l.list.Len()
+	if length > 0 {
+		for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
+			buffer.WriteString(gconv.String(e.Value))
+			if i != length-1 {
+				buffer.WriteString(glue)
+			}
+		}
+	}
+	return buffer.String()
+}
+
+// String returns current list as a string.
+func (l *List) String() string {
+	return "[" + l.Join(",") + "]"
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (l *List) MarshalJSON() ([]byte, error) {
+	return json.Marshal(l.FrontAll())
+}
+
+// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
+func (l *List) UnmarshalJSON(b []byte) error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	if l.list == nil {
+		l.list = list.New()
+	}
+	var array []interface{}
+	if err := json.Unmarshal(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.Unmarshal(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceAny(value)
+	}
+	l.PushBacks(array)
+	return err
+}

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

@@ -0,0 +1,45 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with 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...)
+}

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

@@ -0,0 +1,499 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+type AnyAnyMap struct {
+	mu   rwmutex.RWMutex
+	data map[interface{}]interface{}
+}
+
+// NewAnyAnyMap creates and returns an empty hash map.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
+	return &AnyAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[interface{}]interface{}),
+	}
+}
+
+// NewAnyAnyMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
+	return &AnyAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *AnyAnyMap) Clone(safe ...bool) *AnyAnyMap {
+	return NewFrom(m.MapCopy(), safe...)
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *AnyAnyMap) Map() map[interface{}]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[interface{}]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[interface{}]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[gconv.String(k)] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *AnyAnyMap) FilterEmpty() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+}
+
+// FilterNil deletes all key-value pair of which the value is nil.
+func (m *AnyAnyMap) FilterNil() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for k, v := range m.data {
+		if empty.IsNil(v) {
+			delete(m.data, k)
+		}
+	}
+}
+
+// Set sets key-value to the hash map.
+func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[interface{}]interface{})
+	}
+	m.data[key] = value
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *AnyAnyMap) Pop() (key, value interface{}) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[interface{}]interface{}, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// When setting value, if <value> is type of <func() interface {}>,
+// it will be executed with mutex.Lock of the hash map,
+// and its return value will be set to the map with <key>.
+//
+// It returns value with given <key>.
+func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[interface{}]interface{})
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		m.data[key] = value
+	}
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a Var with the value by given <key>.
+// The returned Var is un-concurrent safe.
+func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
+	return gvar.New(m.Get(key))
+}
+
+// GetVarOrSet returns a Var with result from GetVarOrSet.
+// The returned Var is un-concurrent safe.
+func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
+// The returned Var is un-concurrent safe.
+func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
+// The returned Var is un-concurrent safe.
+func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFuncLock(key, f))
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *AnyAnyMap) Removes(keys []interface{}) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *AnyAnyMap) Keys() []interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	var (
+		keys  = make([]interface{}, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *AnyAnyMap) Values() []interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	var (
+		values = make([]interface{}, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *AnyAnyMap) Contains(key interface{}) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *AnyAnyMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *AnyAnyMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *AnyAnyMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[interface{}]interface{})
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *AnyAnyMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[interface{}]interface{}, len(m.data))
+	for k, v := range m.data {
+		n[v] = k
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *AnyAnyMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(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
+}

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

@@ -0,0 +1,500 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type IntAnyMap struct {
+	mu   rwmutex.RWMutex
+	data map[int]interface{}
+}
+
+// NewIntAnyMap returns an empty IntAnyMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewIntAnyMap(safe ...bool) *IntAnyMap {
+	return &IntAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[int]interface{}),
+	}
+}
+
+// NewIntAnyMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
+	return &IntAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *IntAnyMap) Clone() *IntAnyMap {
+	return NewIntAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *IntAnyMap) Map() map[int]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[int]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *IntAnyMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[gconv.String(k)] = v
+	}
+	m.mu.RUnlock()
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *IntAnyMap) MapCopy() map[int]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[int]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *IntAnyMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// FilterNil deletes all key-value pair of which the value is nil.
+func (m *IntAnyMap) FilterNil() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for k, v := range m.data {
+		if empty.IsNil(v) {
+			delete(m.data, k)
+		}
+	}
+}
+
+// Set sets key-value to the hash map.
+func (m *IntAnyMap) Set(key int, val interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[int]interface{})
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *IntAnyMap) Sets(data map[int]interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *IntAnyMap) Get(key int) (value interface{}) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *IntAnyMap) Pop() (key int, value interface{}) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *IntAnyMap) Pops(size int) map[int]interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[int]interface{}, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// When setting value, if <value> is type of <func() interface {}>,
+// it will be executed with mutex.Lock of the hash map,
+// and its return value will be set to the map with <key>.
+//
+// It returns value with given <key>.
+func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]interface{})
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		m.data[key] = value
+	}
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a Var with the value by given <key>.
+// The returned Var is un-concurrent safe.
+func (m *IntAnyMap) GetVar(key int) *gvar.Var {
+	return gvar.New(m.Get(key))
+}
+
+// GetVarOrSet returns a Var with result from GetVarOrSet.
+// The returned Var is un-concurrent safe.
+func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
+// The returned Var is un-concurrent safe.
+func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
+// The returned Var is un-concurrent safe.
+func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFuncLock(key, f))
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *IntAnyMap) Removes(keys []int) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *IntAnyMap) Remove(key int) (value interface{}) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *IntAnyMap) Keys() []int {
+	m.mu.RLock()
+	var (
+		keys  = make([]int, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *IntAnyMap) Values() []interface{} {
+	m.mu.RLock()
+	var (
+		values = make([]interface{}, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *IntAnyMap) Contains(key int) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *IntAnyMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *IntAnyMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *IntAnyMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[int]interface{})
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *IntAnyMap) Replace(data map[int]interface{}) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *IntAnyMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[int]interface{}, len(m.data))
+	for k, v := range m.data {
+		n[gconv.Int(v)] = k
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *IntAnyMap) Merge(other *IntAnyMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *IntAnyMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]interface{})
+	}
+	switch value.(type) {
+	case string, []byte:
+		return json.Unmarshal(gconv.Bytes(value), &m.data)
+	default:
+		for k, v := range gconv.Map(value) {
+			m.data[gconv.Int(k)] = v
+		}
+	}
+	return
+}

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

@@ -0,0 +1,471 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+type IntIntMap struct {
+	mu   rwmutex.RWMutex
+	data map[int]int
+}
+
+// NewIntIntMap returns an empty IntIntMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewIntIntMap(safe ...bool) *IntIntMap {
+	return &IntIntMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[int]int),
+	}
+}
+
+// NewIntIntMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
+	return &IntIntMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *IntIntMap) Clone() *IntIntMap {
+	return NewIntIntMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *IntIntMap) Map() map[int]int {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[int]int, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *IntIntMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[gconv.String(k)] = v
+	}
+	m.mu.RUnlock()
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *IntIntMap) MapCopy() map[int]int {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[int]int, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *IntIntMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Set sets key-value to the hash map.
+func (m *IntIntMap) Set(key int, val int) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[int]int)
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *IntIntMap) Sets(data map[int]int) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *IntIntMap) Search(key int) (value int, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *IntIntMap) Get(key int) (value int) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *IntIntMap) Pop() (key, value int) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *IntIntMap) Pops(size int) map[int]int {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[int]int, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// It returns value with given <key>.
+func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]int)
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	m.data[key] = value
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *IntIntMap) GetOrSet(key int, value int) int {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
+	if v, ok := m.Search(key); !ok {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[int]int)
+		}
+		if v, ok = m.data[key]; ok {
+			return v
+		}
+		v = f()
+		m.data[key] = v
+		return v
+	} else {
+		return v
+	}
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
+	if !m.Contains(key) {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[int]int)
+		}
+		if _, ok := m.data[key]; !ok {
+			m.data[key] = f()
+		}
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *IntIntMap) Removes(keys []int) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *IntIntMap) Remove(key int) (value int) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *IntIntMap) Keys() []int {
+	m.mu.RLock()
+	var (
+		keys  = make([]int, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *IntIntMap) Values() []int {
+	m.mu.RLock()
+	var (
+		values = make([]int, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *IntIntMap) Contains(key int) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *IntIntMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *IntIntMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *IntIntMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[int]int)
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *IntIntMap) Replace(data map[int]int) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *IntIntMap) RLockFunc(f func(m map[int]int)) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *IntIntMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[int]int, len(m.data))
+	for k, v := range m.data {
+		n[v] = k
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *IntIntMap) Merge(other *IntIntMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *IntIntMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]int)
+	}
+	switch value.(type) {
+	case string, []byte:
+		return json.Unmarshal(gconv.Bytes(value), &m.data)
+	default:
+		for k, v := range gconv.Map(value) {
+			m.data[gconv.Int(k)] = gconv.Int(v)
+		}
+	}
+	return
+}

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

@@ -0,0 +1,471 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type IntStrMap struct {
+	mu   rwmutex.RWMutex
+	data map[int]string
+}
+
+// NewIntStrMap returns an empty IntStrMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewIntStrMap(safe ...bool) *IntStrMap {
+	return &IntStrMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[int]string),
+	}
+}
+
+// NewIntStrMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
+	return &IntStrMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *IntStrMap) Clone() *IntStrMap {
+	return NewIntStrMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *IntStrMap) Map() map[int]string {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[int]string, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *IntStrMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[gconv.String(k)] = v
+	}
+	m.mu.RUnlock()
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *IntStrMap) MapCopy() map[int]string {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[int]string, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *IntStrMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Set sets key-value to the hash map.
+func (m *IntStrMap) Set(key int, val string) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[int]string)
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *IntStrMap) Sets(data map[int]string) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *IntStrMap) Search(key int) (value string, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *IntStrMap) Get(key int) (value string) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *IntStrMap) Pop() (key int, value string) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *IntStrMap) Pops(size int) map[int]string {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[int]string, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// It returns value with given <key>.
+func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]string)
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	m.data[key] = value
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *IntStrMap) GetOrSet(key int, value string) string {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist and returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
+	if v, ok := m.Search(key); !ok {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[int]string)
+		}
+		if v, ok = m.data[key]; ok {
+			return v
+		}
+		v = f()
+		m.data[key] = v
+		return v
+	} else {
+		return v
+	}
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
+	if !m.Contains(key) {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[int]string)
+		}
+		if _, ok := m.data[key]; !ok {
+			m.data[key] = f()
+		}
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *IntStrMap) Removes(keys []int) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *IntStrMap) Remove(key int) (value string) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *IntStrMap) Keys() []int {
+	m.mu.RLock()
+	var (
+		keys  = make([]int, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *IntStrMap) Values() []string {
+	m.mu.RLock()
+	var (
+		values = make([]string, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *IntStrMap) Contains(key int) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *IntStrMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *IntStrMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *IntStrMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[int]string)
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *IntStrMap) Replace(data map[int]string) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *IntStrMap) RLockFunc(f func(m map[int]string)) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *IntStrMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[int]string, len(m.data))
+	for k, v := range m.data {
+		n[gconv.Int(v)] = gconv.String(k)
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *IntStrMap) Merge(other *IntStrMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *IntStrMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[int]string)
+	}
+	switch value.(type) {
+	case string, []byte:
+		return json.Unmarshal(gconv.Bytes(value), &m.data)
+	default:
+		for k, v := range gconv.Map(value) {
+			m.data[gconv.Int(k)] = gconv.String(v)
+		}
+	}
+	return
+}

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

@@ -0,0 +1,486 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type StrAnyMap struct {
+	mu   rwmutex.RWMutex
+	data map[string]interface{}
+}
+
+// NewStrAnyMap returns an empty StrAnyMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewStrAnyMap(safe ...bool) *StrAnyMap {
+	return &StrAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[string]interface{}),
+	}
+}
+
+// NewStrAnyMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
+	return &StrAnyMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *StrAnyMap) Clone() *StrAnyMap {
+	return NewStrAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *StrAnyMap) Map() map[string]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *StrAnyMap) MapStrAny() map[string]interface{} {
+	return m.Map()
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *StrAnyMap) MapCopy() map[string]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *StrAnyMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// FilterNil deletes all key-value pair of which the value is nil.
+func (m *StrAnyMap) FilterNil() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for k, v := range m.data {
+		if empty.IsNil(v) {
+			delete(m.data, k)
+		}
+	}
+}
+
+// Set sets key-value to the hash map.
+func (m *StrAnyMap) Set(key string, val interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[string]interface{})
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *StrAnyMap) Sets(data map[string]interface{}) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *StrAnyMap) Get(key string) (value interface{}) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *StrAnyMap) Pop() (key string, value interface{}) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *StrAnyMap) Pops(size int) map[string]interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[string]interface{}, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// When setting value, if <value> is type of <func() interface {}>,
+// it will be executed with mutex.Lock of the hash map,
+// and its return value will be set to the map with <key>.
+//
+// It returns value with given <key>.
+func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[string]interface{})
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	if f, ok := value.(func() interface{}); ok {
+		value = f()
+	}
+	if value != nil {
+		m.data[key] = value
+	}
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f)
+	} else {
+		return v
+	}
+}
+
+// GetVar returns a Var with the value by given <key>.
+// The returned Var is un-concurrent safe.
+func (m *StrAnyMap) GetVar(key string) *gvar.Var {
+	return gvar.New(m.Get(key))
+}
+
+// GetVarOrSet returns a Var with result from GetVarOrSet.
+// The returned Var is un-concurrent safe.
+func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSet(key, value))
+}
+
+// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
+// The returned Var is un-concurrent safe.
+func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFunc(key, f))
+}
+
+// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
+// The returned Var is un-concurrent safe.
+func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
+	return gvar.New(m.GetOrSetFuncLock(key, f))
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f)
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *StrAnyMap) Removes(keys []string) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *StrAnyMap) Remove(key string) (value interface{}) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *StrAnyMap) Keys() []string {
+	m.mu.RLock()
+	var (
+		keys  = make([]string, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *StrAnyMap) Values() []interface{} {
+	m.mu.RLock()
+	var (
+		values = make([]interface{}, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *StrAnyMap) Contains(key string) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *StrAnyMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *StrAnyMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *StrAnyMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[string]interface{})
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *StrAnyMap) Replace(data map[string]interface{}) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *StrAnyMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		n[gconv.String(v)] = k
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *StrAnyMap) Merge(other *StrAnyMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *StrAnyMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.data = gconv.Map(value)
+	return
+}

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

@@ -0,0 +1,474 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type StrIntMap struct {
+	mu   rwmutex.RWMutex
+	data map[string]int
+}
+
+// NewStrIntMap returns an empty StrIntMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewStrIntMap(safe ...bool) *StrIntMap {
+	return &StrIntMap{
+		mu:   rwmutex.Create(safe...),
+		data: make(map[string]int),
+	}
+}
+
+// NewStrIntMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
+	return &StrIntMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *StrIntMap) Clone() *StrIntMap {
+	return NewStrIntMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *StrIntMap) Map() map[string]int {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[string]int, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *StrIntMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *StrIntMap) MapCopy() map[string]int {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]int, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *StrIntMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Set sets key-value to the hash map.
+func (m *StrIntMap) Set(key string, val int) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[string]int)
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *StrIntMap) Sets(data map[string]int) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *StrIntMap) Search(key string) (value int, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *StrIntMap) Get(key string) (value int) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *StrIntMap) Pop() (key string, value int) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *StrIntMap) Pops(size int) map[string]int {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[string]int, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// It returns value with given <key>.
+func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[string]int)
+	}
+	if v, ok := m.data[key]; ok {
+		m.mu.Unlock()
+		return v
+	}
+	m.data[key] = value
+	m.mu.Unlock()
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *StrIntMap) GetOrSet(key string, value int) int {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
+	if v, ok := m.Search(key); !ok {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[string]int)
+		}
+		if v, ok = m.data[key]; ok {
+			return v
+		}
+		v = f()
+		m.data[key] = v
+		return v
+	} else {
+		return v
+	}
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
+	if !m.Contains(key) {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[string]int)
+		}
+		if _, ok := m.data[key]; !ok {
+			m.data[key] = f()
+		}
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *StrIntMap) Removes(keys []string) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *StrIntMap) Remove(key string) (value int) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *StrIntMap) Keys() []string {
+	m.mu.RLock()
+	var (
+		keys  = make([]string, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *StrIntMap) Values() []int {
+	m.mu.RLock()
+	var (
+		values = make([]int, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *StrIntMap) Contains(key string) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *StrIntMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *StrIntMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *StrIntMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[string]int)
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *StrIntMap) Replace(data map[string]int) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *StrIntMap) RLockFunc(f func(m map[string]int)) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *StrIntMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[string]int, len(m.data))
+	for k, v := range m.data {
+		n[gconv.String(v)] = gconv.Int(k)
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *StrIntMap) Merge(other *StrIntMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *StrIntMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[string]int)
+	}
+	switch value.(type) {
+	case string, []byte:
+		return json.Unmarshal(gconv.Bytes(value), &m.data)
+	default:
+		for k, v := range gconv.Map(value) {
+			m.data[k] = gconv.Int(v)
+		}
+	}
+	return
+}

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

@@ -0,0 +1,464 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+type StrStrMap struct {
+	mu   rwmutex.RWMutex
+	data map[string]string
+}
+
+// NewStrStrMap returns an empty StrStrMap object.
+// The parameter <safe> is used to specify whether using map in concurrent-safety,
+// which is false in default.
+func NewStrStrMap(safe ...bool) *StrStrMap {
+	return &StrStrMap{
+		data: make(map[string]string),
+		mu:   rwmutex.Create(safe...),
+	}
+}
+
+// NewStrStrMapFrom creates and returns a hash map from given map <data>.
+// Note that, the param <data> map will be set as the underlying data map(no deep copy),
+// there might be some concurrent-safe issues when changing the map outside.
+func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
+	return &StrStrMap{
+		mu:   rwmutex.Create(safe...),
+		data: data,
+	}
+}
+
+// Iterator iterates the hash map readonly with custom callback function <f>.
+// If <f> returns true, then it continues iterating; or false to stop.
+func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	for k, v := range m.data {
+		if !f(k, v) {
+			break
+		}
+	}
+}
+
+// Clone returns a new hash map with copy of current map data.
+func (m *StrStrMap) Clone() *StrStrMap {
+	return NewStrStrMapFrom(m.MapCopy(), m.mu.IsSafe())
+}
+
+// Map returns the underlying data map.
+// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
+// or else a pointer to the underlying data.
+func (m *StrStrMap) Map() map[string]string {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if !m.mu.IsSafe() {
+		return m.data
+	}
+	data := make(map[string]string, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
+func (m *StrStrMap) MapStrAny() map[string]interface{} {
+	m.mu.RLock()
+	data := make(map[string]interface{}, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	m.mu.RUnlock()
+	return data
+}
+
+// MapCopy returns a copy of the underlying data of the hash map.
+func (m *StrStrMap) MapCopy() map[string]string {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	data := make(map[string]string, len(m.data))
+	for k, v := range m.data {
+		data[k] = v
+	}
+	return data
+}
+
+// FilterEmpty deletes all key-value pair of which the value is empty.
+// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
+func (m *StrStrMap) FilterEmpty() {
+	m.mu.Lock()
+	for k, v := range m.data {
+		if empty.IsEmpty(v) {
+			delete(m.data, k)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Set sets key-value to the hash map.
+func (m *StrStrMap) Set(key string, val string) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = make(map[string]string)
+	}
+	m.data[key] = val
+	m.mu.Unlock()
+}
+
+// Sets batch sets key-values to the hash map.
+func (m *StrStrMap) Sets(data map[string]string) {
+	m.mu.Lock()
+	if m.data == nil {
+		m.data = data
+	} else {
+		for k, v := range data {
+			m.data[k] = v
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Search searches the map with given <key>.
+// Second return parameter <found> is true if key was found, otherwise false.
+func (m *StrStrMap) Search(key string) (value string, found bool) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, found = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Get returns the value by given <key>.
+func (m *StrStrMap) Get(key string) (value string) {
+	m.mu.RLock()
+	if m.data != nil {
+		value, _ = m.data[key]
+	}
+	m.mu.RUnlock()
+	return
+}
+
+// Pop retrieves and deletes an item from the map.
+func (m *StrStrMap) Pop() (key, value string) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	for key, value = range m.data {
+		delete(m.data, key)
+		return
+	}
+	return
+}
+
+// Pops retrieves and deletes <size> items from the map.
+// It returns all items if size == -1.
+func (m *StrStrMap) Pops(size int) map[string]string {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if size > len(m.data) || size == -1 {
+		size = len(m.data)
+	}
+	if size == 0 {
+		return nil
+	}
+	var (
+		index  = 0
+		newMap = make(map[string]string, size)
+	)
+	for k, v := range m.data {
+		delete(m.data, k)
+		newMap[k] = v
+		index++
+		if index == size {
+			break
+		}
+	}
+	return newMap
+}
+
+// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
+// if not exists, set value to the map with given <key>,
+// or else just return the existing value.
+//
+// It returns value with given <key>.
+func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[string]string)
+	}
+	if v, ok := m.data[key]; ok {
+		return v
+	}
+	m.data[key] = value
+	return value
+}
+
+// GetOrSet returns the value by key,
+// or sets value with given <value> if it does not exist and then returns this value.
+func (m *StrStrMap) GetOrSet(key string, value string) string {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, value)
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFunc returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string {
+	if v, ok := m.Search(key); !ok {
+		return m.doSetWithLockCheck(key, f())
+	} else {
+		return v
+	}
+}
+
+// GetOrSetFuncLock returns the value by key,
+// or sets value with returned value of callback function <f> if it does not exist
+// and then returns this value.
+//
+// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
+// with mutex.Lock of the hash map.
+func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
+	if v, ok := m.Search(key); !ok {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[string]string)
+		}
+		if v, ok = m.data[key]; ok {
+			return v
+		}
+		v = f()
+		m.data[key] = v
+		return v
+	} else {
+		return v
+	}
+}
+
+// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrStrMap) SetIfNotExist(key string, value string) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, value)
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool {
+	if !m.Contains(key) {
+		m.doSetWithLockCheck(key, f())
+		return true
+	}
+	return false
+}
+
+// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
+// It returns false if <key> exists, and <value> would be ignored.
+//
+// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
+// it executes function <f> with mutex.Lock of the hash map.
+func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
+	if !m.Contains(key) {
+		m.mu.Lock()
+		defer m.mu.Unlock()
+		if m.data == nil {
+			m.data = make(map[string]string)
+		}
+		if _, ok := m.data[key]; !ok {
+			m.data[key] = f()
+		}
+		return true
+	}
+	return false
+}
+
+// Removes batch deletes values of the map by keys.
+func (m *StrStrMap) Removes(keys []string) {
+	m.mu.Lock()
+	if m.data != nil {
+		for _, key := range keys {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+}
+
+// Remove deletes value from map by given <key>, and return this deleted value.
+func (m *StrStrMap) Remove(key string) (value string) {
+	m.mu.Lock()
+	if m.data != nil {
+		var ok bool
+		if value, ok = m.data[key]; ok {
+			delete(m.data, key)
+		}
+	}
+	m.mu.Unlock()
+	return
+}
+
+// Keys returns all keys of the map as a slice.
+func (m *StrStrMap) Keys() []string {
+	m.mu.RLock()
+	var (
+		keys  = make([]string, len(m.data))
+		index = 0
+	)
+	for key := range m.data {
+		keys[index] = key
+		index++
+	}
+	m.mu.RUnlock()
+	return keys
+}
+
+// Values returns all values of the map as a slice.
+func (m *StrStrMap) Values() []string {
+	m.mu.RLock()
+	var (
+		values = make([]string, len(m.data))
+		index  = 0
+	)
+	for _, value := range m.data {
+		values[index] = value
+		index++
+	}
+	m.mu.RUnlock()
+	return values
+}
+
+// Contains checks whether a key exists.
+// It returns true if the <key> exists, or else false.
+func (m *StrStrMap) Contains(key string) bool {
+	var ok bool
+	m.mu.RLock()
+	if m.data != nil {
+		_, ok = m.data[key]
+	}
+	m.mu.RUnlock()
+	return ok
+}
+
+// Size returns the size of the map.
+func (m *StrStrMap) Size() int {
+	m.mu.RLock()
+	length := len(m.data)
+	m.mu.RUnlock()
+	return length
+}
+
+// IsEmpty checks whether the map is empty.
+// It returns true if map is empty, or else false.
+func (m *StrStrMap) IsEmpty() bool {
+	return m.Size() == 0
+}
+
+// Clear deletes all data of the map, it will remake a new underlying data map.
+func (m *StrStrMap) Clear() {
+	m.mu.Lock()
+	m.data = make(map[string]string)
+	m.mu.Unlock()
+}
+
+// Replace the data of the map with given <data>.
+func (m *StrStrMap) Replace(data map[string]string) {
+	m.mu.Lock()
+	m.data = data
+	m.mu.Unlock()
+}
+
+// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
+func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	f(m.data)
+}
+
+// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
+func (m *StrStrMap) RLockFunc(f func(m map[string]string)) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	f(m.data)
+}
+
+// Flip exchanges key-value of the map to value-key.
+func (m *StrStrMap) Flip() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	n := make(map[string]string, len(m.data))
+	for k, v := range m.data {
+		n[v] = k
+	}
+	m.data = n
+}
+
+// Merge merges two hash maps.
+// The <other> map will be merged into the map <m>.
+func (m *StrStrMap) Merge(other *StrStrMap) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = other.MapCopy()
+		return
+	}
+	if other != m {
+		other.mu.RLock()
+		defer other.mu.RUnlock()
+	}
+	for k, v := range other.data {
+		m.data[k] = v
+	}
+}
+
+// String returns the map as a string.
+func (m *StrStrMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(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.Unmarshal(b, &m.data); err != nil {
+		return err
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.data = gconv.MapStrStr(value)
+	return
+}

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

@@ -0,0 +1,562 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gmap
+
+import (
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/internal/empty"
+
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/container/glist"
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+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 {
+		node := (*gListMapNode)(nil)
+		m.list.IteratorAsc(func(e *glist.Element) bool {
+			node = e.Value.(*gListMapNode)
+			return f(node.key, node.value)
+		})
+	}
+}
+
+// IteratorDesc iterates the map 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 {
+		node := (*gListMapNode)(nil)
+		m.list.IteratorDesc(func(e *glist.Element) bool {
+			node = e.Value.(*gListMapNode)
+			return f(node.key, node.value)
+		})
+	}
+}
+
+// Clone returns a new link map with copy of current map data.
+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 {
+		keys := make([]interface{}, 0)
+		node := (*gListMapNode)(nil)
+		m.list.IteratorAsc(func(e *glist.Element) bool {
+			node = e.Value.(*gListMapNode)
+			if empty.IsEmpty(node.value) {
+				keys = append(keys, node.key)
+			}
+			return true
+		})
+		if len(keys) > 0 {
+			for _, key := range keys {
+				if e, ok := m.data[key]; ok {
+					delete(m.data, key)
+					m.list.Remove(e)
+				}
+			}
+		}
+	}
+	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()
+	}
+	node := (*gListMapNode)(nil)
+	other.list.IteratorAsc(func(e *glist.Element) bool {
+		node = e.Value.(*gListMapNode)
+		if e, ok := m.data[node.key]; !ok {
+			m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
+		} else {
+			e.Value = &gListMapNode{node.key, node.value}
+		}
+		return true
+	})
+}
+
+// String returns the map as a string.
+func (m *ListMap) String() string {
+	b, _ := m.MarshalJSON()
+	return gconv.UnsafeBytesToStr(b)
+}
+
+// MarshalJSON implements the interface MarshalJSON for json.Marshal.
+func (m *ListMap) MarshalJSON() ([]byte, error) {
+	return json.Marshal(gconv.Map(m.Map()))
+}
+
+// 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.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	for key, value := range data {
+		if e, ok := m.data[key]; !ok {
+			m.data[key] = m.list.PushBack(&gListMapNode{key, value})
+		} else {
+			e.Value = &gListMapNode{key, value}
+		}
+	}
+	return nil
+}
+
+// UnmarshalValue is an interface implement which sets any type of value for map.
+func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	if m.data == nil {
+		m.data = make(map[interface{}]*glist.Element)
+		m.list = glist.New()
+	}
+	for k, v := range gconv.Map(value) {
+		if e, ok := m.data[k]; !ok {
+			m.data[k] = m.list.PushBack(&gListMapNode{k, v})
+		} else {
+			e.Value = &gListMapNode{k, v}
+		}
+	}
+	return
+}

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

@@ -0,0 +1,30 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with gm file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gmap
+
+import (
+	"github.com/gogf/gf/container/gtree"
+)
+
+// Map 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...)
+}

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

@@ -0,0 +1,180 @@
+// Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+// Package gpool provides object-reusable concurrent-safe pool.
+package gpool
+
+import (
+	"errors"
+	"time"
+
+	"github.com/gogf/gf/container/glist"
+	"github.com/gogf/gf/container/gtype"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/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.
+}
+
+// Creation function for object.
+type NewFunc func() (interface{}, error)
+
+// 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(time.Second, r.checkExpireItems)
+	return r
+}
+
+// Put puts an item to pool.
+func (p *Pool) Put(value interface{}) error {
+	if p.closed.Val() {
+		return errors.New("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
+}
+
+// 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 from `Get` operation.
+				p.ExpireFunc(f.value)
+			}
+		} else {
+			break
+		}
+	}
+	if p.NewFunc != nil {
+		return p.NewFunc()
+	}
+	return nil, errors.New("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 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() {
+	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 items 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
+		}
+	}
+}

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

@@ -0,0 +1,146 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+// Package 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/container/glist"
+	"github.com/gogf/gf/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 (
+	// Size for queue buffer.
+	gDEFAULT_QUEUE_SIZE = 10000
+	// Max batch size per-fetching from list.
+	gDEFAULT_MAX_BATCH_SIZE = 10
+)
+
+// 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{}, gDEFAULT_QUEUE_SIZE)
+		go q.asyncLoopFromListToChannel()
+	}
+	return q
+}
+
+// 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 length := q.list.Len(); length > 0 {
+				if length > gDEFAULT_MAX_BATCH_SIZE {
+					length = gDEFAULT_MAX_BATCH_SIZE
+				}
+				for _, v := range q.list.PopFronts(length) {
+					// 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.
+					q.C <- v
+				}
+			} 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)
+}
+
+// Push pushes the data <v> into the queue.
+// Note that it would panics 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) < gDEFAULT_QUEUE_SIZE {
+			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() {
+	q.closed.Set(true)
+	if q.events != nil {
+		close(q.events)
+	}
+	if q.limit > 0 {
+		close(q.C)
+	}
+	for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
+		q.Pop()
+	}
+}
+
+// Len returns the length of the queue.
+// Note that the result might not be accurate as there's a
+// asynchronize channel reading the list constantly.
+func (q *Queue) Len() (length int) {
+	if q.list != nil {
+		length += q.list.Len()
+	}
+	length += len(q.C)
+	return
+}
+
+// Size is alias of Len.
+func (q *Queue) Size() int {
+	return q.Len()
+}

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

@@ -0,0 +1,507 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+// Package gset provides kinds of concurrent-safe/unsafe sets.
+package gset
+
+import (
+	"bytes"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type Set struct {
+	mu   rwmutex.RWMutex
+	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...)
+}
+
+// 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 exists in the set and
+// function <f> returns true, or else it does nothing and returns false.
+//
+// Note that, if <item> is nil, it does nothing and returns false. The function <f>
+// is executed without writing lock.
+func (set *Set) AddIfNotExistFunc(item interface{}, f func() bool) bool {
+	if item == nil {
+		return false
+	}
+	if !set.Contains(item) {
+		if f() {
+			set.mu.Lock()
+			defer set.mu.Unlock()
+			if set.data == nil {
+				set.data = make(map[interface{}]struct{})
+			}
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function <f> returns true, or else it does nothing and returns false.
+//
+// Note that, if <item> is nil, it does nothing and returns false. The function <f>
+// is executed within writing lock.
+func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() bool) bool {
+	if item == nil {
+		return false
+	}
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[interface{}]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains <item>.
+func (set *Set) Contains(item interface{}) bool {
+	var ok bool
+	set.mu.RLock()
+	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 the a of 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 {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		s      = ""
+		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
+}
+
+// Pops 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.Unmarshal(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.Unmarshal(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceAny(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}

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

@@ -0,0 +1,467 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gset
+
+import (
+	"bytes"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type IntSet struct {
+	mu   rwmutex.RWMutex
+	data map[int]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 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
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function <f> returns true, or else it does nothing and returns false.
+//
+// Note that, the function <f> is executed without writing lock.
+func (set *IntSet) AddIfNotExistFuncLock(item int, f func() bool) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[int]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains <item>.
+func (set *IntSet) Contains(item int) bool {
+	var ok bool
+	set.mu.RLock()
+	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 a 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 {
+	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
+}
+
+// Pops 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.Unmarshal(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.Unmarshal(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceInt(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}

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

@@ -0,0 +1,495 @@
+// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+//
+
+package gset
+
+import (
+	"bytes"
+	"github.com/gogf/gf/internal/json"
+	"github.com/gogf/gf/internal/rwmutex"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+	"strings"
+)
+
+type StrSet struct {
+	mu   rwmutex.RWMutex
+	data map[string]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 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 exists 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
+}
+
+// AddIfNotExistFunc checks whether item exists in the set,
+// it adds the item to set and returns true if it does not exists in the set and
+// function <f> returns true, or else it does nothing and returns false.
+//
+// Note that, the function <f> is executed without writing lock.
+func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
+	if !set.Contains(item) {
+		set.mu.Lock()
+		defer set.mu.Unlock()
+		if set.data == nil {
+			set.data = make(map[string]struct{})
+		}
+		if f() {
+			if _, ok := set.data[item]; !ok {
+				set.data[item] = struct{}{}
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// Contains checks whether the set contains <item>.
+func (set *StrSet) Contains(item string) bool {
+	var ok bool
+	set.mu.RLock()
+	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 a 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 {
+	set.mu.RLock()
+	defer set.mu.RUnlock()
+	var (
+		l      = len(set.data)
+		i      = 0
+		buffer = bytes.NewBuffer(nil)
+	)
+	for k, _ := range set.data {
+		buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
+		if i != l-1 {
+			buffer.WriteByte(',')
+		}
+		i++
+	}
+	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
+}
+
+// Pops 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.Unmarshal(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.Unmarshal(gconv.Bytes(value), &array)
+	default:
+		array = gconv.SliceStr(value)
+	}
+	for _, v := range array {
+		set.data[v] = struct{}{}
+	}
+	return
+}

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

@@ -0,0 +1,11 @@
+// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with 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
+// Thanks!
+package gtree

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

@@ -0,0 +1,795 @@
+// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package gtree
+
+import (
+	"fmt"
+	"github.com/gogf/gf/internal/json"
+
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/internal/rwmutex"
+)
+
+// 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 {
+	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() ([]byte, error) {
+	return json.Marshal(tree.Map())
+}
+
+// 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
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác