From 87295ef1fe5f6651abd0de27cce3705e1d5ef4d4 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 11 Dec 2019 21:22:41 +0800 Subject: [PATCH] improve process communication feature for gproc; add more slice converting functions for gconv --- .example/os/gproc/gproc_comm.go | 3 - .../os/gproc/{gproc2.go => gproc_kill.go} | 3 +- .example/os/gproc/gproc_sleep.go | 12 + .example/other/config.toml | 3 - .example/other/terminal-color.go | 42 -- .example/other/test.go | 71 ++- .example/other/test2.go | 18 +- .example/other/try-catch.go | 22 - net/ghttp/ghttp_server_config.go | 3 + net/gtcp/gtcp_conn_pkg.go | 7 +- net/gtcp/gtcp_pool.go | 36 +- net/gtcp/gtcp_unit_init_test.go | 21 + ...tcp_unit_test.go => gtcp_unit_pkg_test.go} | 11 - net/gtcp/gtcp_unit_pool_pkg_test.go | 172 ++++++ net/gtcp/gtcp_unit_pool_test.go | 65 +++ os/gfile/gfile.go | 10 +- os/gproc/gproc.go | 24 +- os/gproc/gproc_comm.go | 87 ++- os/gproc/gproc_comm_receive.go | 87 ++- os/gproc/gproc_comm_send.go | 69 +-- os/gproc/gproc_manager.go | 1 - os/gproc/gproc_process.go | 19 +- util/gconv/gconv.go | 18 +- util/gconv/gconv_slice.go | 510 ------------------ util/gconv/gconv_slice_any.go | 135 +++++ util/gconv/gconv_slice_float.go | 199 +++++++ util/gconv/gconv_slice_int.go | 310 +++++++++++ util/gconv/gconv_slice_str.go | 104 ++++ util/gconv/gconv_slice_uint.go | 295 ++++++++++ util/gconv/gconv_z_unit_struct_slice_test.go | 98 ++++ 30 files changed, 1664 insertions(+), 791 deletions(-) rename .example/os/gproc/{gproc2.go => gproc_kill.go} (67%) create mode 100644 .example/os/gproc/gproc_sleep.go delete mode 100644 .example/other/config.toml delete mode 100644 .example/other/terminal-color.go delete mode 100644 .example/other/try-catch.go create mode 100644 net/gtcp/gtcp_unit_init_test.go rename net/gtcp/{gtcp_unit_test.go => gtcp_unit_pkg_test.go} (96%) create mode 100644 net/gtcp/gtcp_unit_pool_pkg_test.go create mode 100644 net/gtcp/gtcp_unit_pool_test.go create mode 100644 util/gconv/gconv_slice_any.go create mode 100644 util/gconv/gconv_slice_float.go create mode 100644 util/gconv/gconv_slice_int.go create mode 100644 util/gconv/gconv_slice_str.go create mode 100644 util/gconv/gconv_slice_uint.go create mode 100644 util/gconv/gconv_z_unit_struct_slice_test.go diff --git a/.example/os/gproc/gproc_comm.go b/.example/os/gproc/gproc_comm.go index 88afde9fe..858cbf4c6 100644 --- a/.example/os/gproc/gproc_comm.go +++ b/.example/os/gproc/gproc_comm.go @@ -1,6 +1,3 @@ -// 多进程通信示例, -// 子进程每个1秒向父进程发送当前时间, -// 父进程监听进程消息,收到后打印到终端。 package main import ( diff --git a/.example/os/gproc/gproc2.go b/.example/os/gproc/gproc_kill.go similarity index 67% rename from .example/os/gproc/gproc2.go rename to .example/os/gproc/gproc_kill.go index 69464f6b4..978c0deda 100644 --- a/.example/os/gproc/gproc2.go +++ b/.example/os/gproc/gproc_kill.go @@ -5,9 +5,8 @@ import ( "github.com/gogf/gf/os/gproc" ) -// 使用gproc kill指定其他进程(清确保运行该程序的用户有足够权限) func main() { - pid := 14746 + pid := 32556 m := gproc.NewManager() m.AddProcess(pid) err := m.KillAll() diff --git a/.example/os/gproc/gproc_sleep.go b/.example/os/gproc/gproc_sleep.go new file mode 100644 index 000000000..35162ac56 --- /dev/null +++ b/.example/os/gproc/gproc_sleep.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gproc" +) + +func main() { + fmt.Println(gproc.Pid()) + err := gproc.ShellRun("sleep 99999s") + fmt.Println(err) +} diff --git a/.example/other/config.toml b/.example/other/config.toml deleted file mode 100644 index d94ab9d0e..000000000 --- a/.example/other/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[database] - debug = true - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local" diff --git a/.example/other/terminal-color.go b/.example/other/terminal-color.go deleted file mode 100644 index cb0f1dd63..000000000 --- a/.example/other/terminal-color.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("") - - // 前景 背景 颜色 - // --------------------------------------- - // 30 40 黑色 - // 31 41 红色 - // 32 42 绿色 - // 33 43 黄色 - // 34 44 蓝色 - // 35 45 紫红色 - // 36 46 青蓝色 - // 37 47 白色 - // - // 代码 意义 - // ------------------------- - // 0 终端默认设置 - // 1 高亮显示 - // 4 使用下划线 - // 5 闪烁 - // 7 反白显示 - // 8 不可见 - - // 背景色彩 = 40-47 - for b := 40; b <= 47; b++ { - // 前景色彩 = 30-37 - for f := 30; f <= 37; f++ { - // 显示方式 = 0,1,4,5,7,8 - for _, d := range []int{0, 1, 4, 5, 7, 8} { - fmt.Printf(" %c[%d;%d;%dm%s(f=%d,b=%d,d=%d)%c[0m ", 0x1B, d, b, f, "", f, b, d, 0x1B) - } - fmt.Println("") - } - fmt.Println("") - } -} diff --git a/.example/other/test.go b/.example/other/test.go index 3f26018b2..d5d677c24 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,18 +1,69 @@ package main import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" + "fmt" + "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/os/glog" ) func main() { - s := g.Server() - s.Group("/api.v2", func(group *ghttp.RouterGroup) { - group.ALL("/test", func(r *ghttp.Request) { - r.Response.Write(r.GetRequest("nickname")) - }) - }) - s.SetPort(8199) - s.Run() + type Detail struct { + Mall_coupon_id int64 `json:"mall_coupon_id"` //店铺优惠券id + Mall_coupon_discount_pct int `json:"mall_coupon_discount_pct"` //店铺折扣 + Mall_coupon_min_order_amount int `json:"mall_coupon_min_order_amount"` //最小使用金额 + Mall_coupon_max_discount_amount int `json:"mall_coupon_max_discount_amount"` //最大使用金额 + Mall_coupon_total_quantity int64 `json:"mall_coupon_total_quantity"` //店铺券总量 + Mall_coupon_remain_quantity int64 `json:"mall_coupon_remain_quantity"` //店铺券余量 + Mall_coupon_start_time int64 `json:"mall_coupon_start_time"` //店铺券使用开始时间 + Mall_coupon_end_time int64 `json:"mall_coupon_end_time"` //店铺券使用结束时间 + Goods_id int64 `json:"goods_id"` //参与多多进宝的商品ID + Goods_name string `json:"goods_name"` //参与多多进宝的商品标题 + Goods_desc string `json:"goods_desc"` //参与多多进宝的商品描述 + Goods_image_url string `json:"goods_image_url"` //多多进宝商品主图 + Goods_gallery_urls []string `json:"goods_gallery_urls"` //商品轮播图 + Min_group_price int64 `json:"min_group_price"` //最低价sku的拼团价,单位为分 + Min_normal_price int64 `json:"min_normal_price"` //最低价sku的单买价,单位为分 + Mall_name string `json:"mall_name"` //店铺名称 + Opt_id int64 `json:"opt_id"` //商品标签ID,使用pdd.goods.opt.get接口获取 + Opt_name string `json:"opt_name"` //商品标签名称 + Opt_ids []int `json:"opt_ids"` //商品标签ID + Cat_ids []int `json:"cat_ids"` //商品一~四级类目ID列表 + Coupon_min_order_amount int64 `json:"coupon_min_order_amount"` //优惠券门槛金额,单位为分 + Coupon_discount int64 `json:"coupon_discount"` //优惠券面额,单位为分 + Coupon_total_quantity int64 `json:"coupon_total_quantity"` //优惠券总数量 + Coupon_remain_quantity int64 `json:"coupon_remain_quantity"` //优惠券剩余数量 + Coupon_start_time int64 `json:"coupon_start_time"` //优惠券生效时间,UNIX时间戳 + Coupon_end_time int64 `json:"coupon_end_time"` //优惠券失效时间,UNIX时间戳 + Promotion_rate int64 `json:"promotion_rate"` //佣金比例,千分比 + Goods_eval_count int64 `json:"goods_eval_count"` //商品评价数 + Cat_id int64 `json:"cat_id"` //商品类目ID,使用pdd.goods.cats.get接口获取 + Sales_tip string `json:"sales_tip"` //已售卖件数 + Mall_id int64 `json:"mall_id"` //商家id + Service_tags []int64 `json:"service_tags"` // 服务标签: 4-送货入户并安装,5-送货入户,6-电子发票,9-坏果包赔 ... + Clt_cpn_batch_sn string `json:"clt_cpn_batch_sn"` //店铺收藏券id + Clt_cpn_start_time int64 `json:"clt_cpn_start_time"` //店铺收藏券起始时间 + Clt_cpn_end_time int64 `json:"clt_cpn_end_time"` //店铺收藏券截止时间 + Clt_cpn_quantity int64 `json:"clt_cpn_quantity"` //店铺收藏券总量 + Clt_cpn_remain_quantity int64 `json:"clt_cpn_remain_quantity"` //店铺收藏券剩余量 + Clt_cpn_discount int64 `json:"clt_cpn_discount"` //店铺收藏券面额,单位为分 + Clt_cpn_min_amt int64 `json:"clt_cpn_min_amt"` //店铺收藏券使用门槛价格,单位为分 + Desc_txt string `json:"desc_txt"` //描述分 + Serv_txt string `json:"serv_txt"` //服务分 + Lgst_txt string `json:"lgst_txt"` //物流分 + Plan_type int `json:"plan_type"` //推广计划类型 + Zs_duo_id int64 `json:"zs_duo_id"` //招商团长id + Only_scene_auth int `json:"only_scene_auth"` //快手专享 + } + s := `{"goods_detail_response":{"goods_details":[{"category_name":"百货","clt_cpn_end_time":null,"clt_cpn_min_amt":null,"coupon_remain_quantity":7000,"clt_cpn_remain_quantity":null,"promotion_rate":110,"coupon_id":3248825612,"service_tags":[24,13],"mall_id":983152922,"mall_name":"唯美之恋麻雀专卖店","mall_coupon_end_time":0,"clt_cpn_batch_sn":null,"lgst_txt":"高","goods_name":"【40卷32卷12卷】家用实惠卫生纸无芯卷纸批发纸巾厕纸卷筒纸手纸","clt_cpn_discount":null,"goods_id":9133961778,"goods_gallery_urls":["https://t00img.yangkeduo.com/goods/images/2019-08-18/e05846e8-f317-47bf-987d-5ccc0864e60d.jpg","https://t00img.yangkeduo.com/goods/images/2019-12-06/91756ca8-f001-4394-87d3-e7a67dc81d5a.jpg","https://t00img.yangkeduo.com/goods/images/2019-12-07/14c8abe5-cc27-4265-a683-7e2b089bb82e.jpg","https://t00img.yangkeduo.com/goods/images/2019-08-18/c4d338c0-1922-463f-bfcd-accd02018e44.jpg","https://t00img.yangkeduo.com/goods/images/2019-08-18/2b7f67f8-7738-4462-8f27-243594401ecd.jpg","https://t00img.yangkeduo.com/goods/images/2019-08-18/9b9bf86c-a3a7-4de1-9169-8b25c756ed01.jpg","https://t00img.yangkeduo.com/goods/images/2019-12-01/e373de68-c8a4-4752-adbc-d26b1040f8b7.jpg","https://t00img.yangkeduo.com/goods/images/2019-11-25/e622ef70-d917-4606-86af-a238db8c2fce.jpg","https://t00img.yangkeduo.com/goods/images/2019-11-25/9dd92cdd-1a07-4261-9584-cfafb0ddd421.jpg","https://t00img.yangkeduo.com/goods/images/2019-12-03/f7088db8-02f9-412b-a96c-5b452f41d9e3.jpg"],"goods_desc":"本产品原材料精选原生木浆制造,先进喷浆制造工艺,纸质更柔软细腻,没有纸屑,更有韧劲。嘉禾纸业建厂15年,两大自主品牌纸语和唯美之恋,年产能两万万余吨,拥有先进的造纸设备,采用巴西进口制浆制作制造而成,纸质柔软细腻,吸水性强,致力于打造国内孕婴卫生纸首选品牌,深受广大消费者好评。","opt_name":"百货","opt_ids":[17665,10116,10696,330,12619,12,11212,8590,10702,8591,15,13903,8592,17680,12691,22102,13911,12696,22105,21915,223,292,12581,13926,360,10730,12586,20527,11187,8569,12729,17657,8570,8571,10110,10111],"goods_image_url":"https://t00img.yangkeduo.com/goods/images/2019-08-18/e05846e8-f317-47bf-987d-5ccc0864e60d.jpg","has_mall_coupon":false,"min_group_price":720,"coupon_start_time":1575820800,"coupon_discount":200,"coupon_end_time":1575993599,"zs_duo_id":0,"mall_coupon_remain_quantity":0,"plan_type":2,"clt_cpn_quantity":null,"crt_rf_ordr_rto1m":0.03807085638781072,"cat_ids":[17285,17297,17402],"coupon_min_order_amount":200,"category_id":17657,"mall_coupon_discount_pct":0,"cat_id":null,"coupon_total_quantity":12000,"mall_coupon_min_order_amount":0,"merchant_type":4,"clt_cpn_start_time":null,"sales_tip":"4.2万","plan_type_all":4,"only_scene_auth":true,"mall_coupon_id":0,"desc_txt":"高","goods_thumbnail_url":"https://t00img.yangkeduo.com/goods/images/2019-11-20/0b0d078822c6e777ed1eb4ac2cdc930a.jpeg","opt_id":17657,"search_id":null,"min_normal_price":1190,"has_coupon":true,"mall_coupon_start_time":0,"serv_txt":"高","mall_rate":110,"mall_coupon_total_quantity":0,"create_at":null,"mall_coupon_max_discount_amount":0,"mall_cps":1}],"request_id":"15758638812810680"}}` + if j, err := gjson.DecodeToJson([]byte(s)); err != nil { + glog.Error(err) + } else { + + inter := j.GetInterfaces("goods_detail_response.goods_details.0") + fmt.Println("接口:", inter) + detail := Detail{} + err := j.GetStruct("goods_detail_response.goods_details.0", &detail) + fmt.Println("结构体:", err, detail) + } } diff --git a/.example/other/test2.go b/.example/other/test2.go index bda8f04e5..e37f7ca63 100644 --- a/.example/other/test2.go +++ b/.example/other/test2.go @@ -1,17 +1,17 @@ package main import ( - "github.com/gogf/gf/os/gproc" - "log" + "fmt" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gconv" ) func main() { - process := gproc.NewProcessCmd("go run test.go") - if pid, err := process.Start(); err != nil { - log.Print("Build failed:", err) - return - } else { - log.Printf("Build running: pid: %d", pid) + type User struct { + Scores []int64 } - + user := new(User) + err := gconv.Struct(g.Map{"scores": []interface{}{1, 2, 3}}, user) + fmt.Println(err) + fmt.Println(user) } diff --git a/.example/other/try-catch.go b/.example/other/try-catch.go deleted file mode 100644 index 9cf4169e0..000000000 --- a/.example/other/try-catch.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/glog" -) - -func main() { - g.TryCatch(func() { - glog.Println("hello") - g.Throw("exception") - glog.Println("world") - }) - - g.TryCatch(func() { - glog.Println("hello") - g.Throw("exception") - glog.Println("world") - }, func(exception interface{}) { - glog.Error(exception) - }) -} diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index a3b2ca342..c305e2235 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -74,6 +74,7 @@ type ServerConfig struct { FormParsingMemory int64 // Other: Max memory in bytes which can be used for parsing multimedia form. NameToUriType int // Other: Type for converting struct method name to URI when registering routes. DumpRouterMap bool // Other: Whether automatically dump router map when server starts. + Graceful bool // Other: Enable graceful reload feature for all servers of the process. } // defaultServerConfig is the default configuration object for server. @@ -108,6 +109,7 @@ var defaultServerConfig = ServerConfig{ DumpRouterMap: true, FormParsingMemory: 100 * 1024 * 1024, // 100MB Rewrites: make(map[string]string), + Graceful: true, } // Config returns the default ServerConfig object. @@ -140,6 +142,7 @@ func (s *Server) SetConfig(c ServerConfig) error { if c.TLSConfig == nil && c.HTTPSCertPath != "" { s.EnableHTTPS(c.HTTPSCertPath, c.HTTPSKeyPath) } + SetGraceful(c.Graceful) return nil } diff --git a/net/gtcp/gtcp_conn_pkg.go b/net/gtcp/gtcp_conn_pkg.go index 6890f5694..ba75fd374 100644 --- a/net/gtcp/gtcp_conn_pkg.go +++ b/net/gtcp/gtcp_conn_pkg.go @@ -161,13 +161,14 @@ func getPkgOption(option ...PkgOption) (*PkgOption, error) { case 3: pkgOption.MaxDataSize = 0xFFFFFF case 4: - pkgOption.MaxDataSize = 0xFFFFFFFF + // math.MaxInt32 not math.MaxUint32 + pkgOption.MaxDataSize = 0x7FFFFFFF } } - if pkgOption.MaxDataSize > 0xFFFFFFFF { + if pkgOption.MaxDataSize > 0x7FFFFFFF { return nil, fmt.Errorf( `package data size %d definition exceeds allowed max data size %d`, - pkgOption.MaxDataSize, 0xFFFFFFFF, + pkgOption.MaxDataSize, 0x7FFFFFFF, ) } return &pkgOption, nil diff --git a/net/gtcp/gtcp_pool.go b/net/gtcp/gtcp_pool.go index a681fe60e..51c143d02 100644 --- a/net/gtcp/gtcp_pool.go +++ b/net/gtcp/gtcp_pool.go @@ -23,7 +23,7 @@ type PoolConn struct { } const ( - gDEFAULT_POOL_EXPIRE = 30000 // (Millisecond) Default TTL for connection in the pool. + gDEFAULT_POOL_EXPIRE = 10000 // (Millisecond) Default TTL for connection in the pool. gCONN_STATUS_UNKNOWN = 0 // Means it is unknown it's connective or not. gCONN_STATUS_ACTIVE = 1 // Means it is now connective. gCONN_STATUS_ERROR = 2 // Means it should be closed and removed from pool. @@ -36,27 +36,18 @@ var ( // NewPoolConn creates and returns a connection with pool feature. func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { - var pool *gpool.Pool - if v := addressPoolMap.Get(addr); v == nil { - addressPoolMap.LockFunc(func(m map[string]interface{}) { - if v, ok := m[addr]; ok { - pool = v.(*gpool.Pool) + v := addressPoolMap.GetOrSetFuncLock(addr, func() interface{} { + var pool *gpool.Pool + pool = gpool.New(gDEFAULT_POOL_EXPIRE, func() (interface{}, error) { + if conn, err := NewConn(addr, timeout...); err == nil { + return &PoolConn{conn, pool, gCONN_STATUS_ACTIVE}, nil } else { - pool = gpool.New(gDEFAULT_POOL_EXPIRE, func() (interface{}, error) { - if conn, err := NewConn(addr, timeout...); err == nil { - return &PoolConn{conn, pool, gCONN_STATUS_ACTIVE}, nil - } else { - return nil, err - } - }) - m[addr] = pool + return nil, err } }) - } else { - pool = v.(*gpool.Pool) - } - - if v, err := pool.Get(); err == nil { + return pool + }) + if v, err := v.(*gpool.Pool).Get(); err == nil { return v.(*PoolConn), nil } else { return nil, err @@ -65,6 +56,9 @@ func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { // Close puts back the connection to the pool if it's active, // or closes the connection if it's not active. +// +// Note that, if calls Close function closing itself, should not +// be used again. func (c *PoolConn) Close() error { if c.pool != nil && c.status == gCONN_STATUS_ACTIVE { c.status = gCONN_STATUS_UNKNOWN @@ -78,8 +72,8 @@ func (c *PoolConn) Close() error { // Send writes data to the connection. It retrieves a new connection from its pool if it fails // writing data. func (c *PoolConn) Send(data []byte, retry ...Retry) error { - var err error - if err = c.Conn.Send(data, retry...); err != nil && c.status == gCONN_STATUS_UNKNOWN { + err := c.Conn.Send(data, retry...) + if err != nil && c.status == gCONN_STATUS_UNKNOWN { if v, e := c.pool.Get(); e == nil { c.Conn = v.(*PoolConn).Conn err = c.Send(data, retry...) diff --git a/net/gtcp/gtcp_unit_init_test.go b/net/gtcp/gtcp_unit_init_test.go new file mode 100644 index 000000000..47d002af4 --- /dev/null +++ b/net/gtcp/gtcp_unit_init_test.go @@ -0,0 +1,21 @@ +// 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 gtcp_test + +import ( + "github.com/gogf/gf/container/garray" +) + +var ( + ports = garray.NewIntArray(true) +) + +func init() { + for i := 9000; i <= 10000; i++ { + ports.Append(i) + } +} diff --git a/net/gtcp/gtcp_unit_test.go b/net/gtcp/gtcp_unit_pkg_test.go similarity index 96% rename from net/gtcp/gtcp_unit_test.go rename to net/gtcp/gtcp_unit_pkg_test.go index b4e8b1417..4e127b763 100644 --- a/net/gtcp/gtcp_unit_test.go +++ b/net/gtcp/gtcp_unit_pkg_test.go @@ -8,7 +8,6 @@ package gtcp_test import ( "fmt" - "github.com/gogf/gf/container/garray" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/util/gconv" @@ -16,16 +15,6 @@ import ( "time" ) -var ( - ports = garray.NewIntArray(true) -) - -func init() { - for i := 9000; i <= 10000; i++ { - ports.Append(i) - } -} - func Test_Package_Basic(t *testing.T) { p := ports.PopRand() s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { diff --git a/net/gtcp/gtcp_unit_pool_pkg_test.go b/net/gtcp/gtcp_unit_pool_pkg_test.go new file mode 100644 index 000000000..7bca03026 --- /dev/null +++ b/net/gtcp/gtcp_unit_pool_pkg_test.go @@ -0,0 +1,172 @@ +// 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 gtcp_test + +import ( + "fmt" + "github.com/gogf/gf/net/gtcp" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gconv" + "testing" + "time" +) + +func Test_Pool_Package_Basic(t *testing.T) { + p := ports.PopRand() + s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { + defer conn.Close() + for { + data, err := conn.RecvPkg() + if err != nil { + break + } + conn.SendPkg(data) + } + }) + go s.Run() + defer s.Close() + time.Sleep(100 * time.Millisecond) + // SendPkg + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + for i := 0; i < 100; i++ { + err := conn.SendPkg([]byte(gconv.String(i))) + gtest.Assert(err, nil) + } + for i := 0; i < 100; i++ { + err := conn.SendPkgWithTimeout([]byte(gconv.String(i)), time.Second) + gtest.Assert(err, nil) + } + }) + // SendPkg with big data - failure. + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := make([]byte, 65536) + err = conn.SendPkg(data) + gtest.AssertNE(err, nil) + }) + // SendRecvPkg + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + for i := 100; i < 200; i++ { + data := []byte(gconv.String(i)) + result, err := conn.SendRecvPkg(data) + gtest.Assert(err, nil) + gtest.Assert(result, data) + } + for i := 100; i < 200; i++ { + data := []byte(gconv.String(i)) + result, err := conn.SendRecvPkgWithTimeout(data, time.Second) + gtest.Assert(err, nil) + gtest.Assert(result, data) + } + }) + // SendRecvPkg with big data - failure. + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := make([]byte, 65536) + result, err := conn.SendRecvPkg(data) + gtest.AssertNE(err, nil) + gtest.Assert(result, nil) + }) + // SendRecvPkg with big data - success. + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := make([]byte, 65500) + data[100] = byte(65) + data[65400] = byte(85) + result, err := conn.SendRecvPkg(data) + gtest.Assert(err, nil) + gtest.Assert(result, data) + }) +} + +func Test_Pool_Package_Timeout(t *testing.T) { + p := ports.PopRand() + s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { + defer conn.Close() + for { + data, err := conn.RecvPkg() + if err != nil { + break + } + time.Sleep(time.Second) + gtest.Assert(conn.SendPkg(data), nil) + } + }) + go s.Run() + defer s.Close() + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := []byte("10000") + result, err := conn.SendRecvPkgWithTimeout(data, time.Millisecond*500) + gtest.AssertNE(err, nil) + gtest.Assert(result, nil) + }) + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := []byte("10000") + result, err := conn.SendRecvPkgWithTimeout(data, time.Second*2) + gtest.Assert(err, nil) + gtest.Assert(result, data) + }) +} + +func Test_Pool_Package_Option(t *testing.T) { + p := ports.PopRand() + s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { + defer conn.Close() + option := gtcp.PkgOption{HeaderSize: 1} + for { + data, err := conn.RecvPkg(option) + if err != nil { + break + } + gtest.Assert(conn.SendPkg(data, option), nil) + } + }) + go s.Run() + defer s.Close() + time.Sleep(100 * time.Millisecond) + // SendRecvPkg with big data - failure. + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := make([]byte, 0xFF+1) + result, err := conn.SendRecvPkg(data, gtcp.PkgOption{HeaderSize: 1}) + gtest.AssertNE(err, nil) + gtest.Assert(result, nil) + }) + // SendRecvPkg with big data - success. + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := make([]byte, 0xFF) + data[100] = byte(65) + data[200] = byte(85) + result, err := conn.SendRecvPkg(data, gtcp.PkgOption{HeaderSize: 1}) + gtest.Assert(err, nil) + gtest.Assert(result, data) + }) +} diff --git a/net/gtcp/gtcp_unit_pool_test.go b/net/gtcp/gtcp_unit_pool_test.go new file mode 100644 index 000000000..b9dc901f2 --- /dev/null +++ b/net/gtcp/gtcp_unit_pool_test.go @@ -0,0 +1,65 @@ +// 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 gtcp_test + +import ( + "fmt" + "github.com/gogf/gf/net/gtcp" + "github.com/gogf/gf/test/gtest" + "testing" + "time" +) + +func Test_Pool_Basic1(t *testing.T) { + p := ports.PopRand() + s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { + defer conn.Close() + for { + data, err := conn.RecvPkg() + if err != nil { + break + } + conn.SendPkg(data) + } + }) + go s.Run() + defer s.Close() + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := []byte("9999") + err = conn.SendPkg(data) + gtest.Assert(err, nil) + err = conn.SendPkgWithTimeout(data, time.Second) + gtest.Assert(err, nil) + }) +} + +func Test_Pool_Basic2(t *testing.T) { + p := ports.PopRand() + s := gtcp.NewServer(fmt.Sprintf(`:%d`, p), func(conn *gtcp.Conn) { + conn.Close() + }) + go s.Run() + defer s.Close() + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", p)) + gtest.Assert(err, nil) + defer conn.Close() + data := []byte("9999") + err = conn.SendPkg(data) + gtest.Assert(err, nil) + err = conn.SendPkgWithTimeout(data, time.Second) + gtest.Assert(err, nil) + + _, err = conn.SendRecv(data, -1) + gtest.AssertNE(err, nil) + }) +} diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index 52debd0f1..e693dfd1a 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -36,8 +36,16 @@ var ( // The absolute file path for main package. // It can be only checked and set once. mainPkgPath = gtype.NewString() + // Temporary directory of system. + tempDir = "/tmp" ) +func init() { + if !Exists(tempDir) { + tempDir = os.TempDir() + } +} + // Mkdir creates directories recursively with given . // The parameter is suggested to be absolute path. func Mkdir(path string) error { @@ -511,5 +519,5 @@ func homeWindows() (string, error) { // See os.TempDir(). func TempDir() string { - return os.TempDir() + return tempDir } diff --git a/os/gproc/gproc.go b/os/gproc/gproc.go index 3533f8cfe..ddea9931c 100644 --- a/os/gproc/gproc.go +++ b/os/gproc/gproc.go @@ -9,6 +9,7 @@ package gproc import ( "bytes" + "github.com/gogf/gf/text/gstr" "io" "os" "runtime" @@ -21,15 +22,14 @@ import ( const ( gPROC_ENV_KEY_PPID_KEY = "GPROC_PPID" - gPROC_TEMP_DIR_ENV_KEY = "GPROC_TEMP_DIR" ) var ( - // 进程开始执行时间 + // processStartTime is the start time of current process. processStartTime = time.Now() ) -// 获取当前进程ID +// Pid returns the pid of current process. func Pid() int { return os.Getpid() } @@ -39,7 +39,6 @@ func PPid() int { if !IsChild() { return Pid() } - // gPROC_ENV_KEY_PPID_KEY为gproc包自定义的父进程 ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY) if ppidValue != "" && ppidValue != "0" { return gconv.Int(ppidValue) @@ -114,11 +113,11 @@ func checkEnvKey(env []string, key string) bool { func getShell() string { switch runtime.GOOS { case "windows": - return searchBinFromEnvPath("cmd.exe") + return SearchBinary("cmd.exe") default: - path := searchBinFromEnvPath("bash") + path := SearchBinary("bash") if path == "" { - path = searchBinFromEnvPath("sh") + path = SearchBinary("sh") } return path } @@ -135,24 +134,25 @@ func getShellOption() string { } // 从环境变量PATH中搜索可执行文件 -func searchBinFromEnvPath(file string) string { - // 如果是绝对路径,或者相对路径下存在,那么直接返回 +func SearchBinary(file string) string { + // Check if it's absolute path of exists at current working directory. if gfile.Exists(file) { return file } array := ([]string)(nil) switch runtime.GOOS { case "windows": - array = strings.Split(os.Getenv("Path"), ";") + array = gstr.SplitAndTrim(os.Getenv("Path"), ";") if gfile.Ext(file) != ".exe" { file += ".exe" } default: - array = strings.Split(os.Getenv("PATH"), ":") + array = gstr.SplitAndTrim(os.Getenv("PATH"), ":") } if len(array) > 0 { + path := "" for _, v := range array { - path := v + gfile.Separator + file + path = v + gfile.Separator + file if gfile.Exists(path) { return path } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 78de5a177..977800f49 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -3,42 +3,81 @@ // 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 gproc import ( - "os" - + "errors" + "fmt" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/net/gtcp" + "github.com/gogf/gf/os/gfcache" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" ) -// 进程通信数据结构 -type Msg struct { - SendPid int `json:"spid"` // 发送进程ID - RecvPid int `json:"rpid"` // 接收进程ID - Group string `json:"group"` // 分组名称 - Data []byte `json:"data"` // 原始数据 +// MsgRequest is the request structure for process communication. +type MsgRequest struct { + SendPid int // Sender PID. + RecvPid int // Receiver PID. + Group string // Message group name. + Data []byte // Request data. } -// 本地进程通信接收消息队列(按照分组进行构建的map,键值为*gqueue.Queue对象) -var commReceiveQueues = gmap.NewStrAnyMap(true) - -// (用于发送)已建立的PID对应的Conn通信对象,键值为一个Pool,防止并行使用同一个通信对象造成数据重叠 -var commPidConnMap = gmap.NewIntAnyMap(true) - -// 获取指定进程的通信文件地址 -func getCommFilePath(pid int) string { - return getCommDirPath() + gfile.Separator + gconv.String(pid) +// MsgResponse is the response structure for process communication. +type MsgResponse struct { + Code int // 1: OK; Other: Error. + Message string // Response message. + Data []byte // Response data. } -// 获取进程间通信目录地址 -func getCommDirPath() string { - tempDir := os.Getenv(gPROC_TEMP_DIR_ENV_KEY) - if tempDir == "" { - tempDir = gfile.TempDir() +const ( + gPROC_COMM_DEFAULT_GRUOP_NAME = "" // Default group name. + gPROC_DEFAULT_TCP_PORT = 10000 // Starting port number for receiver listening. + gPROC_MSG_QUEUE_MAX_LENGTH = 10000 // Max size for each message queue of the group. +) + +var ( + // commReceiveQueues is the group name to queue map for storing received data. + // The value of the map is type of *gqueue.Queue. + commReceiveQueues = gmap.NewStrAnyMap(true) + + // commPidFolderPath specifies the folder path storing pid to port mapping files. + commPidFolderPath = gfile.Join(gfile.TempDir(), "gproc") +) + +func init() { + // Automatically create the storage folder. + if !gfile.Exists(commPidFolderPath) { + err := gfile.Mkdir(commPidFolderPath) + if err != nil { + panic(fmt.Errorf(`create gproc folder failed: %v`, err)) + } } - return tempDir + gfile.Separator + "gproc" +} + +// getConnByPid creates and returns a TCP connection for specified pid. +func getConnByPid(pid int) (*gtcp.Conn, error) { + port := getPortByPid(pid) + if port > 0 { + if conn, err := gtcp.NewConn(fmt.Sprintf("127.0.0.1:%d", port)); err == nil { + return conn, nil + } else { + return nil, err + } + } + return nil, errors.New(fmt.Sprintf("could not find port for pid: %d", pid)) +} + +// getPortByPid returns the listening port for specified pid. +// It returns 0 if no port found for the specified pid. +func getPortByPid(pid int) int { + path := getCommFilePath(pid) + content := gfcache.GetContents(path) + return gconv.Int(content) +} + +// getCommFilePath returns the pid to port mapping file path for given pid. +func getCommFilePath(pid int) string { + return gfile.Join(commPidFolderPath, gconv.String(pid)) } diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index ce4887910..930570eb7 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -21,50 +21,39 @@ import ( "github.com/gogf/gf/util/gconv" ) -const ( - gPROC_DEFAULT_TCP_PORT = 10000 // 默认开始监听的TCP端口号,如果占用则递增 - gPROC_MSG_QUEUE_MAX_LENGTH = 10000 // 进程消息队列最大长度(每个分组) -) - var ( - // 是否已开启TCP端口监听服务 + // tcpListened marks whether the receiving listening service started. tcpListened = gtype.NewBool() ) -// 获取其他进程传递到当前进程的消息包,阻塞执行。 -// 进程只有在执行该方法后才会打开请求端口,默认情况下不允许进程间通信。 -func Receive(group ...string) *Msg { - // 一个进程只能开启一个监听goroutine - if !tcpListened.Val() && tcpListened.Set(true) == false { - go startTcpListening() +// Receive blocks and receives message from other process using local TCP listening. +// Note that, it only enables the TCP listening service when this function called. +func Receive(group ...string) *MsgRequest { + // Use atomic operations to guarantee only one receiver goroutine listening. + if tcpListened.Cas(false, true) { + go receiveTcpListening() } - queue := (*gqueue.Queue)(nil) - groupName := gPROC_COMM_DEFAULT_GRUOP_NAME + var groupName string if len(group) > 0 { groupName = group[0] - } - if v := commReceiveQueues.Get(groupName); v == nil { - commReceiveQueues.LockFunc(func(m map[string]interface{}) { - if v, ok := m[groupName]; ok { - queue = v.(*gqueue.Queue) - } else { - queue = gqueue.New(gPROC_MSG_QUEUE_MAX_LENGTH) - m[groupName] = queue - } - }) } else { - queue = v.(*gqueue.Queue) + groupName = gPROC_COMM_DEFAULT_GRUOP_NAME } + queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} { + return gqueue.New(gPROC_MSG_QUEUE_MAX_LENGTH) + }).(*gqueue.Queue) + // Blocking receiving. if v := queue.Pop(); v != nil { - return v.(*Msg) + return v.(*MsgRequest) } return nil } -// 创建本地进程TCP通信服务 -func startTcpListening() { +// receiveTcpListening scans local for available port and starts listening. +func receiveTcpListening() { var listen *net.TCPListener + // Scan the available port for listening. for i := gPROC_DEFAULT_TCP_PORT; ; i++ { addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", i)) if err != nil { @@ -74,57 +63,55 @@ func startTcpListening() { if err != nil { continue } - // 将监听的端口保存到通信文件中(字符串类型存放) + // Save the port to the pid file. if err := gfile.PutContents(getCommFilePath(Pid()), gconv.String(i)); err != nil { - glog.Error(err) + glog.Panic(err) } break } + // Start listening. for { if conn, err := listen.Accept(); err != nil { glog.Error(err) } else if conn != nil { - go tcpServiceHandler(gtcp.NewConnByNetConn(conn)) + go receiveTcpHandler(gtcp.NewConnByNetConn(conn)) } } } -// TCP数据通信处理回调函数 -func tcpServiceHandler(conn *gtcp.Conn) { - option := gtcp.PkgOption{ - Retry: gtcp.Retry{ - Count: 3, - Interval: 10, - }, - } +// receiveTcpHandler is the connection handler for receiving data. +func receiveTcpHandler(conn *gtcp.Conn) { + var result []byte + var response MsgResponse for { - var result []byte - buffer, err := conn.RecvPkg(option) + buffer, err := conn.RecvPkg() if len(buffer) > 0 { - msg := new(Msg) + msg := new(MsgRequest) if err := json.Unmarshal(buffer, msg); err != nil { glog.Error(err) continue } if v := commReceiveQueues.Get(msg.Group); v == nil { - result = []byte(fmt.Sprintf("group [%s] does not exist", msg.Group)) - break + response.Code = 0 + response.Message = fmt.Sprintf("group [%s] does not exist", msg.Group) } else { - result = []byte("ok") - if v := commReceiveQueues.Get(msg.Group); v != nil { - v.(*gqueue.Queue).Push(msg) - } + response.Code = 1 + v.(*gqueue.Queue).Push(msg) } } if err == nil { - if err := conn.SendPkg(result, option); err != nil { + result, err = json.Marshal(response) + if err != nil { + glog.Error(err) + } + if err := conn.SendPkg(result); err != nil { glog.Error(err) } } else { if err := conn.Close(); err != nil { glog.Error(err) } - return + break } } } diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 70f84985a..8ad9fc1e6 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -7,28 +7,15 @@ package gproc import ( - "bytes" "encoding/json" "errors" - "fmt" - "io" - "time" - "github.com/gogf/gf/net/gtcp" - "github.com/gogf/gf/os/gfcache" - "github.com/gogf/gf/util/gconv" + "io" ) -const ( - gPROC_COMM_FAILURE_RETRY_COUNT = 3 // 失败重试次数 - gPROC_COMM_FAILURE_RETRY_TIMEOUT = 1000 // (毫秒)失败重试间隔 - gPROC_COMM_SEND_TIMEOUT = 5000 // (毫秒)发送超时时间 - gPROC_COMM_DEFAULT_GRUOP_NAME = "" // 默认分组名称 -) - -// 向指定gproc进程发送数据. +// Send sends data to specified process of given pid. func Send(pid int, data []byte, group ...string) error { - msg := Msg{ + msg := MsgRequest{ SendPid: Pid(), RecvPid: pid, Group: gPROC_COMM_DEFAULT_GRUOP_NAME, @@ -41,49 +28,27 @@ func Send(pid int, data []byte, group ...string) error { if err != nil { return err } - var buf []byte - var conn *gtcp.PoolConn - // 循环获取连接TCP对象 - for i := gPROC_COMM_FAILURE_RETRY_COUNT; i > 0; i-- { - if conn, err = getConnByPid(pid); err == nil { - break - } - time.Sleep(gPROC_COMM_FAILURE_RETRY_TIMEOUT * time.Millisecond) - } - if conn == nil { + var conn *gtcp.Conn + conn, err = getConnByPid(pid) + if err != nil { return err } defer conn.Close() - // 执行数据发送 - buf, err = conn.SendRecvPkgWithTimeout(msgBytes, gPROC_COMM_SEND_TIMEOUT*time.Millisecond) - if len(buf) > 0 { - if !bytes.EqualFold(buf, []byte("ok")) { - err = errors.New(string(buf)) + // Do the sending. + var result []byte + result, err = conn.SendRecvPkg(msgBytes) + if len(result) > 0 { + response := new(MsgResponse) + err = json.Unmarshal(result, response) + if err == nil { + if response.Code != 1 { + err = errors.New(response.Message) + } } } - // EOF不算异常错误 + // EOF is not really an error. if err == io.EOF { err = nil } return err } - -// 获取指定进程的TCP通信对象 -func getConnByPid(pid int) (*gtcp.PoolConn, error) { - port := getPortByPid(pid) - if port > 0 { - if conn, err := gtcp.NewPoolConn(fmt.Sprintf("127.0.0.1:%d", port)); err == nil { - return conn, nil - } else { - return nil, err - } - } - return nil, errors.New(fmt.Sprintf("could not find port for pid: %d", pid)) -} - -// 获取指定进程监听的端口号 -func getPortByPid(pid int) int { - path := getCommFilePath(pid) - content := gfcache.GetContents(path) - return gconv.Int(content) -} diff --git a/os/gproc/gproc_manager.go b/os/gproc/gproc_manager.go index 841fceb72..49e11c055 100644 --- a/os/gproc/gproc_manager.go +++ b/os/gproc/gproc_manager.go @@ -4,7 +4,6 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// 进程管理. package gproc import ( diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 9cc6b86b0..a9c0abc54 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -25,15 +25,12 @@ type Process struct { func NewProcess(path string, args []string, environment ...[]string) *Process { var env []string if len(environment) > 0 { - env = make([]string, 0) - for _, v := range environment[0] { - env = append(env, v) - } + env = make([]string, len(environment[0])) + copy(env, environment[0]) } else { env = os.Environ() } - env = append(env, fmt.Sprintf("%s=%s", gPROC_TEMP_DIR_ENV_KEY, os.TempDir())) - p := &Process{ + process := &Process{ Manager: nil, PPid: os.Getpid(), Cmd: exec.Cmd{ @@ -46,18 +43,16 @@ func NewProcess(path string, args []string, environment ...[]string) *Process { ExtraFiles: make([]*os.File, 0), }, } - // 当前工作目录 - if d, err := os.Getwd(); err == nil { - p.Dir = d - } + process.Dir, _ = os.Getwd() if len(args) > 0 { + // Exclude of current binary path. start := 0 if strings.EqualFold(path, args[0]) { start = 1 } - p.Args = append(p.Args, args[start:]...) + process.Args = append(process.Args, args[start:]...) } - return p + return process } // NewProcessCmd creates and returns a process with given command and optional environment variable array. diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index d33010369..491f4e7bf 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -49,7 +49,7 @@ var ( ) // Convert converts the variable to the type , the type is specified by string. -// The unnecessary parameter is used for additional parameter passing. +// The optional parameter is used for additional parameter passing. func Convert(i interface{}, t string, params ...interface{}) interface{} { switch t { case "int": @@ -84,6 +84,20 @@ func Convert(i interface{}, t string, params ...interface{}) interface{} { return Bytes(i) case "[]int": return Ints(i) + case "[]int32": + return Int32s(i) + case "[]int64": + return Int64s(i) + case "[]uint": + return Uints(i) + case "[]uint32": + return Uint32s(i) + case "[]uint64": + return Uint64s(i) + case "[]float32": + return Float32s(i) + case "[]float64": + return Float64s(i) case "[]string": return Strings(i) @@ -188,8 +202,6 @@ func String(i interface{}) string { return value case []byte: return string(value) - case []rune: - return string(value) case *time.Time: if value == nil { return "" diff --git a/util/gconv/gconv_slice.go b/util/gconv/gconv_slice.go index 125065fb5..fb773bb65 100644 --- a/util/gconv/gconv_slice.go +++ b/util/gconv/gconv_slice.go @@ -10,35 +10,8 @@ import ( "errors" "fmt" "reflect" - - "github.com/gogf/gf/internal/utilstr" ) -// SliceInt is alias of Ints. -func SliceInt(i interface{}) []int { - return Ints(i) -} - -// SliceUint is alias of Uints. -func SliceUint(i interface{}) []uint { - return Uints(i) -} - -// SliceStr is alias of Strings. -func SliceStr(i interface{}) []string { - return Strings(i) -} - -// SliceAny is alias of Interfaces. -func SliceAny(i interface{}) []interface{} { - return Interfaces(i) -} - -// SliceFloat is alias of Floats. -func SliceFloat(i interface{}) []float64 { - return Floats(i) -} - // SliceMap is alias of Maps. func SliceMap(i interface{}) []map[string]interface{} { return Maps(i) @@ -59,489 +32,6 @@ func SliceStructDeep(params interface{}, pointer interface{}, mapping ...map[str return StructsDeep(params, pointer, mapping...) } -// Ints converts to []int. -func Ints(i interface{}) []int { - if i == nil { - return nil - } - if r, ok := i.([]int); ok { - return r - } else { - var array []int - switch value := i.(type) { - case []string: - array = make([]int, len(value)) - for k, v := range value { - array[k] = Int(v) - } - case []int8: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []int16: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []int32: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []int64: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []uint: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []uint8: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []uint16: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []uint32: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []uint64: - array = make([]int, len(value)) - for k, v := range value { - array[k] = int(v) - } - case []bool: - array = make([]int, len(value)) - for k, v := range value { - if v { - array[k] = 1 - } else { - array[k] = 0 - } - } - case []float32: - array = make([]int, len(value)) - for k, v := range value { - array[k] = Int(v) - } - case []float64: - array = make([]int, len(value)) - for k, v := range value { - array[k] = Int(v) - } - case []interface{}: - array = make([]int, len(value)) - for k, v := range value { - array[k] = Int(v) - } - case [][]byte: - array = make([]int, len(value)) - for k, v := range value { - array[k] = Int(v) - } - default: - return []int{Int(i)} - } - return array - } -} - -// Uints converts to []uint. -func Uints(i interface{}) []uint { - if i == nil { - return nil - } - if r, ok := i.([]uint); ok { - return r - } else { - var array []uint - switch value := i.(type) { - case []string: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = Uint(v) - } - case []int8: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []int16: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []int32: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []int64: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []uint8: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []uint16: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []uint32: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []uint64: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = uint(v) - } - case []bool: - array = make([]uint, len(value)) - for k, v := range value { - if v { - array[k] = 1 - } else { - array[k] = 0 - } - } - case []float32: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = Uint(v) - } - case []float64: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = Uint(v) - } - case []interface{}: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = Uint(v) - } - case [][]byte: - array = make([]uint, len(value)) - for k, v := range value { - array[k] = Uint(v) - } - default: - return []uint{Uint(i)} - } - return array - } -} - -// Strings converts to []string. -func Strings(i interface{}) []string { - if i == nil { - return nil - } - if r, ok := i.([]string); ok { - return r - } else { - var array []string - switch value := i.(type) { - case []int: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []int8: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []int16: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []int32: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []int64: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []uint: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []uint8: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []uint16: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []uint32: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []uint64: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []bool: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []float32: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []float64: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case []interface{}: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - case [][]byte: - array = make([]string, len(value)) - for k, v := range value { - array[k] = String(v) - } - default: - return []string{String(i)} - } - return array - } -} - -// Strings converts to []float64. -func Floats(i interface{}) []float64 { - if i == nil { - return nil - } - if r, ok := i.([]float64); ok { - return r - } else { - var array []float64 - switch value := i.(type) { - case []string: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []int: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []int8: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []int16: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []int32: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []int64: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []uint: - for _, v := range value { - array = append(array, Float64(v)) - } - case []uint8: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []uint16: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []uint32: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []uint64: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []bool: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []float32: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - case []interface{}: - array = make([]float64, len(value)) - for k, v := range value { - array[k] = Float64(v) - } - default: - return []float64{Float64(i)} - } - return array - } -} - -// Type assert api for Interfaces. -type apiInterfaces interface { - Interfaces() []interface{} -} - -// Interfaces converts to []interface{}. -func Interfaces(i interface{}) []interface{} { - if i == nil { - return nil - } - if r, ok := i.([]interface{}); ok { - return r - } else if r, ok := i.(apiInterfaces); ok { - return r.Interfaces() - } else { - var array []interface{} - switch value := i.(type) { - case []string: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []int: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []int8: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []int16: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []int32: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []int64: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []uint: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []uint8: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []uint16: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []uint32: - for _, v := range value { - array = append(array, v) - } - case []uint64: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []bool: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []float32: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - case []float64: - array = make([]interface{}, len(value)) - for k, v := range value { - array[k] = v - } - default: - // Finally we use reflection. - rv := reflect.ValueOf(i) - kind := rv.Kind() - for kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() - } - switch kind { - case reflect.Slice, reflect.Array: - array = make([]interface{}, rv.Len()) - for i := 0; i < rv.Len(); i++ { - array[i] = rv.Index(i).Interface() - } - case reflect.Struct: - rt := rv.Type() - array = make([]interface{}, 0) - for i := 0; i < rv.NumField(); i++ { - // Only public attributes. - if !utilstr.IsLetterUpper(rt.Field(i).Name[0]) { - continue - } - array = append(array, rv.Field(i).Interface()) - } - default: - return []interface{}{i} - } - } - return array - } -} - // Maps converts to []map[string]interface{}. func Maps(value interface{}, tags ...string) []map[string]interface{} { if value == nil { diff --git a/util/gconv/gconv_slice_any.go b/util/gconv/gconv_slice_any.go new file mode 100644 index 000000000..1bce42c75 --- /dev/null +++ b/util/gconv/gconv_slice_any.go @@ -0,0 +1,135 @@ +// 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 gconv + +import ( + "github.com/gogf/gf/internal/utilstr" + "reflect" +) + +// SliceAny is alias of Interfaces. +func SliceAny(i interface{}) []interface{} { + return Interfaces(i) +} + +// Type assert api for Interfaces. +type apiInterfaces interface { + Interfaces() []interface{} +} + +// Interfaces converts to []interface{}. +func Interfaces(i interface{}) []interface{} { + if i == nil { + return nil + } + if r, ok := i.([]interface{}); ok { + return r + } else if r, ok := i.(apiInterfaces); ok { + return r.Interfaces() + } else { + var array []interface{} + switch value := i.(type) { + case []string: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []int: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []int8: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []int16: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []int32: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []int64: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []uint: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []uint8: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []uint16: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []uint32: + for _, v := range value { + array = append(array, v) + } + case []uint64: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []bool: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []float32: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + case []float64: + array = make([]interface{}, len(value)) + for k, v := range value { + array[k] = v + } + default: + // Finally we use reflection. + rv := reflect.ValueOf(i) + kind := rv.Kind() + for kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Slice, reflect.Array: + array = make([]interface{}, rv.Len()) + for i := 0; i < rv.Len(); i++ { + array[i] = rv.Index(i).Interface() + } + case reflect.Struct: + rt := rv.Type() + array = make([]interface{}, 0) + for i := 0; i < rv.NumField(); i++ { + // Only public attributes. + if !utilstr.IsLetterUpper(rt.Field(i).Name[0]) { + continue + } + array = append(array, rv.Field(i).Interface()) + } + default: + return []interface{}{i} + } + } + return array + } +} diff --git a/util/gconv/gconv_slice_float.go b/util/gconv/gconv_slice_float.go new file mode 100644 index 000000000..fcf039a77 --- /dev/null +++ b/util/gconv/gconv_slice_float.go @@ -0,0 +1,199 @@ +// 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 gconv + +// SliceFloat is alias of Floats. +func SliceFloat(i interface{}) []float64 { + return Floats(i) +} + +// SliceFloat32 is alias of Float32s. +func SliceFloat32(i interface{}) []float32 { + return Float32s(i) +} + +// SliceFloat64 is alias of Float64s. +func SliceFloat64(i interface{}) []float64 { + return Floats(i) +} + +// Floats converts to []float64. +func Floats(i interface{}) []float64 { + return Float64s(i) +} + +// Float32s converts to []float32. +func Float32s(i interface{}) []float32 { + if i == nil { + return nil + } + if r, ok := i.([]float32); ok { + return r + } else { + var array []float32 + switch value := i.(type) { + case []string: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []int: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []int8: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []int16: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []int32: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []int64: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []uint: + for _, v := range value { + array = append(array, Float32(v)) + } + case []uint8: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []uint16: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []uint32: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []uint64: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []bool: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []float64: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + case []interface{}: + array = make([]float32, len(value)) + for k, v := range value { + array[k] = Float32(v) + } + default: + return []float32{Float32(i)} + } + return array + } +} + +// Float64s converts to []float64. +func Float64s(i interface{}) []float64 { + if i == nil { + return nil + } + if r, ok := i.([]float64); ok { + return r + } else { + var array []float64 + switch value := i.(type) { + case []string: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []int: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []int8: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []int16: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []int32: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []int64: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []uint: + for _, v := range value { + array = append(array, Float64(v)) + } + case []uint8: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []uint16: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []uint32: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []uint64: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []bool: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []float32: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + case []interface{}: + array = make([]float64, len(value)) + for k, v := range value { + array[k] = Float64(v) + } + default: + return []float64{Float64(i)} + } + return array + } +} diff --git a/util/gconv/gconv_slice_int.go b/util/gconv/gconv_slice_int.go new file mode 100644 index 000000000..a6bb81567 --- /dev/null +++ b/util/gconv/gconv_slice_int.go @@ -0,0 +1,310 @@ +// 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 gconv + +// SliceInt is alias of Ints. +func SliceInt(i interface{}) []int { + return Ints(i) +} + +// SliceInt32 is alias of Int32s. +func SliceInt32(i interface{}) []int32 { + return Int32s(i) +} + +// SliceInt is alias of Int64s. +func SliceInt64(i interface{}) []int64 { + return Int64s(i) +} + +// Ints converts to []int. +func Ints(i interface{}) []int { + if i == nil { + return nil + } + if r, ok := i.([]int); ok { + return r + } else { + var array []int + switch value := i.(type) { + case []string: + array = make([]int, len(value)) + for k, v := range value { + array[k] = Int(v) + } + case []int8: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []int16: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []int32: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []int64: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []uint: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []uint8: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []uint16: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []uint32: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []uint64: + array = make([]int, len(value)) + for k, v := range value { + array[k] = int(v) + } + case []bool: + array = make([]int, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]int, len(value)) + for k, v := range value { + array[k] = Int(v) + } + case []float64: + array = make([]int, len(value)) + for k, v := range value { + array[k] = Int(v) + } + case []interface{}: + array = make([]int, len(value)) + for k, v := range value { + array[k] = Int(v) + } + case [][]byte: + array = make([]int, len(value)) + for k, v := range value { + array[k] = Int(v) + } + default: + return []int{Int(i)} + } + return array + } +} + +// Int32s converts to []int32. +func Int32s(i interface{}) []int32 { + if i == nil { + return nil + } + if r, ok := i.([]int32); ok { + return r + } else { + var array []int32 + switch value := i.(type) { + case []string: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = Int32(v) + } + case []int: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []int8: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []int16: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []int64: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []uint: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []uint8: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []uint16: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []uint32: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []uint64: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = int32(v) + } + case []bool: + array = make([]int32, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = Int32(v) + } + case []float64: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = Int32(v) + } + case []interface{}: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = Int32(v) + } + case [][]byte: + array = make([]int32, len(value)) + for k, v := range value { + array[k] = Int32(v) + } + default: + return []int32{Int32(i)} + } + return array + } +} + +// Int64s converts to []int64. +func Int64s(i interface{}) []int64 { + if i == nil { + return nil + } + if r, ok := i.([]int64); ok { + return r + } else { + var array []int64 + switch value := i.(type) { + case []string: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = Int64(v) + } + case []int: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []int8: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []int16: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []int32: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []uint: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []uint8: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []uint16: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []uint32: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []uint64: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = int64(v) + } + case []bool: + array = make([]int64, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = Int64(v) + } + case []float64: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = Int64(v) + } + case []interface{}: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = Int64(v) + } + case [][]byte: + array = make([]int64, len(value)) + for k, v := range value { + array[k] = Int64(v) + } + default: + return []int64{Int64(i)} + } + return array + } +} diff --git a/util/gconv/gconv_slice_str.go b/util/gconv/gconv_slice_str.go new file mode 100644 index 000000000..7f847f46b --- /dev/null +++ b/util/gconv/gconv_slice_str.go @@ -0,0 +1,104 @@ +// 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 gconv + +// SliceStr is alias of Strings. +func SliceStr(i interface{}) []string { + return Strings(i) +} + +// Strings converts to []string. +func Strings(i interface{}) []string { + if i == nil { + return nil + } + if r, ok := i.([]string); ok { + return r + } else { + var array []string + switch value := i.(type) { + case []int: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []int8: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []int16: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []int32: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []int64: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []uint: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []uint8: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []uint16: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []uint32: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []uint64: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []bool: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []float32: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []float64: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case []interface{}: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + case [][]byte: + array = make([]string, len(value)) + for k, v := range value { + array[k] = String(v) + } + default: + return []string{String(i)} + } + return array + } +} diff --git a/util/gconv/gconv_slice_uint.go b/util/gconv/gconv_slice_uint.go new file mode 100644 index 000000000..808b3fc23 --- /dev/null +++ b/util/gconv/gconv_slice_uint.go @@ -0,0 +1,295 @@ +// 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 gconv + +// SliceUint is alias of Uints. +func SliceUint(i interface{}) []uint { + return Uints(i) +} + +// SliceUint32 is alias of Uint32s. +func SliceUint32(i interface{}) []uint32 { + return Uint32s(i) +} + +// SliceUint64 is alias of Uint64s. +func SliceUint64(i interface{}) []uint64 { + return Uint64s(i) +} + +// Uints converts to []uint. +func Uints(i interface{}) []uint { + if i == nil { + return nil + } + if r, ok := i.([]uint); ok { + return r + } else { + var array []uint + switch value := i.(type) { + case []string: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = Uint(v) + } + case []int8: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []int16: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []int32: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []int64: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []uint8: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []uint16: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []uint32: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []uint64: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = uint(v) + } + case []bool: + array = make([]uint, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = Uint(v) + } + case []float64: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = Uint(v) + } + case []interface{}: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = Uint(v) + } + case [][]byte: + array = make([]uint, len(value)) + for k, v := range value { + array[k] = Uint(v) + } + default: + return []uint{Uint(i)} + } + return array + } +} + +// Uint32s converts to []uint32. +func Uint32s(i interface{}) []uint32 { + if i == nil { + return nil + } + if r, ok := i.([]uint32); ok { + return r + } else { + var array []uint32 + switch value := i.(type) { + case []string: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = Uint32(v) + } + case []int8: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []int16: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []int32: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []int64: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []uint: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []uint8: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []uint16: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []uint64: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = uint32(v) + } + case []bool: + array = make([]uint32, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = Uint32(v) + } + case []float64: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = Uint32(v) + } + case []interface{}: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = Uint32(v) + } + case [][]byte: + array = make([]uint32, len(value)) + for k, v := range value { + array[k] = Uint32(v) + } + default: + return []uint32{Uint32(i)} + } + return array + } +} + +// Uint64s converts to []uint64. +func Uint64s(i interface{}) []uint64 { + if i == nil { + return nil + } + if r, ok := i.([]uint64); ok { + return r + } else { + var array []uint64 + switch value := i.(type) { + case []string: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = Uint64(v) + } + case []int8: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []int16: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []int32: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []int64: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []uint: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []uint8: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []uint16: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []uint32: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = uint64(v) + } + case []bool: + array = make([]uint64, len(value)) + for k, v := range value { + if v { + array[k] = 1 + } else { + array[k] = 0 + } + } + case []float32: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = Uint64(v) + } + case []float64: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = Uint64(v) + } + case []interface{}: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = Uint64(v) + } + case [][]byte: + array = make([]uint64, len(value)) + for k, v := range value { + array[k] = Uint64(v) + } + default: + return []uint64{Uint64(i)} + } + return array + } +} diff --git a/util/gconv/gconv_z_unit_struct_slice_test.go b/util/gconv/gconv_z_unit_struct_slice_test.go new file mode 100644 index 000000000..39be12750 --- /dev/null +++ b/util/gconv/gconv_z_unit_struct_slice_test.go @@ -0,0 +1,98 @@ +// 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 gconv_test + +import ( + "testing" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gconv" +) + +func Test_Struct_Slice(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Scores []int + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []int32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []int64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []uint + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []uint32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []uint64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []float32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) + gtest.Case(t, func() { + type User struct { + Scores []float64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Scores, array) + }) +}