From fe179fff42d3bd55e98ca5fe84081c22bd435d18 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 21 Jun 2019 22:23:07 +0800 Subject: [PATCH 01/14] gofmt --- g/container/gpool/gpool.go | 4 +- g/container/gtype/int64.go | 2 +- g/crypto/gcrc32/gcrc32_test.go | 5 +- g/crypto/gdes/gdes.go | 10 +- g/crypto/gdes/gdes_test.go | 8 +- g/crypto/gmd5/gmd5.go | 41 +-- g/crypto/gmd5/gmd5_test.go | 14 +- g/crypto/gsha1/gsha1.go | 30 +- g/crypto/gsha1/gsha1_test.go | 6 +- g/database/gdb/gdb_unit_basic_test.go | 3 +- g/encoding/gcompress/gcompress.go | 56 ++-- g/encoding/gcompress/gcompress_test.go | 27 +- g/frame/gins/gins_view_test.go | 3 +- g/g.go | 2 + g/g_func.go | 2 +- g/g_logger.go | 2 +- g/g_object.go | 3 +- g/g_setting.go | 2 +- g/internal/cmdenv/cmdenv_test.go | 3 +- g/net/gtcp/gtcp_conn.go | 52 ++-- g/net/gtcp/gtcp_conn_pkg.go | 24 +- g/net/gtcp/gtcp_pool.go | 24 +- g/net/gtcp/gtcp_pool_pkg.go | 23 +- g/net/gudp/gudp_conn.go | 52 ++-- g/net/gudp/gudp_func.go | 10 - g/os/gcfg/gcfg.go | 16 +- g/os/gcfg/gcfg_z_unit_test.go | 37 ++- g/os/gmlock/gmlock.go | 77 +++--- g/os/gmlock/gmlock_locker.go | 156 ++++------- g/os/gmlock/gmlock_unit_lock_test.go | 78 +++--- g/os/gmlock/gmlock_unit_rlock_test.go | 236 +++++++++++++++- .../gmlock_mutex.go => gmutex/gmutex.go} | 144 ++++++---- g/os/gmutex/gmutex_bench_test.go | 47 ++++ g/os/gmutex/gmutex_uinit_test.go | 258 ++++++++++++++++++ g/test/gtest/gtest_test.go | 4 +- g/text/gregex/gregex_z_bench_test.go | 3 +- g/util/gvalid/gvalid_check_map.go | 7 +- g/util/gvalid/gvalid_check_struct.go | 7 +- geg/other/test.go | 42 +-- geg/other/test2.go | 53 +++- 40 files changed, 1082 insertions(+), 491 deletions(-) rename g/os/{gmlock/gmlock_mutex.go => gmutex/gmutex.go} (52%) create mode 100644 g/os/gmutex/gmutex_bench_test.go create mode 100644 g/os/gmutex/gmutex_uinit_test.go diff --git a/g/container/gpool/gpool.go b/g/container/gpool/gpool.go index 3f9ccdd5e..508105183 100644 --- a/g/container/gpool/gpool.go +++ b/g/container/gpool/gpool.go @@ -9,11 +9,12 @@ package gpool import ( "errors" + "time" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/os/gtimer" - "time" ) // Object-Reusable Pool. @@ -126,6 +127,7 @@ func (p *Pool) checkExpire() { gtimer.Exit() } for { + // TODO Do not use Pop and Push mechanism, which is not graceful. if r := p.list.PopFront(); r != nil { item := r.(*poolItem) if item.expire == 0 || item.expire > gtime.Millisecond() { diff --git a/g/container/gtype/int64.go b/g/container/gtype/int64.go index ee1846c1b..6c961efb1 100644 --- a/g/container/gtype/int64.go +++ b/g/container/gtype/int64.go @@ -41,7 +41,7 @@ func (v *Int64) Val() int64 { } // Add atomically adds to t.value and returns the new value. -func (v *Int64) Add(delta int64) int64 { +func (v *Int64) Add(delta int64) (new int64) { return atomic.AddInt64(&v.value, delta) } diff --git a/g/crypto/gcrc32/gcrc32_test.go b/g/crypto/gcrc32/gcrc32_test.go index 7a67d1c41..753e1c034 100644 --- a/g/crypto/gcrc32/gcrc32_test.go +++ b/g/crypto/gcrc32/gcrc32_test.go @@ -9,9 +9,10 @@ package gcrc32_test import ( - "github.com/gogf/gf/g/crypto/gmd5" "testing" + "github.com/gogf/gf/g/crypto/gmd5" + "github.com/gogf/gf/g/crypto/gcrc32" "github.com/gogf/gf/g/test/gtest" ) @@ -36,7 +37,7 @@ func TestEncrypt(t *testing.T) { gtest.AssertEQ(int(encrypt1), result) gtest.AssertEQ(int(encrypt2), result) - strmd5 := gmd5.Encrypt(s) + strmd5, _ := gmd5.Encrypt(s) test1 := gcrc32.Encrypt(strmd5) test2 := gcrc32.Encrypt([]byte(strmd5)) gtest.AssertEQ(test2, test1) diff --git a/g/crypto/gdes/gdes.go b/g/crypto/gdes/gdes.go index 8f945db46..7316d28d7 100644 --- a/g/crypto/gdes/gdes.go +++ b/g/crypto/gdes/gdes.go @@ -3,8 +3,6 @@ // 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. -// -// @author wenzi1 // Package gdes provides useful API for DES encryption/decryption algorithms. package gdes @@ -75,7 +73,7 @@ func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, err return nil, err } - newKey := make([]byte, 0) + var newKey []byte if len(key) == 16 { newKey = append([]byte{}, key...) newKey = append(newKey, key[:8]...) @@ -103,7 +101,7 @@ func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, er return nil, errors.New("key length error") } - newKey := make([]byte, 0) + var newKey []byte if len(key) == 16 { newKey = append([]byte{}, key...) newKey = append(newKey, key[:8]...) @@ -182,7 +180,7 @@ func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ( return nil, errors.New("key length invalid") } - newKey := make([]byte, 0) + var newKey []byte if len(key) == 16 { newKey = append([]byte{}, key...) newKey = append(newKey, key[:8]...) @@ -217,7 +215,7 @@ func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) return nil, errors.New("key length invalid") } - newKey := make([]byte, 0) + var newKey []byte if len(key) == 16 { newKey = append([]byte{}, key...) newKey = append(newKey, key[:8]...) diff --git a/g/crypto/gdes/gdes_test.go b/g/crypto/gdes/gdes_test.go index 4236352a6..b1a0e86d2 100644 --- a/g/crypto/gdes/gdes_test.go +++ b/g/crypto/gdes/gdes_test.go @@ -1,3 +1,9 @@ +// 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 gdes_test import ( @@ -139,7 +145,7 @@ func TestDesCBC(t *testing.T) { gtest.AssertEQ(errEncrypt, nil) // the iv is err errEncrypt, err = gdes.DesCBCEncrypt(key, text, errIv, padding) - //gtest.AssertNE(err,nil) + gtest.AssertNE(err, nil) gtest.AssertEQ(errEncrypt, nil) // the padding is err errEncrypt, err = gdes.DesCBCEncrypt(key, text, iv, errPadding) diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index 98e389f24..a4829e97a 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -9,38 +9,45 @@ package gmd5 import ( "crypto/md5" + "errors" "fmt" - "github.com/gogf/gf/g/util/gconv" "io" "os" + + "github.com/gogf/gf/g/util/gconv" ) // Encrypt encrypts any type of variable using MD5 algorithms. // It uses gconv package to convert to its bytes type. -func Encrypt(v interface{}) string { +func Encrypt(v interface{}) (encrypt string, err error) { h := md5.New() - h.Write([]byte(gconv.Bytes(v))) - return fmt.Sprintf("%x", h.Sum(nil)) + if _, err = h.Write([]byte(gconv.Bytes(v))); err != nil { + return "", err + } + return fmt.Sprintf("%x", h.Sum(nil)), nil } +// EncryptString is alias of Encrypt. // Deprecated. -func EncryptString(v string) string { - h := md5.New() - h.Write([]byte(v)) - return fmt.Sprintf("%x", h.Sum(nil)) +func EncryptString(v string) (encrypt string, err error) { + return Encrypt(v) } // EncryptFile encrypts file content of using MD5 algorithms. -func EncryptFile(path string) string { - f, e := os.Open(path) - if e != nil { - return "" +func EncryptFile(path string) (encrypt string, err error) { + f, err := os.Open(path) + if err != nil { + return "", err } - defer f.Close() + defer func() { + if e := f.Close(); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() h := md5.New() - _, e = io.Copy(h, f) - if e != nil { - return "" + _, err = io.Copy(h, f) + if err != nil { + return "", err } - return fmt.Sprintf("%x", h.Sum(nil)) + return fmt.Sprintf("%x", h.Sum(nil)), nil } diff --git a/g/crypto/gmd5/gmd5_test.go b/g/crypto/gmd5/gmd5_test.go index 9f8b9f94b..7cbfcf696 100644 --- a/g/crypto/gmd5/gmd5_test.go +++ b/g/crypto/gmd5/gmd5_test.go @@ -30,11 +30,11 @@ type user struct { func TestEncrypt(t *testing.T) { gtest.Case(t, func() { - encryptString := gmd5.Encrypt(s) + encryptString, _ := gmd5.Encrypt(s) gtest.Assert(encryptString, result) result := "1427562bb29f88a1161590b76398ab72" - encrypt := gmd5.Encrypt(123456) + encrypt, _ := gmd5.Encrypt(123456) gtest.AssertEQ(encrypt, result) }) @@ -45,14 +45,14 @@ func TestEncrypt(t *testing.T) { age: 23, } result := "70917ebce8bd2f78c736cda63870fb39" - encrypt := gmd5.Encrypt(user) + encrypt, _ := gmd5.Encrypt(user) gtest.AssertEQ(encrypt, result) }) } func TestEncryptString(t *testing.T) { gtest.Case(t, func() { - encryptString := gmd5.EncryptString(s) + encryptString, _ := gmd5.EncryptString(s) gtest.Assert(encryptString, result) }) } @@ -66,11 +66,11 @@ func TestEncryptFile(t *testing.T) { defer os.Remove(path) defer file.Close() gtest.Assert(err, nil) - file.Write([]byte("Hello Go Frame")) - encryptFile := gmd5.EncryptFile(path) + _, _ = file.Write([]byte("Hello Go Frame")) + encryptFile, _ := gmd5.EncryptFile(path) gtest.AssertEQ(encryptFile, result) // when the file is not exist,encrypt will return empty string - errEncrypt := gmd5.EncryptFile(errorPath) + errEncrypt, _ := gmd5.EncryptFile(errorPath) gtest.AssertEQ(errEncrypt, "") }) diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go index 77a342aa6..b296bdb74 100644 --- a/g/crypto/gsha1/gsha1.go +++ b/g/crypto/gsha1/gsha1.go @@ -10,9 +10,11 @@ package gsha1 import ( "crypto/sha1" "encoding/hex" - "github.com/gogf/gf/g/util/gconv" + "errors" "io" "os" + + "github.com/gogf/gf/g/util/gconv" ) // Encrypt encrypts any type of variable using SHA1 algorithms. @@ -22,23 +24,27 @@ func Encrypt(v interface{}) string { return hex.EncodeToString(r[:]) } +// EncryptString is alias of Encrypt. // Deprecated. func EncryptString(s string) string { - r := sha1.Sum([]byte(s)) - return hex.EncodeToString(r[:]) + return Encrypt(s) } // EncryptFile encrypts file content of using SHA1 algorithms. -func EncryptFile(path string) string { - f, e := os.Open(path) - if e != nil { - return "" +func EncryptFile(path string) (encrypt string, err error) { + f, err := os.Open(path) + if err != nil { + return "", err } - defer f.Close() + defer func() { + if e := f.Close(); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() h := sha1.New() - _, e = io.Copy(h, f) - if e != nil { - return "" + _, err = io.Copy(h, f) + if err != nil { + return "", err } - return hex.EncodeToString(h.Sum(nil)) + return hex.EncodeToString(h.Sum(nil)), nil } diff --git a/g/crypto/gsha1/gsha1_test.go b/g/crypto/gsha1/gsha1_test.go index 773473c11..b3c1dad76 100644 --- a/g/crypto/gsha1/gsha1_test.go +++ b/g/crypto/gsha1/gsha1_test.go @@ -57,11 +57,11 @@ func TestEncryptFile(t *testing.T) { defer os.Remove(path) defer file.Close() gtest.Assert(err, nil) - file.Write([]byte("Hello Go Frame")) - encryptFile := gsha1.EncryptFile(path) + _, _ = file.Write([]byte("Hello Go Frame")) + encryptFile, _ := gsha1.EncryptFile(path) gtest.AssertEQ(encryptFile, result) // when the file is not exist,encrypt will return empty string - errEncrypt := gsha1.EncryptFile(errPath) + errEncrypt, _ := gsha1.EncryptFile(errPath) gtest.AssertEQ(errEncrypt, "") }) } diff --git a/g/database/gdb/gdb_unit_basic_test.go b/g/database/gdb/gdb_unit_basic_test.go index 4506bd8ef..3a28160f8 100644 --- a/g/database/gdb/gdb_unit_basic_test.go +++ b/g/database/gdb/gdb_unit_basic_test.go @@ -7,9 +7,10 @@ package gdb_test import ( + "testing" + "github.com/gogf/gf/g/database/gdb" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_Instance(t *testing.T) { diff --git a/g/encoding/gcompress/gcompress.go b/g/encoding/gcompress/gcompress.go index bff744d5e..8447e4f24 100644 --- a/g/encoding/gcompress/gcompress.go +++ b/g/encoding/gcompress/gcompress.go @@ -15,53 +15,69 @@ import ( ) // Zlib compresses with zlib algorithm. -func Zlib(data []byte) []byte { +func Zlib(data []byte) ([]byte, error) { if data == nil || len(data) < 13 { - return data + return data, nil } var in bytes.Buffer + var err error w := zlib.NewWriter(&in) - _, _ = w.Write(data) - _ = w.Close() - return in.Bytes() + if _, err = w.Write(data); err != nil { + return nil, err + } + if err = w.Close(); err != nil { + return in.Bytes(), err + } + return in.Bytes(), nil } // UnZlib decompresses with zlib algorithm. -func UnZlib(data []byte) []byte { +func UnZlib(data []byte) ([]byte, error) { if data == nil || len(data) < 13 { - return data + return data, nil } + b := bytes.NewReader(data) var out bytes.Buffer + var err error r, err := zlib.NewReader(b) if err != nil { - return nil + return nil, err } - _, _ = io.Copy(&out, r) - return out.Bytes() + if _, err = io.Copy(&out, r); err != nil { + return nil, err + } + return out.Bytes(), nil } // Gzip compresses with gzip algorithm. -func Gzip(data []byte) []byte { +func Gzip(data []byte) ([]byte, error) { var buf bytes.Buffer + var err error zip := gzip.NewWriter(&buf) - _, err := zip.Write(data) + _, err = zip.Write(data) if err != nil { - return nil + return nil, err } - _ = zip.Close() - return buf.Bytes() + if err = zip.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil } // UnGzip decompresses with gzip algorithm. -func UnGzip(data []byte) []byte { +func UnGzip(data []byte) ([]byte, error) { var buf bytes.Buffer content := bytes.NewReader(data) zipData, err := gzip.NewReader(content) if err != nil { - return nil + return nil, err } - _, _ = io.Copy(&buf, zipData) - _ = zipData.Close() - return buf.Bytes() + if _, err = io.Copy(&buf, zipData); err != nil { + return nil, err + } + if err = zipData.Close(); err != nil { + return buf.Bytes(), err + } + return buf.Bytes(), nil } diff --git a/g/encoding/gcompress/gcompress_test.go b/g/encoding/gcompress/gcompress_test.go index ac34a2027..31f161649 100644 --- a/g/encoding/gcompress/gcompress_test.go +++ b/g/encoding/gcompress/gcompress_test.go @@ -7,23 +7,29 @@ package gcompress_test import ( + "testing" + "github.com/gogf/gf/g/encoding/gcompress" "github.com/gogf/gf/g/test/gtest" - "testing" ) func TestZlib(t *testing.T) { gtest.Case(t, func() { src := "hello, world\n" dst := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147} - gtest.Assert(gcompress.Zlib([]byte(src)), dst) + data, _ := gcompress.Zlib([]byte(src)) + gtest.Assert(data, dst) - gtest.Assert(gcompress.UnZlib(dst), []byte(src)) + data, _ = gcompress.UnZlib(dst) + gtest.Assert(data, []byte(src)) - gtest.Assert(gcompress.Zlib(nil), nil) - gtest.Assert(gcompress.UnZlib(nil), nil) + data, _ = gcompress.Zlib(nil) + gtest.Assert(data, nil) + data, _ = gcompress.UnZlib(nil) + gtest.Assert(data, nil) - gtest.Assert(gcompress.UnZlib(dst[1:]), nil) + data, _ = gcompress.UnZlib(dst[1:]) + gtest.Assert(data, nil) }) } @@ -43,9 +49,12 @@ func TestGzip(t *testing.T) { } arr := []byte(src) - gtest.Assert(gcompress.Gzip(arr), gzip) + data, _ := gcompress.Gzip(arr) + gtest.Assert(data, gzip) - gtest.Assert(gcompress.UnGzip(gzip), arr) + data, _ = gcompress.UnGzip(gzip) + gtest.Assert(data, arr) - gtest.Assert(gcompress.UnGzip(gzip[1:]), nil) + data, _ = gcompress.UnGzip(gzip[1:]) + gtest.Assert(data, nil) } diff --git a/g/frame/gins/gins_view_test.go b/g/frame/gins/gins_view_test.go index 9b3598d35..249b052f5 100644 --- a/g/frame/gins/gins_view_test.go +++ b/g/frame/gins/gins_view_test.go @@ -8,11 +8,12 @@ package gins_test import ( "fmt" + "testing" + "github.com/gogf/gf/g/frame/gins" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_View(t *testing.T) { diff --git a/g/g.go b/g/g.go index 23f6bccb3..8a1350e59 100644 --- a/g/g.go +++ b/g/g.go @@ -39,6 +39,8 @@ type Slice = []interface{} type SliceAny = []interface{} type SliceStr = []string type SliceInt = []int + +// Array is alias of Slice. type Array = []interface{} type ArrayAny = []interface{} type ArrayStr = []string diff --git a/g/g_func.go b/g/g_func.go index 1d6198d6f..ea5ac080f 100644 --- a/g/g_func.go +++ b/g/g_func.go @@ -39,7 +39,7 @@ func Throw(exception interface{}) { gutil.Throw(exception) } -// TryCatch does the try...catch... logic. +// TryCatch does the try...catch... mechanism. func TryCatch(try func(), catch ...func(exception interface{})) { gutil.TryCatch(try, catch...) } diff --git a/g/g_logger.go b/g/g_logger.go index f64589a03..41b2556dd 100644 --- a/g/g_logger.go +++ b/g/g_logger.go @@ -10,7 +10,7 @@ import ( "github.com/gogf/gf/g/os/glog" ) -// SetDebug disables/enables debug level for logging globally. +// SetDebug disables/enables debug level for logging component globally. func SetDebug(debug bool) { glog.SetDebug(debug) } diff --git a/g/g_object.go b/g/g_object.go index f83aea35e..9d3f7f64d 100644 --- a/g/g_object.go +++ b/g/g_object.go @@ -47,7 +47,8 @@ func Database(name ...string) gdb.DB { return gins.Database(name...) } -// DB is alias of Database. See Database. +// DB is alias of Database. +// See Database. func DB(name ...string) gdb.DB { return gins.Database(name...) } diff --git a/g/g_setting.go b/g/g_setting.go index f220806ca..a19187859 100644 --- a/g/g_setting.go +++ b/g/g_setting.go @@ -8,7 +8,7 @@ package g import "github.com/gogf/gf/g/net/ghttp" -// SetServerGraceful enables/disables graceful/hot reload feature of http Web Server. +// SetServerGraceful enables/disables graceful reload feature of http Web Server. // This feature is disabled in default. func SetServerGraceful(enabled bool) { ghttp.SetGraceful(enabled) diff --git a/g/internal/cmdenv/cmdenv_test.go b/g/internal/cmdenv/cmdenv_test.go index 0eb05a7aa..90702733c 100644 --- a/g/internal/cmdenv/cmdenv_test.go +++ b/g/internal/cmdenv/cmdenv_test.go @@ -9,9 +9,10 @@ package cmdenv import ( - "github.com/gogf/gf/g/test/gtest" "os" "testing" + + "github.com/gogf/gf/g/test/gtest" ) func Test_Get(t *testing.T) { diff --git a/g/net/gtcp/gtcp_conn.go b/g/net/gtcp/gtcp_conn.go index b18526d4c..f1ace01e9 100644 --- a/g/net/gtcp/gtcp_conn.go +++ b/g/net/gtcp/gtcp_conn.go @@ -10,6 +10,7 @@ import ( "bufio" "bytes" "crypto/tls" + "errors" "io" "net" "time" @@ -17,7 +18,7 @@ import ( // 封装的链接对象 type Conn struct { - conn net.Conn // 底层tcp对象 + net.Conn // 底层tcp对象 reader *bufio.Reader // 当前链接的缓冲读取对象 buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理) recvDeadline time.Time // 读取超时时间 @@ -60,7 +61,7 @@ func NewConnKeyCrt(addr, crtFile, keyFile string) (*Conn, error) { // 将net.Conn接口对象转换为*gtcp.Conn对象 func NewConnByNetConn(conn net.Conn) *Conn { return &Conn{ - conn: conn, + Conn: conn, reader: bufio.NewReader(conn), recvDeadline: time.Time{}, sendDeadline: time.Time{}, @@ -68,15 +69,10 @@ func NewConnByNetConn(conn net.Conn) *Conn { } } -// 关闭连接 -func (c *Conn) Close() error { - return c.conn.Close() -} - // 发送数据 func (c *Conn) Send(data []byte, retry ...Retry) error { for { - if _, err := c.conn.Write(data); err != nil { + if _, err := c.Write(data); err != nil { // 链接已关闭 if err == io.EOF { return err @@ -124,7 +120,7 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { // 仅对读取全部缓冲区数据操作有效 if length < 0 && index > 0 { bufferWait = true - if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil { + if err = c.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil { return nil, err } } @@ -155,7 +151,7 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { } // 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠) if bufferWait && isTimeout(err) { - if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil { + if err = c.SetReadDeadline(c.recvDeadline); err != nil { return nil, err } err = nil @@ -207,21 +203,31 @@ func (c *Conn) RecvLine(retry ...Retry) ([]byte, error) { } // 带超时时间的数据获取 -func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) ([]byte, error) { +func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) { if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) - return c.Recv(length, retry...) + defer func() { + if e := c.SetRecvDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + data, err = c.Recv(length, retry...) + return } // 带超时时间的数据发送 -func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) error { +func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) { if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { return err } - defer c.SetSendDeadline(time.Time{}) - return c.Send(data, retry...) + defer func() { + if e := c.SetSendDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + err = c.Send(data, retry...) + return } // 发送数据并等待接收返回数据 @@ -243,7 +249,7 @@ func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Durati } func (c *Conn) SetDeadline(t time.Time) error { - err := c.conn.SetDeadline(t) + err := c.Conn.SetDeadline(t) if err == nil { c.recvDeadline = t c.sendDeadline = t @@ -252,7 +258,7 @@ func (c *Conn) SetDeadline(t time.Time) error { } func (c *Conn) SetRecvDeadline(t time.Time) error { - err := c.conn.SetReadDeadline(t) + err := c.SetReadDeadline(t) if err == nil { c.recvDeadline = t } @@ -260,7 +266,7 @@ func (c *Conn) SetRecvDeadline(t time.Time) error { } func (c *Conn) SetSendDeadline(t time.Time) error { - err := c.conn.SetWriteDeadline(t) + err := c.SetWriteDeadline(t) if err == nil { c.sendDeadline = t } @@ -272,11 +278,3 @@ func (c *Conn) SetSendDeadline(t time.Time) error { func (c *Conn) SetRecvBufferWait(bufferWaitDuration time.Duration) { c.recvBufferWait = bufferWaitDuration } - -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} diff --git a/g/net/gtcp/gtcp_conn_pkg.go b/g/net/gtcp/gtcp_conn_pkg.go index 8a39f76ce..0bc114a50 100644 --- a/g/net/gtcp/gtcp_conn_pkg.go +++ b/g/net/gtcp/gtcp_conn_pkg.go @@ -55,7 +55,7 @@ func (c *Conn) SendPkg(data []byte, option ...PkgOption) error { } length := len(data) if length > pkgOption.MaxSize { - return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE)) + return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE) } buffer := make([]byte, gPKG_HEADER_SIZE+1+len(data)) binary.BigEndian.PutUint32(buffer[0:], uint32(length)) @@ -68,12 +68,17 @@ func (c *Conn) SendPkg(data []byte, option ...PkgOption) error { } // 简单协议: 带超时时间的数据发送 -func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) error { +func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) (err error) { if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { return err } - defer c.SetSendDeadline(time.Time{}) - return c.SendPkg(data, option...) + defer func() { + if e := c.SetSendDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + err = c.SendPkg(data, option...) + return } // 简单协议: 发送数据并等待接收返回数据 @@ -138,10 +143,15 @@ func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) { } // 简单协议: 带超时时间的消息包获取 -func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) ([]byte, error) { +func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) { if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) - return c.RecvPkg(option...) + defer func() { + if e := c.SetRecvDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + data, err = c.RecvPkg(option...) + return } diff --git a/g/net/gtcp/gtcp_pool.go b/g/net/gtcp/gtcp_pool.go index cf8b4f00e..ee65f0f61 100644 --- a/g/net/gtcp/gtcp_pool.go +++ b/g/net/gtcp/gtcp_pool.go @@ -7,9 +7,11 @@ package gtcp import ( + "errors" + "time" + "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gpool" - "time" ) // 链接池链接对象 @@ -118,17 +120,27 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) - return c.Recv(length, retry...) + defer func() { + if e := c.SetRecvDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + data, err = c.Recv(length, retry...) + return } // (方法覆盖)带超时时间的数据发送 -func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) error { +func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) { if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { return err } - defer c.SetSendDeadline(time.Time{}) - return c.Send(data, retry...) + defer func() { + if e := c.SetSendDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + err = c.Send(data, retry...) + return } // (方法覆盖)发送数据并等待接收返回数据 diff --git a/g/net/gtcp/gtcp_pool_pkg.go b/g/net/gtcp/gtcp_pool_pkg.go index f487ce742..6b71eea0d 100644 --- a/g/net/gtcp/gtcp_pool_pkg.go +++ b/g/net/gtcp/gtcp_pool_pkg.go @@ -7,6 +7,7 @@ package gtcp import ( + "errors" "time" ) @@ -40,21 +41,31 @@ func (c *PoolConn) RecvPkg(option ...PkgOption) ([]byte, error) { } // 简单协议: (方法覆盖)带超时时间的数据获取 -func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) ([]byte, error) { +func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) { if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) - return c.RecvPkg(option...) + defer func() { + if e := c.SetRecvDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + data, err = c.RecvPkg(option...) + return } // 简单协议: (方法覆盖)带超时时间的数据发送 -func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) error { +func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) (err error) { if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { return err } - defer c.SetSendDeadline(time.Time{}) - return c.SendPkg(data, option...) + defer func() { + if e := c.SetSendDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + err = c.SendPkg(data, option...) + return } // 简单协议: (方法覆盖)发送数据并等待接收返回数据 diff --git a/g/net/gudp/gudp_conn.go b/g/net/gudp/gudp_conn.go index 50b30b5d9..1d59791f9 100644 --- a/g/net/gudp/gudp_conn.go +++ b/g/net/gudp/gudp_conn.go @@ -7,6 +7,7 @@ package gudp import ( + "errors" "io" "net" "time" @@ -14,9 +15,8 @@ import ( // 封装的UDP链接对象 type Conn struct { - conn *net.UDPConn // 底层链接对象 + *net.UDPConn // 底层链接对象 raddr *net.UDPAddr // 远程地址 - buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理) recvDeadline time.Time // 读取超时时间 sendDeadline time.Time // 写入超时时间 recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔 @@ -45,7 +45,7 @@ func NewConn(raddr string, laddr ...string) (*Conn, error) { // 将*net.UDPConn对象转换为*Conn对象 func NewConnByNetConn(udp *net.UDPConn) *Conn { return &Conn{ - conn: udp, + UDPConn: udp, recvDeadline: time.Time{}, sendDeadline: time.Time{}, recvBufferWait: gRECV_ALL_WAIT_TIMEOUT, @@ -56,9 +56,9 @@ func NewConnByNetConn(udp *net.UDPConn) *Conn { func (c *Conn) Send(data []byte, retry ...Retry) (err error) { for { if c.raddr != nil { - _, err = c.conn.WriteToUDP(data, c.raddr) + _, err = c.WriteToUDP(data, c.raddr) } else { - _, err = c.conn.Write(data) + _, err = c.Write(data) } if err != nil { // 链接已关闭 @@ -104,11 +104,11 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { for { if length < 0 && index > 0 { bufferWait = true - if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil { + if err = c.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil { return nil, err } } - size, raddr, err = c.conn.ReadFromUDP(buffer[index:]) + size, raddr, err = c.ReadFromUDP(buffer[index:]) if err == nil { c.raddr = raddr } @@ -138,7 +138,7 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { } // 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠) if bufferWait && isTimeout(err) { - if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil { + if err = c.SetReadDeadline(c.recvDeadline); err != nil { return nil, err } err = nil @@ -176,21 +176,31 @@ func (c *Conn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error } // 带超时时间的数据获取 -func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) ([]byte, error) { +func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) { if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) - return c.Recv(length, retry...) + defer func() { + if e := c.SetRecvDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + data, err = c.Recv(length, retry...) + return } // 带超时时间的数据发送 -func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) error { +func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) { if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { return err } - defer c.SetSendDeadline(time.Time{}) - return c.Send(data, retry...) + defer func() { + if e := c.SetSendDeadline(time.Time{}); e != nil { + err = errors.New(err.Error() + "; " + e.Error()) + } + }() + err = c.Send(data, retry...) + return } // 发送数据并等待接收返回数据(带返回超时等待时间) @@ -203,7 +213,7 @@ func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Durati } func (c *Conn) SetDeadline(t time.Time) error { - err := c.conn.SetDeadline(t) + err := c.UDPConn.SetDeadline(t) if err == nil { c.recvDeadline = t c.sendDeadline = t @@ -212,7 +222,7 @@ func (c *Conn) SetDeadline(t time.Time) error { } func (c *Conn) SetRecvDeadline(t time.Time) error { - err := c.conn.SetReadDeadline(t) + err := c.SetReadDeadline(t) if err == nil { c.recvDeadline = t } @@ -220,7 +230,7 @@ func (c *Conn) SetRecvDeadline(t time.Time) error { } func (c *Conn) SetSendDeadline(t time.Time) error { - err := c.conn.SetWriteDeadline(t) + err := c.SetWriteDeadline(t) if err == nil { c.sendDeadline = t } @@ -233,17 +243,9 @@ func (c *Conn) SetRecvBufferWait(d time.Duration) { c.recvBufferWait = d } -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - // 不能使用c.conn.RemoteAddr(),其返回为nil, // 这里使用c.raddr获取远程连接地址。 func (c *Conn) RemoteAddr() net.Addr { //return c.conn.RemoteAddr() return c.raddr } - -func (c *Conn) Close() error { - return c.conn.Close() -} diff --git a/g/net/gudp/gudp_func.go b/g/net/gudp/gudp_func.go index 702dee05b..cf82f5567 100644 --- a/g/net/gudp/gudp_func.go +++ b/g/net/gudp/gudp_func.go @@ -10,16 +10,6 @@ import ( "net" ) -// Deprecated. -// 常见的二进制数据校验方式,生成校验结果 -func Checksum(buffer []byte) uint32 { - var checksum uint32 - for _, b := range buffer { - checksum += uint32(b) - } - return checksum -} - // 创建标准库UDP链接操作对象 func NewNetConn(raddr string, laddr ...string) (*net.UDPConn, error) { var err error diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index b8edb32da..1badfa033 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -11,6 +11,8 @@ import ( "bytes" "errors" "fmt" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gtype" @@ -22,11 +24,10 @@ import ( "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gspath" "github.com/gogf/gf/g/os/gtime" - "time" ) const ( - // Default configuration file name. + // DEFAULT_CONFIG_FILE is the default configuration file name. DEFAULT_CONFIG_FILE = "config.toml" ) @@ -144,7 +145,7 @@ func (c *Config) SetPath(path string) error { } // Should be a directory. if !gfile.IsDir(realPath) { - err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path)) + err := fmt.Errorf(`[gcfg] SetPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -205,7 +206,7 @@ func (c *Config) AddPath(path string) error { return err } if !gfile.IsDir(realPath) { - err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path)) + err := fmt.Errorf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -220,8 +221,8 @@ func (c *Config) AddPath(path string) error { return nil } +// GetFilePath is alias of FilePath. // Deprecated. -// Alias of FilePath. func (c *Config) GetFilePath(file ...string) (path string) { return c.FilePath(file...) } @@ -281,9 +282,12 @@ func (c *Config) getJson(file ...string) *gjson.Json { // Add monitor for this configuration file, // any changes of this file will refresh its cache in Config object. if filePath != "" { - _, _ = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { + _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { c.jsons.Remove(name) }) + if err != nil && errorPrint() { + glog.Error(err) + } } return j } else { diff --git a/g/os/gcfg/gcfg_z_unit_test.go b/g/os/gcfg/gcfg_z_unit_test.go index caadec849..858a1e4f7 100644 --- a/g/os/gcfg/gcfg_z_unit_test.go +++ b/g/os/gcfg/gcfg_z_unit_test.go @@ -9,14 +9,15 @@ package gcfg_test import ( + "io/ioutil" + "os" + "testing" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/encoding/gjson" "github.com/gogf/gf/g/os/gcfg" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/test/gtest" - "io/ioutil" - "os" - "testing" ) func init() { @@ -38,7 +39,9 @@ array = [1,2,3] path := gcfg.DEFAULT_CONFIG_FILE err := gfile.PutContents(path, config) gtest.Assert(err, nil) - defer gfile.Remove(path) + defer func() { + _ = gfile.Remove(path) + }() c := gcfg.New() gtest.Assert(c.Get("v1"), 1) @@ -163,7 +166,9 @@ func Test_SetFileName(t *testing.T) { path := "config.json" err := gfile.PutContents(path, config) gtest.Assert(err, nil) - defer gfile.Remove(path) + defer func() { + _ = gfile.Remove(path) + }() c := gcfg.New() c.SetFileName(path) @@ -232,7 +237,9 @@ func Test_Instance(t *testing.T) { path := gcfg.DEFAULT_CONFIG_FILE err := gfile.PutContents(path, config) gtest.Assert(err, nil) - defer gfile.Remove(path) + defer func() { + gtest.Assert(gfile.Remove(path), nil) + }() c := gcfg.Instance() gtest.Assert(c.Get("v1"), 1) @@ -286,12 +293,14 @@ func TestCfg_New(t *testing.T) { gtest.Assert(c.GetFileName(), "config.yml") configPath := gfile.Pwd() + gfile.Separator + "config" - gfile.Mkdir(configPath) - defer gfile.Remove(configPath) + _ = gfile.Mkdir(configPath) + defer func() { + _ = gfile.Remove(configPath) + }() c = gcfg.New("config.yml") gtest.Assert(c.Get("name"), nil) - os.Unsetenv("GF_GCFG_PATH") + _ = os.Unsetenv("GF_GCFG_PATH") c = gcfg.New("config.yml") gtest.Assert(c.Get("name"), nil) }) @@ -340,9 +349,11 @@ func TestCfg_FilePath(t *testing.T) { func TestCfg_Get(t *testing.T) { gtest.Case(t, func() { configPath := gfile.Pwd() + gfile.Separator + "config" - gfile.Mkdir(configPath) - defer gfile.Remove(configPath) - ioutil.WriteFile(configPath+gfile.Separator+"config.yml", []byte("wrong config"), 0644) + _ = gfile.Mkdir(configPath) + defer func() { + _ = gfile.Remove(configPath) + }() + _ = ioutil.WriteFile(configPath+gfile.Separator+"config.yml", []byte("wrong config"), 0644) c := gcfg.New("config.yml") gtest.Assert(c.Get("name"), nil) gtest.Assert(c.GetVar("name").Val(), nil) @@ -379,7 +390,7 @@ func TestCfg_Get(t *testing.T) { c.Clear() arr, _ := gjson.Encode(g.Map{"name": "gf", "time": "2019-06-12", "person": g.Map{"name": "gf"}, "floats": g.Slice{1, 2, 3}}) - ioutil.WriteFile(configPath+gfile.Separator+"config.yml", arr, 0644) + _ = ioutil.WriteFile(configPath+gfile.Separator+"config.yml", arr, 0644) gtest.Assert(c.GetTime("time").Format("2006-01-02"), "2019-06-12") gtest.Assert(c.GetGTime("time").Format("Y-m-d"), "2019-06-12") gtest.Assert(c.GetDuration("time").String(), "0s") diff --git a/g/os/gmlock/gmlock.go b/g/os/gmlock/gmlock.go index 7d61a7fb5..dcfb0c557 100644 --- a/g/os/gmlock/gmlock.go +++ b/g/os/gmlock/gmlock.go @@ -7,26 +7,23 @@ // Package gmlock implements a concurrent-safe memory-based locker. package gmlock -import "time" - var ( // Default locker. locker = New() ) -// TryLock tries locking the with writing lock, -// it returns true if success, or if there's a write/reading lock the , -// it returns false. The parameter specifies the max duration it locks. -func TryLock(key string, expire ...time.Duration) bool { - return locker.TryLock(key, expire...) -} - // Lock locks the with writing lock. // If there's a write/reading lock the , // it will blocks until the lock is released. -// The parameter specifies the max duration it locks. -func Lock(key string, expire ...time.Duration) { - locker.Lock(key, expire...) +func Lock(key string) { + locker.Lock(key) +} + +// TryLock tries locking the with writing lock, +// it returns true if success, or if there's a write/reading lock the , +// it returns false. +func TryLock(key string) bool { + return locker.TryLock(key) } // Unlock unlocks the writing lock of the . @@ -34,12 +31,6 @@ func Unlock(key string) { locker.Unlock(key) } -// TryRLock tries locking the with reading lock. -// It returns true if success, or if there's a writing lock on , it returns false. -func TryRLock(key string) bool { - return locker.TryRLock(key) -} - // RLock locks the with reading lock. // If there's a writing lock on , // it will blocks until the writing lock is released. @@ -47,40 +38,24 @@ func RLock(key string) { locker.RLock(key) } +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. +func TryRLock(key string) bool { + return locker.TryRLock(key) +} + // RUnlock unlocks the reading lock of the . func RUnlock(key string) { locker.RUnlock(key) } -// TryLockFunc locks the with writing lock and callback function . -// It returns true if success, or else if there's a write/reading lock the , it return false. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func TryLockFunc(key string, f func(), expire ...time.Duration) bool { - return locker.TryLockFunc(key, f, expire...) -} - -// TryRLockFunc locks the with reading lock and callback function . -// It returns true if success, or else if there's a writing lock the , it returns false. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func TryRLockFunc(key string, f func()) bool { - return locker.TryRLockFunc(key, f) -} - // LockFunc locks the with writing lock and callback function . // If there's a write/reading lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func LockFunc(key string, f func(), expire ...time.Duration) { - locker.LockFunc(key, f, expire...) +func LockFunc(key string, f func()) { + locker.LockFunc(key, f) } // RLockFunc locks the with reading lock and callback function . @@ -88,8 +63,22 @@ func LockFunc(key string, f func(), expire ...time.Duration) { // it will blocks until the lock is released. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. func RLockFunc(key string, f func()) { locker.RLockFunc(key, f) } + +// TryLockFunc locks the with writing lock and callback function . +// It returns true if success, or else if there's a write/reading lock the , it return false. +// +// It releases the lock after is executed. +func TryLockFunc(key string, f func()) bool { + return locker.TryLockFunc(key, f) +} + +// TryRLockFunc locks the with reading lock and callback function . +// It returns true if success, or else if there's a writing lock the , it returns false. +// +// It releases the lock after is executed. +func TryRLockFunc(key string, f func()) bool { + return locker.TryRLockFunc(key, f) +} diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index d341e1a97..8041fd270 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -8,11 +8,11 @@ package gmlock import ( "github.com/gogf/gf/g/container/gmap" - "github.com/gogf/gf/g/os/gtimer" - "time" + "github.com/gogf/gf/g/os/gmutex" ) // Memory locker. +// Note that there's no cache expire mechanism for attribute map . type Locker struct { m *gmap.StrAnyMap } @@ -25,56 +25,74 @@ func New() *Locker { } } -// TryLock tries locking the with writing lock, -// it returns true if success, or it returns false if there's a writing/reading lock the . -// The parameter specifies the max duration it locks. -func (l *Locker) TryLock(key string, expire ...time.Duration) bool { - return l.doLock(key, l.getExpire(expire...), true) -} - // Lock locks the with writing lock. // If there's a write/reading lock the , // it will blocks until the lock is released. -// The parameter specifies the max duration it locks. -func (l *Locker) Lock(key string, expire ...time.Duration) { - l.doLock(key, l.getExpire(expire...), false) +func (l *Locker) Lock(key string) { + l.getOrNewMutex(key).Lock() +} + +// TryLock tries locking the with writing lock, +// it returns true if success, or it returns false if there's a writing/reading lock the . +func (l *Locker) TryLock(key string) bool { + return l.getOrNewMutex(key).TryLock() } // Unlock unlocks the writing lock of the . func (l *Locker) Unlock(key string) { if v := l.m.Get(key); v != nil { - v.(*Mutex).Unlock() + v.(*gmutex.Mutex).Unlock() } } -// TryRLock tries locking the with reading lock. -// It returns true if success, or if there's a writing lock on , it returns false. -func (l *Locker) TryRLock(key string) bool { - return l.doRLock(key, true) -} - // RLock locks the with reading lock. // If there's a writing lock on , // it will blocks until the writing lock is released. func (l *Locker) RLock(key string) { - l.doRLock(key, false) + l.getOrNewMutex(key).RLock() +} + +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. +func (l *Locker) TryRLock(key string) bool { + return l.getOrNewMutex(key).TryRLock() } // RUnlock unlocks the reading lock of the . func (l *Locker) RUnlock(key string) { if v := l.m.Get(key); v != nil { - v.(*Mutex).RUnlock() + v.(*gmutex.Mutex).RUnlock() } } +// LockFunc locks the with writing lock and callback function . +// If there's a write/reading lock the , +// it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (l *Locker) LockFunc(key string, f func()) { + l.Lock(key) + defer l.Unlock(key) + f() +} + +// RLockFunc locks the with reading lock and callback function . +// If there's a writing lock the , +// it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (l *Locker) RLockFunc(key string, f func()) { + l.RLock(key) + defer l.RUnlock(key) + f() +} + // TryLockFunc locks the with writing lock and callback function . // It returns true if success, or else if there's a write/reading lock the , it return false. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool { - if l.TryLock(key, expire...) { +func (l *Locker) TryLockFunc(key string, f func()) bool { + if l.TryLock(key) { defer l.Unlock(key) f() return true @@ -86,8 +104,6 @@ func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool // It returns true if success, or else if there's a writing lock the , it returns false. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. func (l *Locker) TryRLockFunc(key string, f func()) bool { if l.TryRLock(key) { defer l.RUnlock(key) @@ -97,90 +113,10 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool { return false } -// LockFunc locks the with writing lock and callback function . -// If there's a write/reading lock the , -// it will blocks until the lock is released. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) LockFunc(key string, f func(), expire ...time.Duration) { - l.Lock(key, expire...) - defer l.Unlock(key) - f() -} - -// RLockFunc locks the with reading lock and callback function . -// If there's a writing lock the , -// it will blocks until the lock is released. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) RLockFunc(key string, f func()) { - l.RLock(key) - defer l.RUnlock(key) - f() -} - -// getExpire returns the duration object passed. -// If is not passed, it returns a default duration object. -func (l *Locker) getExpire(expire ...time.Duration) time.Duration { - e := time.Duration(0) - if len(expire) > 0 { - e = expire[0] - } - return e -} - -// doLock locks writing on . -// It returns true if success, or else returns false. -// -// The parameter is true, -// it returns false immediately if it fails getting the writing lock. -// If is false, it blocks until it gets the writing lock. -// -// The parameter specifies the max duration it locks. -func (l *Locker) doLock(key string, expire time.Duration, try bool) bool { - mu := l.getOrNewMutex(key) - ok := true - if try { - ok = mu.TryLock() - } else { - mu.Lock() - } - if ok && expire > 0 { - wid := mu.wid.Val() - gtimer.AddOnce(expire, func() { - if wid == mu.wid.Val() { - mu.Unlock() - } - }) - } - return ok -} - -// doRLock locks reading on . -// It returns true if success, or else returns false. -// -// The parameter is true, -// it returns false immediately if it fails getting the reading lock. -// If is false, it blocks until it gets the reading lock. -func (l *Locker) doRLock(key string, try bool) bool { - mu := l.getOrNewMutex(key) - ok := true - if try { - ok = mu.TryRLock() - } else { - mu.RLock() - } - return ok -} - // getOrNewMutex returns the mutex of given if it exists, // or else creates and returns a new one. -func (l *Locker) getOrNewMutex(key string) *Mutex { +func (l *Locker) getOrNewMutex(key string) *gmutex.Mutex { return l.m.GetOrSetFuncLock(key, func() interface{} { - return NewMutex() - }).(*Mutex) + return gmutex.New() + }).(*gmutex.Mutex) } diff --git a/g/os/gmlock/gmlock_unit_lock_test.go b/g/os/gmlock/gmlock_unit_lock_test.go index 6ebd23109..fcd730dd7 100644 --- a/g/os/gmlock/gmlock_unit_lock_test.go +++ b/g/os/gmlock/gmlock_unit_lock_test.go @@ -7,21 +7,23 @@ package gmlock_test import ( + "testing" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/os/gmlock" "github.com/gogf/gf/g/test/gtest" - "testing" - "time" ) -func TestLocker_Lock_Unlock(t *testing.T) { +func Test_Locker_Lock(t *testing.T) { + //no expire gtest.Case(t, func() { - key := "test1" + key := "testLock" array := garray.New() go func() { gmlock.Lock(key) array.Append(1) - time.Sleep(100 * time.Millisecond) + time.Sleep(50 * time.Millisecond) array.Append(1) gmlock.Unlock(key) }() @@ -29,71 +31,73 @@ func TestLocker_Lock_Unlock(t *testing.T) { time.Sleep(10 * time.Millisecond) gmlock.Lock(key) array.Append(1) - time.Sleep(200 * time.Millisecond) + time.Sleep(100 * time.Millisecond) array.Append(1) gmlock.Unlock(key) }() - time.Sleep(50 * time.Millisecond) + time.Sleep(10 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(80 * time.Millisecond) + time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 3) - time.Sleep(100 * time.Millisecond) + time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 3) - time.Sleep(100 * time.Millisecond) + time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 4) }) + } -func TestLocker_Lock_Expire(t *testing.T) { +func Test_Locker_LockFunc(t *testing.T) { + //no expire gtest.Case(t, func() { - key := "test2" + key := "testLockFunc" array := garray.New() go func() { - gmlock.Lock(key, 100*time.Millisecond) - array.Append(1) + gmlock.LockFunc(key, func() { + array.Append(1) + time.Sleep(50 * time.Millisecond) + }) // }() go func() { time.Sleep(10 * time.Millisecond) - gmlock.Lock(key) - time.Sleep(100 * time.Millisecond) - array.Append(1) - gmlock.Unlock(key) + gmlock.LockFunc(key, func() { + array.Append(1) + }) }() - time.Sleep(150 * time.Millisecond) + time.Sleep(10 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(250 * time.Millisecond) + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) // + time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 2) }) } - -func TestLocker_TryLock_Expire(t *testing.T) { +func Test_Locker_TryLockFunc(t *testing.T) { + //no expire gtest.Case(t, func() { - key := "test3" + key := "testTryLockFunc" array := garray.New() go func() { - gmlock.Lock(key, 200*time.Millisecond) - array.Append(1) + gmlock.TryLockFunc(key, func() { + array.Append(1) + time.Sleep(50 * time.Millisecond) + }) }() go func() { - time.Sleep(100 * time.Millisecond) - if !gmlock.TryLock(key) { + time.Sleep(10 * time.Millisecond) + gmlock.TryLockFunc(key, func() { array.Append(1) - } else { - gmlock.Unlock(key) - } + }) }() go func() { - time.Sleep(300 * time.Millisecond) - if gmlock.TryLock(key) { + time.Sleep(70 * time.Millisecond) + gmlock.TryLockFunc(key, func() { array.Append(1) - gmlock.Unlock(key) - } + }) }() time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(80 * time.Millisecond) + time.Sleep(100 * time.Millisecond) gtest.Assert(array.Len(), 2) - time.Sleep(350 * time.Millisecond) - gtest.Assert(array.Len(), 3) }) } diff --git a/g/os/gmlock/gmlock_unit_rlock_test.go b/g/os/gmlock/gmlock_unit_rlock_test.go index 6529114aa..ca596040f 100644 --- a/g/os/gmlock/gmlock_unit_rlock_test.go +++ b/g/os/gmlock/gmlock_unit_rlock_test.go @@ -7,16 +7,18 @@ package gmlock_test import ( + "testing" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/os/gmlock" "github.com/gogf/gf/g/test/gtest" - "testing" - "time" ) -func TestLocker_RLock1(t *testing.T) { +func Test_Locker_RLock(t *testing.T) { + //RLock before Lock gtest.Case(t, func() { - key := "test100" + key := "testRLockBeforeLock" array := garray.New() go func() { gmlock.RLock(key) @@ -36,16 +38,15 @@ func TestLocker_RLock1(t *testing.T) { time.Sleep(80 * time.Millisecond) gtest.Assert(array.Len(), 3) }) -} -func TestLocker_RLock2(t *testing.T) { + //Lock before RLock gtest.Case(t, func() { - key := "test200" + key := "testLockBeforeRLock" array := garray.New() go func() { gmlock.Lock(key) array.Append(1) - time.Sleep(100 * time.Millisecond) + time.Sleep(50 * time.Millisecond) gmlock.Unlock(key) }() go func() { @@ -54,10 +55,229 @@ func TestLocker_RLock2(t *testing.T) { array.Append(1) gmlock.RUnlock(key) }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) + //Lock before RLocks + gtest.Case(t, func() { + key := "testLockBeforeRLocks" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.RLock(key) + array.Append(1) + time.Sleep(70 * time.Millisecond) + gmlock.RUnlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.RLock(key) + array.Append(1) + time.Sleep(70 * time.Millisecond) + gmlock.RUnlock(key) + }() time.Sleep(20 * time.Millisecond) gtest.Assert(array.Len(), 1) time.Sleep(120 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Locker_TryRLock(t *testing.T) { + //Lock before TryRLock + gtest.Case(t, func() { + key := "testLockBeforeTryRLock" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + if gmlock.TryRLock(key) { + array.Append(1) + gmlock.RUnlock(key) + } + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + }) + + //Lock before TryRLocks + gtest.Case(t, func() { + key := "testLockBeforeTryRLocks" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + if gmlock.TryRLock(key) { + array.Append(1) + gmlock.RUnlock(key) + } + }() + go func() { + time.Sleep(70 * time.Millisecond) + if gmlock.TryRLock(key) { + array.Append(1) + gmlock.RUnlock(key) + } + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func Test_Locker_RLockFunc1(t *testing.T) { + //RLockFunc before Lock + gtest.Case(t, func() { + key := "testRLockFuncBeforeLock" + array := garray.New() + go func() { + gmlock.RLockFunc(key, func() { + array.Append(1) + time.Sleep(500 * time.Millisecond) + array.Append(1) + }) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.Lock(key) + array.Append(1) + gmlock.Unlock(key) + }() + time.Sleep(200 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(800 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) + + //Lock before RLockFunc + gtest.Case(t, func() { + key := "testLockBeforeRLockFunc" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.RLockFunc(key, func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) + +} + +func Test_Locker_RLockFunc2(t *testing.T) { + //Lock before RLockFuncs + gtest.Case(t, func() { + key := "testLockBeforeRLockFuncs" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + //glog.Println("add1") + time.Sleep(500 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(100 * time.Millisecond) + gmlock.RLockFunc(key, func() { + array.Append(1) + //glog.Println("add2") + time.Sleep(700 * time.Millisecond) + }) + }() + go func() { + time.Sleep(100 * time.Millisecond) + gmlock.RLockFunc(key, func() { + array.Append(1) + //glog.Println("add3") + time.Sleep(700 * time.Millisecond) + }) + }() + time.Sleep(200 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(700 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Locker_TryRLockFunc(t *testing.T) { + //Lock before TryRLockFunc + gtest.Case(t, func() { + key := "testLockBeforeTryRLockFunc" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.TryRLockFunc(key, func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + }) + + //Lock before TryRLockFuncs + gtest.Case(t, func() { + key := "testLockBeforeTryRLockFuncs" + array := garray.New() + go func() { + gmlock.Lock(key) + array.Append(1) + time.Sleep(50 * time.Millisecond) + gmlock.Unlock(key) + }() + go func() { + time.Sleep(10 * time.Millisecond) + gmlock.TryRLockFunc(key, func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(70 * time.Millisecond) + gmlock.TryRLockFunc(key, func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(70 * time.Millisecond) gtest.Assert(array.Len(), 2) }) } diff --git a/g/os/gmlock/gmlock_mutex.go b/g/os/gmutex/gmutex.go similarity index 52% rename from g/os/gmlock/gmlock_mutex.go rename to g/os/gmutex/gmutex.go index 73447024a..948f82d62 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmutex/gmutex.go @@ -1,36 +1,36 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2018-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 gmlock +// Package gmutex implements graceful concurrent-safe mutex with more rich features. +package gmutex import ( - "github.com/gogf/gf/g/container/gtype" + "math" "runtime" - "sync" + + "github.com/gogf/gf/g/container/gtype" ) -// The high level RWMutex. -// It wraps the sync.RWMutex to implements more rich features. +// The high level Mutex, which implements more rich features for mutex. type Mutex struct { - mu sync.RWMutex - wid *gtype.Int64 // Unique id, used for multiple and safe logic Unlock. - locking *gtype.Bool // Locking mark for atomic operation for *Lock and Try*Lock functions. - // There must be only one locking operation at the same time for concurrent safe purpose. - state *gtype.Int32 // Locking state: - // 0: writing lock false; - // -1: writing lock true; - // >=1: reading lock; + state *gtype.Int32 // Indicates the state of mutex. + writer *gtype.Int32 // Pending Writer count. + reader *gtype.Int32 // Pending reader count. + writing chan struct{} // Channel used for writer blocking. + reading chan struct{} // Channel used for reader blocking. } -// NewMutex creates and returns a new mutex. -func NewMutex() *Mutex { +// New creates and returns a new mutex. +func New() *Mutex { return &Mutex{ - wid: gtype.NewInt64(), state: gtype.NewInt32(), - locking: gtype.NewBool(), + writer: gtype.NewInt32(), + reader: gtype.NewInt32(), + writing: make(chan struct{}, 1), + reading: make(chan struct{}, math.MaxInt32), } } @@ -38,15 +38,15 @@ func NewMutex() *Mutex { // If the lock is already locked for reading or writing, // Lock blocks until the lock is available. func (m *Mutex) Lock() { - if m.locking.Cas(false, true) { - m.mu.Lock() - // State should be changed after locks. - m.state.Set(-1) - m.wid.Add(1) - m.locking.Set(false) - } else { - runtime.Gosched() - m.Lock() + for { + // If there're no readers pending and no writing lock currently, + // then do the writing lock checks. + if m.reader.Val() == 0 && m.state.Cas(0, -1) { + return + } + m.writer.Add(1) + <-m.writing + m.writer.Add(-1) } } @@ -54,7 +54,18 @@ func (m *Mutex) Lock() { // It is safe to be called multiple times if there's any locks or not. func (m *Mutex) Unlock() { if m.state.Cas(-1, 0) { - m.mu.Unlock() + // Writing lock unlocks, then first check the blocked readers. + if n := m.reader.Val(); n > 0 { + // If there're readers blocked, unlock them with preemption. + for i := n; i > 0; i-- { + m.reading <- struct{}{} + } + return + } + if m.writer.Val() > 0 { + m.writing <- struct{}{} + return + } } } @@ -62,14 +73,8 @@ func (m *Mutex) Unlock() { // It returns true if success, or if there's a write/reading lock on the mutex, // it returns false. func (m *Mutex) TryLock() bool { - if m.locking.Cas(false, true) { - if m.state.Cas(0, -1) { - m.mu.Lock() - m.wid.Add(1) - m.locking.Set(false) - return true - } - m.locking.Set(false) + if m.reader.Val() == 0 && m.state.Cas(0, -1) { + return true } return false } @@ -78,54 +83,79 @@ func (m *Mutex) TryLock() bool { // If the mutex is already locked for writing, // It blocks until the lock is available. func (m *Mutex) RLock() { - if m.locking.Cas(false, true) { - m.mu.RLock() - // State should be changed after locks. - m.state.Add(1) - m.locking.Set(false) - } else { - runtime.Gosched() - m.RLock() + for { + // If there no writing lock and no pending writers, + // then do the reading lock checks. + if n := m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if m.state.Cas(n, n+1) { + return + } else { + runtime.Gosched() + } + } + // Or else pending the reader. + m.reader.Add(1) + <-m.reading + m.reader.Add(-1) } } // RUnlock unlocks the reading lock. // It is safe to be called multiple times if there's any locks or not. func (m *Mutex) RUnlock() { - if n := m.state.Val(); n >= 1 { - if m.state.Cas(n, n-1) { - m.mu.RUnlock() + var n int32 + for { + if n = m.state.Val(); n >= 1 { + if m.state.Cas(n, n-1) { + break + } else { + runtime.Gosched() + } } else { - m.RUnlock() + break } } + // Reading lock unlocks, then first check the blocked writers. + if n == 1 && m.writer.Val() > 0 { + // No readers blocked, then the writers can take place. + m.writing <- struct{}{} + return + } } // TryRLock tries locking the mutex for reading. // It returns true if success, or if there's a writing lock on the mutex, it returns false. func (m *Mutex) TryRLock() bool { - if m.locking.Cas(false, true) { - if m.state.Val() >= 0 { - m.mu.RLock() - m.state.Add(1) - m.locking.Set(false) - return true + for { + if n := m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if m.state.Cas(n, n+1) { + return true + } else { + runtime.Gosched() + } + } else { + return false } } - return false } -// IsLocked checks whether the mutex is locked by writing or reading lock. +// IsLocked checks whether the mutex is locked with writing or reading lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsLocked() bool { return m.state.Val() != 0 } -// IsRLocked checks whether the mutex is locked by writing lock. +// IsWLocked checks whether the mutex is locked by writing lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsWLocked() bool { return m.state.Val() < 0 } // IsRLocked checks whether the mutex is locked by reading lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsRLocked() bool { return m.state.Val() > 0 } diff --git a/g/os/gmutex/gmutex_bench_test.go b/g/os/gmutex/gmutex_bench_test.go new file mode 100644 index 000000000..47b100f43 --- /dev/null +++ b/g/os/gmutex/gmutex_bench_test.go @@ -0,0 +1,47 @@ +// 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 gmutex_test + +import ( + "sync" + "testing" + + "github.com/gogf/gf/g/os/gmutex" +) + +var ( + mu = sync.RWMutex{} + gmu = gmutex.New() +) + +func Benchmark_Sync_LockUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + mu.Lock() + mu.Unlock() + } +} + +func Benchmark_Sync_RLockRUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + mu.RLock() + mu.RUnlock() + } +} + +func Benchmark_GMutex_LockUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.Lock() + gmu.Unlock() + } +} + +func Benchmark_GMutex_RLockRUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.RLock() + gmu.RUnlock() + } +} diff --git a/g/os/gmutex/gmutex_uinit_test.go b/g/os/gmutex/gmutex_uinit_test.go new file mode 100644 index 000000000..c3389c507 --- /dev/null +++ b/g/os/gmutex/gmutex_uinit_test.go @@ -0,0 +1,258 @@ +// 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 gmutex_test + +import ( + "testing" + "time" + + "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/os/gmutex" + "github.com/gogf/gf/g/test/gtest" +) + +func Test_Mutex_RUnlock(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + for index := 0; index < 1000; index++ { + go func() { + mu.RLockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + } + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), true) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), false) + for index := 0; index < 1000; index++ { + go func() { + mu.RUnlock() + }() + } + time.Sleep(150 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), false) + + }) +} + +func Test_Mutex_IsLocked(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + go func() { + mu.LockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), true) + gtest.Assert(mu.IsRLocked(), false) + time.Sleep(110 * time.Millisecond) + gtest.Assert(mu.IsLocked(), false) + gtest.Assert(mu.IsWLocked(), false) + + go func() { + mu.RLockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), true) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), false) + time.Sleep(110 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), false) + }) +} + +func Test_Mutex_Unlock(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + + go func() { + time.Sleep(60 * time.Millisecond) + mu.Unlock() + mu.Unlock() + mu.Unlock() + }() + + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Mutex_LockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func Test_Mutex_TryLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.TryLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(110 * time.Millisecond) + mu.TryLockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func Test_Mutex_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) + + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + gtest.Assert(array.Len(), 0) + time.Sleep(80 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Mutex_TryRLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(500 * time.Millisecond) + }) + }() + go func() { + time.Sleep(200 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(700 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(700 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(500 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(500 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} diff --git a/g/test/gtest/gtest_test.go b/g/test/gtest/gtest_test.go index 92ba83f7d..6b64ad151 100644 --- a/g/test/gtest/gtest_test.go +++ b/g/test/gtest/gtest_test.go @@ -7,12 +7,14 @@ package gtest_test import ( - "github.com/gogf/gf/g/test/gtest" "testing" + + "github.com/gogf/gf/g/test/gtest" ) func TestCase(t *testing.T) { gtest.Case(t, func() { gtest.Assert(1, 1) + gtest.AssertNE(1, 0) }) } diff --git a/g/text/gregex/gregex_z_bench_test.go b/g/text/gregex/gregex_z_bench_test.go index 8668ce37f..c8cfa553d 100644 --- a/g/text/gregex/gregex_z_bench_test.go +++ b/g/text/gregex/gregex_z_bench_test.go @@ -9,9 +9,10 @@ package gregex_test import ( - "github.com/gogf/gf/g/text/gregex" "regexp" "testing" + + "github.com/gogf/gf/g/text/gregex" ) var pattern = `(\w+).+\-\-\s*(.+)` diff --git a/g/util/gvalid/gvalid_check_map.go b/g/util/gvalid/gvalid_check_map.go index ed607a3bf..88a450476 100644 --- a/g/util/gvalid/gvalid_check_map.go +++ b/g/util/gvalid/gvalid_check_map.go @@ -7,8 +7,9 @@ package gvalid import ( - "github.com/gogf/gf/g/util/gconv" "strings" + + "github.com/gogf/gf/g/util/gconv" ) // 检测键值对参数Map, @@ -75,7 +76,7 @@ func CheckMap(params interface{}, rules interface{}, msgs ...CustomMsg) *Error { } } // 开始执行校验: 以校验规则作为基础进行遍历校验 - value := (interface{})(nil) + var value interface{} // 这里的rule变量为多条校验规则,不包含名字或者错误信息定义 for key, rule := range checkRules { // 如果规则为空,那么不执行校验 @@ -92,7 +93,7 @@ func CheckMap(params interface{}, rules interface{}, msgs ...CustomMsg) *Error { if value == nil || gconv.String(value) == "" { required := false // rule => error - for k, _ := range item { + for k := range item { if _, ok := mustCheckRulesEvenValueEmpty[k]; ok { required = true break diff --git a/g/util/gvalid/gvalid_check_struct.go b/g/util/gvalid/gvalid_check_struct.go index 9ad4e39df..e2750ad3d 100644 --- a/g/util/gvalid/gvalid_check_struct.go +++ b/g/util/gvalid/gvalid_check_struct.go @@ -7,10 +7,11 @@ package gvalid import ( + "strings" + "github.com/gogf/gf/g/text/gstr" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/third/github.com/fatih/structs" - "strings" ) // 校验struct对象属性,object参数也可以是一个指向对象的指针,返回值同CheckMap方法。 @@ -121,7 +122,7 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro /* 以下逻辑和CheckMap相同 */ // 开始执行校验: 以校验规则作为基础进行遍历校验 - value := (interface{})(nil) + var value interface{} // 这里的rule变量为多条校验规则,不包含名字或者错误信息定义 for key, rule := range checkRules { value = nil @@ -134,7 +135,7 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro if value == nil || gconv.String(value) == "" { required := false // rule => error - for k, _ := range item { + for k := range item { if _, ok := mustCheckRulesEvenValueEmpty[k]; ok { required = true break diff --git a/geg/other/test.go b/geg/other/test.go index 5d42430e6..520b0ef1e 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,43 +1,15 @@ package main import ( - "github.com/gogf/gf/g/container/garray" - "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gmlock" - "github.com/gogf/gf/g/test/gtest" - "time" + "fmt" + "math" + + "github.com/gogf/gf/g/container/gtype" ) func main() { - mu := gmlock.NewMutex() - array := garray.New() - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(10000 * time.Millisecond) - }) - }() - time.Sleep(10 * time.Millisecond) - for i := 0; i < 10000; i++ { - go func(i int) { - time.Sleep(50 * time.Millisecond) - mu.LockFunc(func() { - glog.Print(i) - array.Append(1) - }) - }(i) + v := gtype.NewInt32(math.MaxInt32) + for i := 1; i < 100; i++ { + fmt.Println(v.Add(int32(i))) } - go func() { - time.Sleep(60 * time.Millisecond) - mu.Unlock() - mu.Unlock() - mu.Unlock() - }() - - time.Sleep(20 * time.Millisecond) - gtest.Assert(array.Len(), 1) - time.Sleep(50 * time.Millisecond) - gtest.Assert(array.Len(), 1) - time.Sleep(50 * time.Millisecond) - gtest.Assert(array.Len(), 3) } diff --git a/geg/other/test2.go b/geg/other/test2.go index 521b49b46..594daf472 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,14 +1,55 @@ package main import ( - "fmt" - "github.com/gogf/gf/g/os/gmlock" "time" + + "github.com/gogf/gf/g/os/gmutex" + + "github.com/gogf/gf/g/os/glog" + + "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/test/gtest" ) func main() { - key := "test3" - gmlock.Lock(key, 200*time.Millisecond) - fmt.Println("TryLock:", gmlock.TryLock(key)) - fmt.Println("TryLock:", gmlock.TryLock(key)) + mu := gmutex.New() + array := garray.New() + glog.Println("step0") + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(200 * time.Millisecond) + glog.Println("unlocked") + }) + }() + go func() { + time.Sleep(150 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + glog.Println("add1") + }) + }() + sum := 1000 + for index := 1; index < sum; index++ { + go func(i int) { + time.Sleep(300 * time.Millisecond) + //fmt.Println(i*10, mu.IsLocked()) + if r := mu.TryRLockFunc(func() { + array.Append(1) + time.Sleep(200 * time.Millisecond) + }); !r { + glog.Println(i, r) + } + }(index) + } + glog.Println("step1") + time.Sleep(100 * time.Millisecond) + glog.Println("step2") + gtest.Assert(array.Len(), 1) + time.Sleep(100 * time.Millisecond) + glog.Println("step3") + gtest.Assert(array.Len(), 1) + time.Sleep(1000 * time.Millisecond) + gtest.Assert(array.Len(), sum) + } From d546424f181dc3039777e242284b12f51c1a4c68 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 21 Jun 2019 22:37:07 +0800 Subject: [PATCH 02/14] improve gmutex --- g/os/gmutex/gmutex.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/g/os/gmutex/gmutex.go b/g/os/gmutex/gmutex.go index 948f82d62..5535fc5de 100644 --- a/g/os/gmutex/gmutex.go +++ b/g/os/gmutex/gmutex.go @@ -17,7 +17,7 @@ import ( // The high level Mutex, which implements more rich features for mutex. type Mutex struct { state *gtype.Int32 // Indicates the state of mutex. - writer *gtype.Int32 // Pending Writer count. + writer *gtype.Int32 // Pending writer count. reader *gtype.Int32 // Pending reader count. writing chan struct{} // Channel used for writer blocking. reading chan struct{} // Channel used for reader blocking. @@ -39,7 +39,7 @@ func New() *Mutex { // Lock blocks until the lock is available. func (m *Mutex) Lock() { for { - // If there're no readers pending and no writing lock currently, + // If there're no readers pending and writing lock currently, // then do the writing lock checks. if m.reader.Val() == 0 && m.state.Cas(0, -1) { return @@ -57,14 +57,13 @@ func (m *Mutex) Unlock() { // Writing lock unlocks, then first check the blocked readers. if n := m.reader.Val(); n > 0 { // If there're readers blocked, unlock them with preemption. - for i := n; i > 0; i-- { + for ; n > 0; n-- { m.reading <- struct{}{} } return } if m.writer.Val() > 0 { m.writing <- struct{}{} - return } } } @@ -83,14 +82,16 @@ func (m *Mutex) TryLock() bool { // If the mutex is already locked for writing, // It blocks until the lock is available. func (m *Mutex) RLock() { + var n int32 for { - // If there no writing lock and no pending writers, + // If there're no writing lock and pending writers currently, // then do the reading lock checks. - if n := m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 { if m.state.Cas(n, n+1) { return } else { runtime.Gosched() + continue } } // Or else pending the reader. @@ -126,8 +127,9 @@ func (m *Mutex) RUnlock() { // TryRLock tries locking the mutex for reading. // It returns true if success, or if there's a writing lock on the mutex, it returns false. func (m *Mutex) TryRLock() bool { + var n int32 for { - if n := m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 { if m.state.Cas(n, n+1) { return true } else { From 912b743a1e0e8c73e4da7fbeedeea604bfa4fa2c Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 11:03:50 +0800 Subject: [PATCH 03/14] improve Range/SubSlice functions for garray --- g/container/garray/garray_normal_int.go | 100 +++++++++++------ g/container/garray/garray_normal_interface.go | 100 +++++++++++------ g/container/garray/garray_normal_string.go | 102 ++++++++++++------ g/container/garray/garray_sorted_int.go | 100 +++++++++++------ g/container/garray/garray_sorted_interface.go | 100 +++++++++++------ g/container/garray/garray_sorted_string.go | 102 ++++++++++++------ g/container/garray/garray_z_unit_int_test.go | 17 ++- g/net/ghttp/ghttp_server.go | 20 ++-- geg/other/test.go | 13 +-- geg/other/test2.go | 80 ++++++-------- 10 files changed, 460 insertions(+), 274 deletions(-) diff --git a/g/container/garray/garray_normal_int.go b/g/container/garray/garray_normal_int.go index 2be98a810..339063ddf 100644 --- a/g/container/garray/garray_normal_int.go +++ b/g/container/garray/garray_normal_int.go @@ -9,11 +9,12 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) type IntArray struct { @@ -266,31 +267,83 @@ func (a *IntArray) PopRights(size int) []int { // 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. -func (a *IntArray) Range(start, end int) []int { +// +// If is negative, then the offset will start from the end of array. +// If 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() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]int)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]int, end-start) - copy(array, a.array[start:end]) + array = make([]int, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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() @@ -488,27 +541,6 @@ func (a *IntArray) Pad(size int, value int) *IntArray { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *IntArray) SubSlice(offset, size int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *IntArray) Rand() int { a.mu.RLock() diff --git a/g/container/garray/garray_normal_interface.go b/g/container/garray/garray_normal_interface.go index e95048ef2..c91cd2d97 100644 --- a/g/container/garray/garray_normal_interface.go +++ b/g/container/garray/garray_normal_interface.go @@ -9,11 +9,12 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) type Array struct { @@ -262,31 +263,83 @@ func (a *Array) PopRights(size int) []interface{} { // 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. -func (a *Array) Range(start, end int) []interface{} { +// +// If is negative, then the offset will start from the end of array. +// If 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() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]interface{})(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]interface{}, end-start) - copy(array, a.array[start:end]) + array = make([]interface{}, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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...) @@ -482,27 +535,6 @@ func (a *Array) Pad(size int, val interface{}) *Array { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *Array) SubSlice(offset, size int) []interface{} { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *Array) Rand() interface{} { a.mu.RLock() diff --git a/g/container/garray/garray_normal_string.go b/g/container/garray/garray_normal_string.go index 702ecda52..bfc61092e 100644 --- a/g/container/garray/garray_normal_string.go +++ b/g/container/garray/garray_normal_string.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" - "github.com/gogf/gf/g/util/grand" "math" "sort" "strings" + + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/util/grand" ) type StringArray struct { @@ -267,31 +268,83 @@ func (a *StringArray) PopRights(size int) []string { // 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. -func (a *StringArray) Range(start, end int) []string { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *StringArray) Range(start int, end ...int) []string { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]string)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]string, end-start) - copy(array, a.array[start:end]) + array = make([]string, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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 *StringArray) 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 *StringArray) Append(value ...string) *StringArray { a.mu.Lock() @@ -488,27 +541,6 @@ func (a *StringArray) Pad(size int, value string) *StringArray { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *StringArray) SubSlice(offset, size int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *StringArray) Rand() string { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_int.go b/g/container/garray/garray_sorted_int.go index e7f792479..8fcd9a8e7 100644 --- a/g/container/garray/garray_sorted_int.go +++ b/g/container/garray/garray_sorted_int.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) // It's using increasing order in default. @@ -216,31 +217,83 @@ func (a *SortedIntArray) PopRights(size int) []int { // 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. -func (a *SortedIntArray) Range(start, end int) []int { +// +// If is negative, then the offset will start from the end of array. +// If 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() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]int)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]int, end-start) - copy(array, a.array[start:end]) + array = make([]int, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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() @@ -433,27 +486,6 @@ func (a *SortedIntArray) Chunk(size int) [][]int { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedIntArray) SubSlice(offset, size int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedIntArray) Rand() int { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_interface.go b/g/container/garray/garray_sorted_interface.go index cb18f5933..ea2966fae 100644 --- a/g/container/garray/garray_sorted_interface.go +++ b/g/container/garray/garray_sorted_interface.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) // It's using increasing order in default. @@ -217,31 +218,83 @@ func (a *SortedArray) PopRights(size int) []interface{} { // 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. -func (a *SortedArray) Range(start, end int) []interface{} { +// +// If is negative, then the offset will start from the end of array. +// If 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() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]interface{})(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]interface{}, end-start) - copy(array, a.array[start:end]) + array = make([]interface{}, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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() @@ -434,27 +487,6 @@ func (a *SortedArray) Chunk(size int) [][]interface{} { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedArray) SubSlice(offset, size int) []interface{} { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedArray) Rand() interface{} { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_string.go b/g/container/garray/garray_sorted_string.go index 2092d4f18..89e06b43d 100644 --- a/g/container/garray/garray_sorted_string.go +++ b/g/container/garray/garray_sorted_string.go @@ -9,13 +9,14 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "strings" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" - "strings" ) // It's using increasing order in default. @@ -211,31 +212,83 @@ func (a *SortedStringArray) PopRights(size int) []string { // 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. -func (a *SortedStringArray) Range(start, end int) []string { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *SortedStringArray) Range(start int, end ...int) []string { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]string)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]string, end-start) - copy(array, a.array[start:end]) + array = make([]string, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and 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 *SortedStringArray) 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 *SortedStringArray) Sum() (sum int) { a.mu.RLock() @@ -428,27 +481,6 @@ func (a *SortedStringArray) Chunk(size int) [][]string { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedStringArray) SubSlice(offset, size int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > 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:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedStringArray) Rand() string { a.mu.RLock() diff --git a/g/container/garray/garray_z_unit_int_test.go b/g/container/garray/garray_z_unit_int_test.go index 6d7e53377..2a65f89f3 100644 --- a/g/container/garray/garray_z_unit_int_test.go +++ b/g/container/garray/garray_z_unit_int_test.go @@ -9,9 +9,10 @@ package garray_test import ( + "testing" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_IntArray_Basic(t *testing.T) { @@ -100,6 +101,7 @@ func TestIntArray_Range(t *testing.T) { gtest.Assert(array1.Range(0, 1), []int{0}) gtest.Assert(array1.Range(1, 2), []int{1}) gtest.Assert(array1.Range(0, 2), []int{0, 1}) + gtest.Assert(array1.Range(10, 2), nil) gtest.Assert(array1.Range(-1, 10), value1) }) } @@ -151,9 +153,21 @@ func TestIntArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []int{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewIntArrayFrom(a1) + gtest.Assert(array1.SubSlice(6), []int{6}) + gtest.Assert(array1.SubSlice(5), []int{5, 6}) + gtest.Assert(array1.SubSlice(8), nil) gtest.Assert(array1.SubSlice(0, 2), []int{0, 1}) gtest.Assert(array1.SubSlice(2, 2), []int{2, 3}) gtest.Assert(array1.SubSlice(5, 8), []int{5, 6}) + gtest.Assert(array1.SubSlice(-1, 1), []int{6}) + gtest.Assert(array1.SubSlice(-1, 9), []int{6}) + gtest.Assert(array1.SubSlice(-2, 3), []int{5, 6}) + gtest.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2}) + gtest.Assert(array1.SubSlice(-8, 3), nil) + gtest.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5}) + gtest.Assert(array1.SubSlice(-9, 3), nil) + gtest.Assert(array1.SubSlice(1, -1), []int{0}) + gtest.Assert(array1.SubSlice(1, -3), nil) }) } @@ -435,7 +449,6 @@ func TestSortedIntArray_Chunk(t *testing.T) { array1 := garray.NewSortedIntArrayFrom(a1) ns1 := array1.Chunk(2) //按每几个元素切成一个数组 ns2 := array1.Chunk(-1) - t.Log(ns1) gtest.Assert(len(ns1), 3) gtest.Assert(ns1[0], []int{1, 2}) gtest.Assert(ns1[2], []int{5}) diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 98f58c6ca..1f2886ced 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -10,6 +10,14 @@ import ( "bytes" "errors" "fmt" + "net/http" + "os" + "reflect" + "runtime" + "strings" + "sync" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gtype" @@ -23,13 +31,6 @@ import ( "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/third/github.com/gorilla/websocket" "github.com/gogf/gf/third/github.com/olekukonko/tablewriter" - "net/http" - "os" - "reflect" - "runtime" - "strings" - "sync" - "time" ) type ( @@ -382,15 +383,14 @@ func (s *Server) GetRouteMap() string { } // 阻塞执行监听 -func (s *Server) Run() error { +func (s *Server) Run() { if err := s.Start(); err != nil { - return err + glog.Fatal(err) } // 阻塞等待服务执行完成 <-s.closeChan glog.Printf("%d: all servers shutdown", gproc.Pid()) - return nil } // 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景 diff --git a/geg/other/test.go b/geg/other/test.go index 520b0ef1e..6ec392dcf 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,15 +1,12 @@ package main import ( - "fmt" - "math" - - "github.com/gogf/gf/g/container/gtype" + "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/test/gtest" ) func main() { - v := gtype.NewInt32(math.MaxInt32) - for i := 1; i < 100; i++ { - fmt.Println(v.Add(int32(i))) - } + a2 := []interface{}{0, 1, 2, 3, 4, 5, 6} + array3 := garray.NewArrayFrom(a2, true) + gtest.Assert(array3.SubSlice(2, 2), []interface{}{2, 3}) } diff --git a/geg/other/test2.go b/geg/other/test2.go index 594daf472..8623d32ca 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,55 +1,39 @@ package main import ( - "time" - - "github.com/gogf/gf/g/os/gmutex" - - "github.com/gogf/gf/g/os/glog" - - "github.com/gogf/gf/g/container/garray" - "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" ) -func main() { - mu := gmutex.New() - array := garray.New() - glog.Println("step0") - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(200 * time.Millisecond) - glog.Println("unlocked") - }) - }() - go func() { - time.Sleep(150 * time.Millisecond) - mu.TryRLockFunc(func() { - array.Append(1) - glog.Println("add1") - }) - }() - sum := 1000 - for index := 1; index < sum; index++ { - go func(i int) { - time.Sleep(300 * time.Millisecond) - //fmt.Println(i*10, mu.IsLocked()) - if r := mu.TryRLockFunc(func() { - array.Append(1) - time.Sleep(200 * time.Millisecond) - }); !r { - glog.Println(i, r) - } - }(index) - } - glog.Println("step1") - time.Sleep(100 * time.Millisecond) - glog.Println("step2") - gtest.Assert(array.Len(), 1) - time.Sleep(100 * time.Millisecond) - glog.Println("step3") - gtest.Assert(array.Len(), 1) - time.Sleep(1000 * time.Millisecond) - gtest.Assert(array.Len(), sum) +type Schedule struct{} +type Task struct{} + +func (c *Schedule) ListDir(r *ghttp.Request) { + r.Response.Writeln("ListDir") +} + +func (c *Task) Add(r *ghttp.Request) { + r.Response.Writeln("Add") +} + +func (c *Task) Task(r *ghttp.Request) { + r.Response.Writeln("Task") +} + +// 实现权限校验 +// 通过事件回调,类似于中间件机制,但是可控制的粒度更细,可以精准注册到路由规则 +func AuthHookHandler(r *ghttp.Request) { + // 如果权限校验失败,调用 r.ExitAll() 退出执行流程 +} + +func main() { + s := g.Server() + s.Group("/schedule").Bind([]ghttp.GroupItem{ + {"ALL", "*", AuthHookHandler, ghttp.HOOK_BEFORE_SERVE}, + {"POST", "/schedule", new(Schedule)}, + {"POST", "/task", new(Task)}, + }) + s.SetPort(8199) + s.Run() } From c88f516759bdb17b70be64d70b94a639da636446 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 11:29:51 +0800 Subject: [PATCH 04/14] remove mutex usage from gflock --- g/os/gflock/gflock.go | 66 ++++++++++++++++++++++++++++------------- geg/os/gflock/flock.go | 3 +- geg/os/gflock/gflock.go | 5 ++-- geg/other/test.go | 10 +++---- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/g/os/gflock/gflock.go b/g/os/gflock/gflock.go index 9bcc0d6e9..e629720ca 100644 --- a/g/os/gflock/gflock.go +++ b/g/os/gflock/gflock.go @@ -10,13 +10,11 @@ package gflock import ( "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/third/github.com/theckman/go-flock" - "sync" ) // File locker. type Locker struct { - mu sync.RWMutex // 用于外部接口调用的互斥锁(阻塞机制) - flock *flock.Flock // 底层文件锁对象 + flock *flock.Flock // Underlying file locker. } // New creates and returns a new file locker with given . @@ -47,9 +45,6 @@ func (l *Locker) IsLocked() bool { // It returns true if success, or else returns false immediately. func (l *Locker) TryLock() bool { ok, _ := l.flock.TryLock() - if ok { - l.mu.Lock() - } return ok } @@ -57,32 +52,61 @@ func (l *Locker) TryLock() bool { // It returns true if success, or else returns false immediately. func (l *Locker) TryRLock() bool { ok, _ := l.flock.TryRLock() - if ok { - l.mu.RLock() - } return ok } +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), this may transparently replace the +// shared lock with an exclusive lock on some UNIX-like operating systems. Be +// careful when using exclusive locks in conjunction with shared locks +// (RLock()), because calling Unlock() may accidentally release the exclusive +// lock that was once a shared lock. func (l *Locker) Lock() (err error) { - l.mu.Lock() - err = l.flock.Lock() - return + return l.flock.Lock() } +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. func (l *Locker) UnLock() (err error) { - err = l.flock.Unlock() - l.mu.Unlock() - return + return l.flock.Unlock() } +// RLock is a blocking call to try and take a ahred file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. func (l *Locker) RLock() (err error) { - l.mu.RLock() - err = l.flock.RLock() - return + return l.flock.RLock() } +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. func (l *Locker) RUnlock() (err error) { - err = l.flock.Unlock() - l.mu.RUnlock() - return + return l.flock.Unlock() } diff --git a/geg/os/gflock/flock.go b/geg/os/gflock/flock.go index 7cd2d0e09..5d9271383 100644 --- a/geg/os/gflock/flock.go +++ b/geg/os/gflock/flock.go @@ -2,8 +2,9 @@ package main import ( "fmt" - "github.com/gogf/gf/third/github.com/theckman/go-flock" "time" + + "github.com/gogf/gf/third/github.com/theckman/go-flock" ) func main() { diff --git a/geg/os/gflock/gflock.go b/geg/os/gflock/gflock.go index f6c2b929a..07b3b8033 100644 --- a/geg/os/gflock/gflock.go +++ b/geg/os/gflock/gflock.go @@ -1,17 +1,18 @@ package main import ( + "time" + "github.com/gogf/gf/g/os/gflock" "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gproc" - "time" ) func main() { l := gflock.New("demo.lock") l.Lock() glog.Printf("locked by pid: %d", gproc.Pid()) - time.Sleep(3 * time.Second) + time.Sleep(10 * time.Second) l.UnLock() glog.Printf("unlocked by pid: %d", gproc.Pid()) } diff --git a/geg/other/test.go b/geg/other/test.go index 6ec392dcf..7fc1f4910 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,12 +1,12 @@ package main import ( - "github.com/gogf/gf/g/container/garray" - "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/os/glog" ) func main() { - a2 := []interface{}{0, 1, 2, 3, 4, 5, 6} - array3 := garray.NewArrayFrom(a2, true) - gtest.Assert(array3.SubSlice(2, 2), []interface{}{2, 3}) + glog.SetDebug(false) + glog.Warning(1) + glog.SetDebug(true) + glog.Warning(1) } From 9a507b54d712d0140b074aab31f8d0485d354d6b Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 11:45:58 +0800 Subject: [PATCH 05/14] add Remove/Clear functions for gmlock --- g/os/gmlock/gmlock.go | 5 +++++ g/os/gmlock/gmlock_locker.go | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/g/os/gmlock/gmlock.go b/g/os/gmlock/gmlock.go index dcfb0c557..a6baa38ee 100644 --- a/g/os/gmlock/gmlock.go +++ b/g/os/gmlock/gmlock.go @@ -82,3 +82,8 @@ func TryLockFunc(key string, f func()) bool { func TryRLockFunc(key string, f func()) bool { return locker.TryRLockFunc(key, f) } + +// Remove removes mutex with given . +func Remove(key string) { + locker.Remove(key) +} diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index 8041fd270..ce00c132d 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -12,7 +12,8 @@ import ( ) // Memory locker. -// Note that there's no cache expire mechanism for attribute map . +// Note that there's no cache expire mechanism for mutex in locker. +// You need remove certain mutex manually when you do not want use it any more. type Locker struct { m *gmap.StrAnyMap } @@ -113,6 +114,16 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool { return false } +// Remove removes mutex with given from locker. +func (l *Locker) Remove(key string) { + l.m.Remove(key) +} + +// Clear removes all mutexes from locker. +func (l *Locker) Clear() { + l.m.Clear() +} + // getOrNewMutex returns the mutex of given if it exists, // or else creates and returns a new one. func (l *Locker) getOrNewMutex(key string) *gmutex.Mutex { From 6ee6c007c5c4ce5a2037b0702a3cc924b26b3073 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 14:42:27 +0800 Subject: [PATCH 06/14] update gmutex and its benchmark test cases --- g/os/gmutex/gmutex.go | 41 ++++++++++++++++---------------- g/os/gmutex/gmutex_bench_test.go | 32 ++++++++++++++++++++----- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/g/os/gmutex/gmutex.go b/g/os/gmutex/gmutex.go index 5535fc5de..7acc97df9 100644 --- a/g/os/gmutex/gmutex.go +++ b/g/os/gmutex/gmutex.go @@ -120,7 +120,6 @@ func (m *Mutex) RUnlock() { if n == 1 && m.writer.Val() > 0 { // No readers blocked, then the writers can take place. m.writing <- struct{}{} - return } } @@ -162,6 +161,26 @@ func (m *Mutex) IsRLocked() bool { return m.state.Val() > 0 } +// LockFunc locks the mutex for writing with given callback function . +// If there's a write/reading lock the mutex, it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (m *Mutex) LockFunc(f func()) { + m.Lock() + defer m.Unlock() + f() +} + +// RLockFunc locks the mutex for reading with given callback function . +// If there's a writing lock the mutex, it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (m *Mutex) RLockFunc(f func()) { + m.RLock() + defer m.RUnlock() + f() +} + // TryLockFunc tries locking the mutex for writing with given callback function . // it returns true if success, or if there's a write/reading lock on the mutex, // it returns false. @@ -188,23 +207,3 @@ func (m *Mutex) TryRLockFunc(f func()) bool { } return false } - -// LockFunc locks the mutex for writing with given callback function . -// If there's a write/reading lock the mutex, it will blocks until the lock is released. -// -// It releases the lock after is executed. -func (m *Mutex) LockFunc(f func()) { - m.Lock() - defer m.Unlock() - f() -} - -// RLockFunc locks the mutex for reading with given callback function . -// If there's a writing lock the mutex, it will blocks until the lock is released. -// -// It releases the lock after is executed. -func (m *Mutex) RLockFunc(f func()) { - m.RLock() - defer m.RUnlock() - f() -} diff --git a/g/os/gmutex/gmutex_bench_test.go b/g/os/gmutex/gmutex_bench_test.go index 47b100f43..9b7473bd2 100644 --- a/g/os/gmutex/gmutex_bench_test.go +++ b/g/os/gmutex/gmutex_bench_test.go @@ -14,21 +14,29 @@ import ( ) var ( - mu = sync.RWMutex{} - gmu = gmutex.New() + mu = sync.Mutex{} + rwmu = sync.RWMutex{} + gmu = gmutex.New() ) -func Benchmark_Sync_LockUnlock(b *testing.B) { +func Benchmark_Mutex_LockUnlock(b *testing.B) { for i := 0; i < b.N; i++ { mu.Lock() mu.Unlock() } } -func Benchmark_Sync_RLockRUnlock(b *testing.B) { +func Benchmark_RWMutex_LockUnlock(b *testing.B) { for i := 0; i < b.N; i++ { - mu.RLock() - mu.RUnlock() + rwmu.Lock() + rwmu.Unlock() + } +} + +func Benchmark_RWMutex_RLockRUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + rwmu.RLock() + rwmu.RUnlock() } } @@ -39,9 +47,21 @@ func Benchmark_GMutex_LockUnlock(b *testing.B) { } } +func Benchmark_GMutex_TryLock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.TryLock() + } +} + func Benchmark_GMutex_RLockRUnlock(b *testing.B) { for i := 0; i < b.N; i++ { gmu.RLock() gmu.RUnlock() } } + +func Benchmark_GMutex_TryRLock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.TryRLock() + } +} From b7e0f1f983bd01f60adcb3f7357e0280758cf757 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 15:05:15 +0800 Subject: [PATCH 07/14] improve grpool --- g/os/grpool/grpool.go | 46 ++++++++++++++++++++++++++++++------------- geg/other/test.go | 18 ++++++++++++----- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/g/os/grpool/grpool.go b/g/os/grpool/grpool.go index b37a567c7..bb12a265d 100644 --- a/g/os/grpool/grpool.go +++ b/g/os/grpool/grpool.go @@ -8,6 +8,8 @@ package grpool import ( + "errors" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gtype" ) @@ -41,35 +43,46 @@ func New(limit ...int) *Pool { // Add pushes a new job to the pool using default goroutine pool. // The job will be executed asynchronously. -func Add(f func()) { - pool.Add(f) +func Add(f func()) error { + return pool.Add(f) } // Size returns current goroutine count of default goroutine pool. func Size() int { - return pool.count.Val() + return pool.Size() } // Jobs returns current job count of default goroutine pool. func Jobs() int { - return pool.list.Len() + return pool.Jobs() } // Add pushes a new job to the pool. // The job will be executed asynchronously. -func (p *Pool) Add(f func()) { +func (p *Pool) Add(f func()) error { + for p.closed.Val() { + return errors.New("pool closed") + } p.list.PushFront(f) - // check whether to create a new goroutine or not. - if p.count.Val() == p.limit { - return + var n int + for { + n = p.count.Val() + if p.limit != -1 && n >= p.limit { + return nil + } + if p.count.Cas(n, n+1) { + break + } } - // ensure atomicity. - if p.limit != -1 && p.count.Add(1) > p.limit { - p.count.Add(-1) - return - } - // fork a new goroutine to consume the job list. p.fork() + return nil +} + +// Cap returns the capacity of the pool. +// This capacity is defined when pool is created. +// If it returns -1 means no limit. +func (p *Pool) Cap() int { + return p.limit } // Size returns current goroutine count of the pool. @@ -97,6 +110,11 @@ func (p *Pool) fork() { }() } +// IsClosed returns if pool is closed. +func (p *Pool) IsClosed() bool { + return p.closed.Val() +} + // Close closes the goroutine pool, which makes all goroutines exit. func (p *Pool) Close() { p.closed.Set(true) diff --git a/geg/other/test.go b/geg/other/test.go index 7fc1f4910..60674ee90 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,12 +1,20 @@ package main import ( - "github.com/gogf/gf/g/os/glog" + "fmt" + "time" + + "github.com/gogf/gf/g/os/grpool" ) func main() { - glog.SetDebug(false) - glog.Warning(1) - glog.SetDebug(true) - glog.Warning(1) + grpool.Add(func() { + + }) + fmt.Println(grpool.Size()) + fmt.Println(grpool.Jobs()) + time.Sleep(time.Second) + fmt.Println(grpool.Size()) + fmt.Println(grpool.Jobs()) + } From bb935967ac8df4338b293fd9508c1dd3f2e62377 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 17:02:36 +0800 Subject: [PATCH 08/14] add MarshalJSON function for gjson/gparser to implements the interface MarshalJSON for json.Marshal --- g/encoding/gjson/gjson.go | 15 +++++-- g/encoding/gparser/gparser_api_encoding.go | 5 +++ geg/other/test.go | 51 +++++++++++++++++----- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index 755a658f4..4af8249ee 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -8,12 +8,13 @@ package gjson import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" "reflect" "strconv" "strings" + + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" ) const ( @@ -26,8 +27,14 @@ type Json struct { mu *rwmutex.RWMutex p *interface{} // Pointer for hierarchical data access, it's the root of data in default. c byte // Char separator('.' in default). - vc bool // Violence Check(false in default), which is used to access data + // Violence Check(false in default), which is used to access data // when the hierarchical data key contains separator char. + vc bool +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (j *Json) MarshalJSON() ([]byte, error) { + return j.ToJson() } // Set by . diff --git a/g/encoding/gparser/gparser_api_encoding.go b/g/encoding/gparser/gparser_api_encoding.go index d1208b2c4..efc342731 100644 --- a/g/encoding/gparser/gparser_api_encoding.go +++ b/g/encoding/gparser/gparser_api_encoding.go @@ -6,6 +6,11 @@ package gparser +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (p *Parser) MarshalJSON() ([]byte, error) { + return p.json.MarshalJSON() +} + func (p *Parser) ToXml(rootTag ...string) ([]byte, error) { return p.json.ToXml(rootTag...) } diff --git a/geg/other/test.go b/geg/other/test.go index 60674ee90..d856ff4b0 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,19 +2,50 @@ package main import ( "fmt" - "time" - "github.com/gogf/gf/g/os/grpool" + "github.com/gogf/gf/g/encoding/gjson" + "github.com/gogf/gf/g/util/gconv" ) -func main() { - grpool.Add(func() { +//type TemplateMessage struct { +// Touser string `json:"touser,omitempty"` +// TemplateId string `json:"template_id,omitempty"` +// Miniprogram interface{} `json:"miniprograme,omitempty"` +// Data interface{} `json:"data,omitempty"` +//} - }) - fmt.Println(grpool.Size()) - fmt.Println(grpool.Jobs()) - time.Sleep(time.Second) - fmt.Println(grpool.Size()) - fmt.Println(grpool.Jobs()) +type TemplateMessage struct { + Touser string `json:"touser,omitempty"` + TemplateId string `json:"template_id,omitempty"` + Miniprogram *gjson.Json `json:"miniprograme,omitempty"` + Data *gjson.Json `json:"data,omitempty"` +} + +// 封装模版消息 +func getTemplateMessage(message string) string { + templateId := "22222222" + miniprogram := `{"appid":"111111111","pagepath":"pages\/index?ald_media_id=20962&ald_link_key=bd660b4962a599f2"}` + data := `{"first":{"value":"送您一个随机红包,点击领取¥0.3-¥10","color":"#FF0000"},"keyword1":{"value":"¥0.3-¥10"},"keyword2":{"value":"2019年06月19日 11:45"},"keyword3":{"value":"微信零钱"},"keyword4":{"value":"点击此消息即可提现","color":"#FF0000"}}` + miniprogramJson := gjson.New(miniprogram) + dataJson := gjson.New(data) + //glog.Infof(miniprogramJson.ToJsonString()) + //glog.Info(dataJson.ToJsonString()) + + templateMessage := TemplateMessage{ + Touser: message, + TemplateId: templateId, + Miniprogram: miniprogramJson, + Data: dataJson, + } + + //glog.Debug(dataJson.ToJsonString()) + //json, _ := gjson.New(templateMessage).ToJsonString() + //return json + //glog.Info(templateMessage.Miniprogram.ToJsonString()) + return gconv.String(templateMessage) +} + +func main() { + fmt.Println(getTemplateMessage("test")) } From 9248fd6b28ec2a9af59894447d5435bc833f881b Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 22:05:39 +0800 Subject: [PATCH 09/14] add internal/errors --- g/crypto/gmd5/gmd5.go | 5 +--- g/crypto/gsha1/gsha1.go | 6 ++-- g/internal/errors/errors.go | 48 ++++++++++++++++++++++++++++++++ g/internal/errors/errors_test.go | 39 ++++++++++++++++++++++++++ g/net/gtcp/gtcp_conn.go | 11 +++----- g/net/gtcp/gtcp_conn_pkg.go | 11 +++----- g/net/gtcp/gtcp_pool.go | 11 +++----- g/net/gtcp/gtcp_pool_pkg.go | 11 +++----- g/net/gudp/gudp_conn.go | 11 +++----- geg/other/test.go | 46 ------------------------------ go.mod | 2 +- 11 files changed, 111 insertions(+), 90 deletions(-) create mode 100644 g/internal/errors/errors.go create mode 100644 g/internal/errors/errors_test.go diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index a4829e97a..26888db0a 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -9,7 +9,6 @@ package gmd5 import ( "crypto/md5" - "errors" "fmt" "io" "os" @@ -40,9 +39,7 @@ func EncryptFile(path string) (encrypt string, err error) { return "", err } defer func() { - if e := f.Close(); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(f.Close(), "file closing error") }() h := md5.New() _, err = io.Copy(h, f) diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go index b296bdb74..e38a8299c 100644 --- a/g/crypto/gsha1/gsha1.go +++ b/g/crypto/gsha1/gsha1.go @@ -10,10 +10,10 @@ package gsha1 import ( "crypto/sha1" "encoding/hex" - "errors" "io" "os" + "github.com/gogf/gf/g/internal/errors" "github.com/gogf/gf/g/util/gconv" ) @@ -37,9 +37,7 @@ func EncryptFile(path string) (encrypt string, err error) { return "", err } defer func() { - if e := f.Close(); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(f.Close(), "file closing error") }() h := sha1.New() _, err = io.Copy(h, f) diff --git a/g/internal/errors/errors.go b/g/internal/errors/errors.go new file mode 100644 index 000000000..7d6270869 --- /dev/null +++ b/g/internal/errors/errors.go @@ -0,0 +1,48 @@ +// 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 errors provides simple functions to manipulate errors. +// +// This package can be scalable due to https://go.googlesource.com/proposal/+/master/design/go2draft.md. +package errors + +import "github.com/gogf/gf/g/util/gconv" + +// errorWrapper is a simple wrapper for errors. +type errorWrapper struct { + s string +} + +// New returns an error that formats as the given value. +func New(value interface{}) error { + if value == nil { + return nil + } + return NewText(gconv.String(value)) +} + +// NewText returns an error that formats as the given text. +func NewText(text string) error { + if text == "" { + return nil + } + return &errorWrapper{ + s: text, + } +} + +// Wrap wraps error with text. +func Wrap(err error, text string) error { + if err == nil { + return nil + } + return NewText(text + ": " + err.Error()) +} + +// Error implements interface Error. +func (e *errorWrapper) Error() string { + return e.s +} diff --git a/g/internal/errors/errors_test.go b/g/internal/errors/errors_test.go new file mode 100644 index 000000000..7d0328baa --- /dev/null +++ b/g/internal/errors/errors_test.go @@ -0,0 +1,39 @@ +// 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 errors_test + +import ( + "testing" + + "github.com/gogf/gf/g/internal/errors" + "github.com/gogf/gf/g/test/gtest" +) + +func interfaceNil() interface{} { + return nil +} + +func nilError() error { + return nil +} + +func Test_Nil(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(errors.New(interfaceNil()), nil) + gtest.Assert(errors.Wrap(nilError(), "test"), nil) + }) +} + +func Test_Wrap(t *testing.T) { + gtest.Case(t, func() { + err := errors.New("1") + err = errors.Wrap(err, "func2 error") + err = errors.Wrap(err, "func3 error") + gtest.AssertNE(err, nil) + gtest.Assert(err.Error(), "func3 error: func2 error: 1") + }) +} diff --git a/g/net/gtcp/gtcp_conn.go b/g/net/gtcp/gtcp_conn.go index f1ace01e9..43ee80ec3 100644 --- a/g/net/gtcp/gtcp_conn.go +++ b/g/net/gtcp/gtcp_conn.go @@ -10,10 +10,11 @@ import ( "bufio" "bytes" "crypto/tls" - "errors" "io" "net" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 封装的链接对象 @@ -208,9 +209,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -222,9 +221,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/g/net/gtcp/gtcp_conn_pkg.go b/g/net/gtcp/gtcp_conn_pkg.go index 0bc114a50..90b02ca97 100644 --- a/g/net/gtcp/gtcp_conn_pkg.go +++ b/g/net/gtcp/gtcp_conn_pkg.go @@ -8,9 +8,10 @@ package gtcp import ( "encoding/binary" - "errors" "fmt" "time" + + "github.com/gogf/gf/g/internal/errors" ) const ( @@ -73,9 +74,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ... return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.SendPkg(data, option...) return @@ -148,9 +147,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.RecvPkg(option...) return diff --git a/g/net/gtcp/gtcp_pool.go b/g/net/gtcp/gtcp_pool.go index ee65f0f61..ec706e344 100644 --- a/g/net/gtcp/gtcp_pool.go +++ b/g/net/gtcp/gtcp_pool.go @@ -7,9 +7,10 @@ package gtcp import ( - "errors" "time" + "github.com/gogf/gf/g/internal/errors" + "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gpool" ) @@ -121,9 +122,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -135,9 +134,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ... return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/g/net/gtcp/gtcp_pool_pkg.go b/g/net/gtcp/gtcp_pool_pkg.go index 6b71eea0d..03a182de6 100644 --- a/g/net/gtcp/gtcp_pool_pkg.go +++ b/g/net/gtcp/gtcp_pool_pkg.go @@ -7,8 +7,9 @@ package gtcp import ( - "errors" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 简单协议: (方法覆盖)发送数据 @@ -46,9 +47,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.RecvPkg(option...) return @@ -60,9 +59,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.SendPkg(data, option...) return diff --git a/g/net/gudp/gudp_conn.go b/g/net/gudp/gudp_conn.go index 1d59791f9..2e6c9fdf0 100644 --- a/g/net/gudp/gudp_conn.go +++ b/g/net/gudp/gudp_conn.go @@ -7,10 +7,11 @@ package gudp import ( - "errors" "io" "net" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 封装的UDP链接对象 @@ -181,9 +182,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -195,9 +194,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/geg/other/test.go b/geg/other/test.go index d856ff4b0..790580777 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,51 +1,5 @@ package main -import ( - "fmt" - - "github.com/gogf/gf/g/encoding/gjson" - "github.com/gogf/gf/g/util/gconv" -) - -//type TemplateMessage struct { -// Touser string `json:"touser,omitempty"` -// TemplateId string `json:"template_id,omitempty"` -// Miniprogram interface{} `json:"miniprograme,omitempty"` -// Data interface{} `json:"data,omitempty"` -//} - -type TemplateMessage struct { - Touser string `json:"touser,omitempty"` - TemplateId string `json:"template_id,omitempty"` - Miniprogram *gjson.Json `json:"miniprograme,omitempty"` - Data *gjson.Json `json:"data,omitempty"` -} - -// 封装模版消息 -func getTemplateMessage(message string) string { - templateId := "22222222" - miniprogram := `{"appid":"111111111","pagepath":"pages\/index?ald_media_id=20962&ald_link_key=bd660b4962a599f2"}` - data := `{"first":{"value":"送您一个随机红包,点击领取¥0.3-¥10","color":"#FF0000"},"keyword1":{"value":"¥0.3-¥10"},"keyword2":{"value":"2019年06月19日 11:45"},"keyword3":{"value":"微信零钱"},"keyword4":{"value":"点击此消息即可提现","color":"#FF0000"}}` - miniprogramJson := gjson.New(miniprogram) - dataJson := gjson.New(data) - //glog.Infof(miniprogramJson.ToJsonString()) - //glog.Info(dataJson.ToJsonString()) - - templateMessage := TemplateMessage{ - Touser: message, - TemplateId: templateId, - Miniprogram: miniprogramJson, - Data: dataJson, - } - - //glog.Debug(dataJson.ToJsonString()) - //json, _ := gjson.New(templateMessage).ToJsonString() - //return json - //glog.Info(templateMessage.Miniprogram.ToJsonString()) - return gconv.String(templateMessage) -} - func main() { - fmt.Println(getTemplateMessage("test")) } diff --git a/go.mod b/go.mod index fd3232e72..ef37cb8d6 100644 --- a/go.mod +++ b/go.mod @@ -1 +1 @@ -module github.com/gogf/gf \ No newline at end of file +module github.com/gogf/gf From 83669964f55dc501c4a62247522f63c4fc58324d Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 22:06:13 +0800 Subject: [PATCH 10/14] README updates --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index f34bcffe6..26310d175 100644 --- a/README.MD +++ b/README.MD @@ -4,7 +4,7 @@ [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories) [![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)](https://goreportcard.com/report/github.com/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) From b118cd8f27707182916de3d43f903627699e8aef Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 22:06:39 +0800 Subject: [PATCH 11/14] README updates --- README_ZH.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/README_ZH.MD b/README_ZH.MD index 7e44f2e81..59bb9de75 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -4,6 +4,7 @@ [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories) [![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) From 4b9db0c794bf0e99474e48040fa094db7b1d17d0 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Jun 2019 23:06:44 +0800 Subject: [PATCH 12/14] fix issue for gmd5/gcrc32 --- g/crypto/gcrc32/gcrc32_test.go | 3 +-- g/crypto/gmd5/gmd5.go | 1 + g/os/gcron/gcron_unit_1_test.go | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/g/crypto/gcrc32/gcrc32_test.go b/g/crypto/gcrc32/gcrc32_test.go index 753e1c034..6472a7a01 100644 --- a/g/crypto/gcrc32/gcrc32_test.go +++ b/g/crypto/gcrc32/gcrc32_test.go @@ -11,9 +11,8 @@ package gcrc32_test import ( "testing" - "github.com/gogf/gf/g/crypto/gmd5" - "github.com/gogf/gf/g/crypto/gcrc32" + "github.com/gogf/gf/g/crypto/gmd5" "github.com/gogf/gf/g/test/gtest" ) diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index 26888db0a..1a9a56ea3 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -13,6 +13,7 @@ import ( "io" "os" + "github.com/gogf/gf/g/internal/errors" "github.com/gogf/gf/g/util/gconv" ) diff --git a/g/os/gcron/gcron_unit_1_test.go b/g/os/gcron/gcron_unit_1_test.go index 59e3062ee..125fc2edc 100644 --- a/g/os/gcron/gcron_unit_1_test.go +++ b/g/os/gcron/gcron_unit_1_test.go @@ -7,11 +7,12 @@ package gcron_test import ( + "testing" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/os/gcron" "github.com/gogf/gf/g/test/gtest" - "testing" - "time" ) func TestCron_Add_Close(t *testing.T) { @@ -146,7 +147,7 @@ func TestCron_AddOnce2(t *testing.T) { array.Append(1) }) gtest.Assert(cron.Size(), 1) - time.Sleep(2500 * time.Millisecond) + time.Sleep(3000 * time.Millisecond) gtest.Assert(array.Len(), 1) gtest.Assert(cron.Size(), 0) }) From d2a053c1d7220b99c7c956d9f19716ff48d33bea Mon Sep 17 00:00:00 2001 From: guoyahao <515449117@qq.com> Date: Sun, 23 Jun 2019 17:53:10 +0800 Subject: [PATCH 13/14] no message --- g/util/gconv/gconv_z_all_test.go | 1368 ++++++++++++++++++++++++++++++ 1 file changed, 1368 insertions(+) create mode 100644 g/util/gconv/gconv_z_all_test.go diff --git a/g/util/gconv/gconv_z_all_test.go b/g/util/gconv/gconv_z_all_test.go new file mode 100644 index 000000000..c47eed1e0 --- /dev/null +++ b/g/util/gconv/gconv_z_all_test.go @@ -0,0 +1,1368 @@ +package gconv_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gconv" + "testing" + "time" +) + +type apiString interface { + String() string +} +type S struct { +} + +func (s S) String() string { + return "22222" +} + +type apiError interface { + Error() string +} +type S1 struct { +} + +func (s1 S1) Error() string { + return "22222" +} + +func Test_Bool_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Bool(i), false) + gtest.AssertEQ(gconv.Bool(false), false) + gtest.AssertEQ(gconv.Bool(nil), false) + gtest.AssertEQ(gconv.Bool(0), false) + gtest.AssertEQ(gconv.Bool("0"), false) + gtest.AssertEQ(gconv.Bool(""), false) + gtest.AssertEQ(gconv.Bool("false"), false) + gtest.AssertEQ(gconv.Bool("off"), false) + gtest.AssertEQ(gconv.Bool([]byte{}), false) + gtest.AssertEQ(gconv.Bool([]string{}), false) + gtest.AssertEQ(gconv.Bool([2]int{1, 2}), true) + gtest.AssertEQ(gconv.Bool([]interface{}{}), false) + gtest.AssertEQ(gconv.Bool([]map[int]int{}), false) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Bool(countryCapitalMap), true) + + gtest.AssertEQ(gconv.Bool("1"), true) + gtest.AssertEQ(gconv.Bool("on"), true) + gtest.AssertEQ(gconv.Bool(1), true) + gtest.AssertEQ(gconv.Bool(123.456), true) + gtest.AssertEQ(gconv.Bool(boolStruct{}), true) + gtest.AssertEQ(gconv.Bool(&boolStruct{}), true) + }) +} + +func Test_Int_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Int(i), 0) + gtest.AssertEQ(gconv.Int(false), 0) + gtest.AssertEQ(gconv.Int(nil), 0) + gtest.Assert(gconv.Int(nil), 0) + gtest.AssertEQ(gconv.Int(0), 0) + gtest.AssertEQ(gconv.Int("0"), 0) + gtest.AssertEQ(gconv.Int(""), 0) + gtest.AssertEQ(gconv.Int("false"), 0) + gtest.AssertEQ(gconv.Int("off"), 0) + gtest.AssertEQ(gconv.Int([]byte{}), 0) + gtest.AssertEQ(gconv.Int([]string{}), 0) + gtest.AssertEQ(gconv.Int([2]int{1, 2}), 0) + gtest.AssertEQ(gconv.Int([]interface{}{}), 0) + gtest.AssertEQ(gconv.Int([]map[int]int{}), 0) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int(countryCapitalMap), 0) + + gtest.AssertEQ(gconv.Int("1"), 1) + gtest.AssertEQ(gconv.Int("on"), 0) + gtest.AssertEQ(gconv.Int(1), 1) + gtest.AssertEQ(gconv.Int(123.456), 123) + gtest.AssertEQ(gconv.Int(boolStruct{}), 0) + gtest.AssertEQ(gconv.Int(&boolStruct{}), 0) + }) +} + +func Test_Int8_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int8(i), int8(0)) + gtest.AssertEQ(gconv.Int8(false), int8(0)) + gtest.AssertEQ(gconv.Int8(nil), int8(0)) + gtest.AssertEQ(gconv.Int8(0), int8(0)) + gtest.AssertEQ(gconv.Int8("0"), int8(0)) + gtest.AssertEQ(gconv.Int8(""), int8(0)) + gtest.AssertEQ(gconv.Int8("false"), int8(0)) + gtest.AssertEQ(gconv.Int8("off"), int8(0)) + gtest.AssertEQ(gconv.Int8([]byte{}), int8(0)) + gtest.AssertEQ(gconv.Int8([]string{}), int8(0)) + gtest.AssertEQ(gconv.Int8([2]int{1, 2}), int8(0)) + gtest.AssertEQ(gconv.Int8([]interface{}{}), int8(0)) + gtest.AssertEQ(gconv.Int8([]map[int]int{}), int8(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int8(countryCapitalMap), int8(0)) + + gtest.AssertEQ(gconv.Int8("1"), int8(1)) + gtest.AssertEQ(gconv.Int8("on"), int8(0)) + gtest.AssertEQ(gconv.Int8(int8(1)), int8(1)) + gtest.AssertEQ(gconv.Int8(123.456), int8(123)) + gtest.AssertEQ(gconv.Int8(boolStruct{}), int8(0)) + gtest.AssertEQ(gconv.Int8(&boolStruct{}), int8(0)) + }) +} + +func Test_Int16_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int16(i), int16(0)) + gtest.AssertEQ(gconv.Int16(false), int16(0)) + gtest.AssertEQ(gconv.Int16(nil), int16(0)) + gtest.AssertEQ(gconv.Int16(0), int16(0)) + gtest.AssertEQ(gconv.Int16("0"), int16(0)) + gtest.AssertEQ(gconv.Int16(""), int16(0)) + gtest.AssertEQ(gconv.Int16("false"), int16(0)) + gtest.AssertEQ(gconv.Int16("off"), int16(0)) + gtest.AssertEQ(gconv.Int16([]byte{}), int16(0)) + gtest.AssertEQ(gconv.Int16([]string{}), int16(0)) + gtest.AssertEQ(gconv.Int16([2]int{1, 2}), int16(0)) + gtest.AssertEQ(gconv.Int16([]interface{}{}), int16(0)) + gtest.AssertEQ(gconv.Int16([]map[int]int{}), int16(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int16(countryCapitalMap), int16(0)) + + gtest.AssertEQ(gconv.Int16("1"), int16(1)) + gtest.AssertEQ(gconv.Int16("on"), int16(0)) + gtest.AssertEQ(gconv.Int16(int16(1)), int16(1)) + gtest.AssertEQ(gconv.Int16(123.456), int16(123)) + gtest.AssertEQ(gconv.Int16(boolStruct{}), int16(0)) + gtest.AssertEQ(gconv.Int16(&boolStruct{}), int16(0)) + }) +} + +func Test_Int32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int32(i), int32(0)) + gtest.AssertEQ(gconv.Int32(false), int32(0)) + gtest.AssertEQ(gconv.Int32(nil), int32(0)) + gtest.AssertEQ(gconv.Int32(0), int32(0)) + gtest.AssertEQ(gconv.Int32("0"), int32(0)) + gtest.AssertEQ(gconv.Int32(""), int32(0)) + gtest.AssertEQ(gconv.Int32("false"), int32(0)) + gtest.AssertEQ(gconv.Int32("off"), int32(0)) + gtest.AssertEQ(gconv.Int32([]byte{}), int32(0)) + gtest.AssertEQ(gconv.Int32([]string{}), int32(0)) + gtest.AssertEQ(gconv.Int32([2]int{1, 2}), int32(0)) + gtest.AssertEQ(gconv.Int32([]interface{}{}), int32(0)) + gtest.AssertEQ(gconv.Int32([]map[int]int{}), int32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int32(countryCapitalMap), int32(0)) + + gtest.AssertEQ(gconv.Int32("1"), int32(1)) + gtest.AssertEQ(gconv.Int32("on"), int32(0)) + gtest.AssertEQ(gconv.Int32(int32(1)), int32(1)) + gtest.AssertEQ(gconv.Int32(123.456), int32(123)) + gtest.AssertEQ(gconv.Int32(boolStruct{}), int32(0)) + gtest.AssertEQ(gconv.Int32(&boolStruct{}), int32(0)) + }) +} + +func Test_Int64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Int64("0x00e"), int64(14)) + gtest.Assert(gconv.Int64("022"), int64(18)) + + gtest.Assert(gconv.Int64(i), int64(0)) + gtest.Assert(gconv.Int64(true), 1) + gtest.Assert(gconv.Int64("1"), int64(1)) + gtest.Assert(gconv.Int64("0"), int64(0)) + gtest.Assert(gconv.Int64("X"), int64(0)) + gtest.Assert(gconv.Int64("x"), int64(0)) + gtest.Assert(gconv.Int64(int64(1)), int64(1)) + gtest.Assert(gconv.Int64(int(0)), int64(0)) + gtest.Assert(gconv.Int64(int8(0)), int64(0)) + gtest.Assert(gconv.Int64(int16(0)), int64(0)) + gtest.Assert(gconv.Int64(int32(0)), int64(0)) + gtest.Assert(gconv.Int64(uint64(0)), int64(0)) + gtest.Assert(gconv.Int64(uint32(0)), int64(0)) + gtest.Assert(gconv.Int64(uint16(0)), int64(0)) + gtest.Assert(gconv.Int64(uint8(0)), int64(0)) + gtest.Assert(gconv.Int64(uint(0)), int64(0)) + gtest.Assert(gconv.Int64(float32(0)), int64(0)) + + gtest.AssertEQ(gconv.Int64(false), int64(0)) + gtest.AssertEQ(gconv.Int64(nil), int64(0)) + gtest.AssertEQ(gconv.Int64(0), int64(0)) + gtest.AssertEQ(gconv.Int64("0"), int64(0)) + gtest.AssertEQ(gconv.Int64(""), int64(0)) + gtest.AssertEQ(gconv.Int64("false"), int64(0)) + gtest.AssertEQ(gconv.Int64("off"), int64(0)) + gtest.AssertEQ(gconv.Int64([]byte{}), int64(0)) + gtest.AssertEQ(gconv.Int64([]string{}), int64(0)) + gtest.AssertEQ(gconv.Int64([2]int{1, 2}), int64(0)) + gtest.AssertEQ(gconv.Int64([]interface{}{}), int64(0)) + gtest.AssertEQ(gconv.Int64([]map[int]int{}), int64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int64(countryCapitalMap), int64(0)) + + gtest.AssertEQ(gconv.Int64("1"), int64(1)) + gtest.AssertEQ(gconv.Int64("on"), int64(0)) + gtest.AssertEQ(gconv.Int64(int64(1)), int64(1)) + gtest.AssertEQ(gconv.Int64(123.456), int64(123)) + gtest.AssertEQ(gconv.Int64(boolStruct{}), int64(0)) + gtest.AssertEQ(gconv.Int64(&boolStruct{}), int64(0)) + }) +} + +func Test_Uint_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Uint(i), uint(0)) + gtest.AssertEQ(gconv.Uint(false), uint(0)) + gtest.AssertEQ(gconv.Uint(nil), uint(0)) + gtest.Assert(gconv.Uint(nil), uint(0)) + gtest.AssertEQ(gconv.Uint(uint(0)), uint(0)) + gtest.AssertEQ(gconv.Uint("0"), uint(0)) + gtest.AssertEQ(gconv.Uint(""), uint(0)) + gtest.AssertEQ(gconv.Uint("false"), uint(0)) + gtest.AssertEQ(gconv.Uint("off"), uint(0)) + gtest.AssertEQ(gconv.Uint([]byte{}), uint(0)) + gtest.AssertEQ(gconv.Uint([]string{}), uint(0)) + gtest.AssertEQ(gconv.Uint([2]int{1, 2}), uint(0)) + gtest.AssertEQ(gconv.Uint([]interface{}{}), uint(0)) + gtest.AssertEQ(gconv.Uint([]map[int]int{}), uint(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint(countryCapitalMap), uint(0)) + + gtest.AssertEQ(gconv.Uint("1"), uint(1)) + gtest.AssertEQ(gconv.Uint("on"), uint(0)) + gtest.AssertEQ(gconv.Uint(1), uint(1)) + gtest.AssertEQ(gconv.Uint(123.456), uint(123)) + gtest.AssertEQ(gconv.Uint(boolStruct{}), uint(0)) + gtest.AssertEQ(gconv.Uint(&boolStruct{}), uint(0)) + }) +} + +func Test_Uint8_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint8(i), uint8(0)) + gtest.AssertEQ(gconv.Uint8(uint8(1)), uint8(1)) + gtest.AssertEQ(gconv.Uint8(false), uint8(0)) + gtest.AssertEQ(gconv.Uint8(nil), uint8(0)) + gtest.AssertEQ(gconv.Uint8(0), uint8(0)) + gtest.AssertEQ(gconv.Uint8("0"), uint8(0)) + gtest.AssertEQ(gconv.Uint8(""), uint8(0)) + gtest.AssertEQ(gconv.Uint8("false"), uint8(0)) + gtest.AssertEQ(gconv.Uint8("off"), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]byte{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]string{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([2]int{1, 2}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]interface{}{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]map[int]int{}), uint8(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint8(countryCapitalMap), uint8(0)) + + gtest.AssertEQ(gconv.Uint8("1"), uint8(1)) + gtest.AssertEQ(gconv.Uint8("on"), uint8(0)) + gtest.AssertEQ(gconv.Uint8(int8(1)), uint8(1)) + gtest.AssertEQ(gconv.Uint8(123.456), uint8(123)) + gtest.AssertEQ(gconv.Uint8(boolStruct{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8(&boolStruct{}), uint8(0)) + }) +} + +func Test_Uint16_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint16(i), uint16(0)) + gtest.AssertEQ(gconv.Uint16(uint16(1)), uint16(1)) + gtest.AssertEQ(gconv.Uint16(false), uint16(0)) + gtest.AssertEQ(gconv.Uint16(nil), uint16(0)) + gtest.AssertEQ(gconv.Uint16(0), uint16(0)) + gtest.AssertEQ(gconv.Uint16("0"), uint16(0)) + gtest.AssertEQ(gconv.Uint16(""), uint16(0)) + gtest.AssertEQ(gconv.Uint16("false"), uint16(0)) + gtest.AssertEQ(gconv.Uint16("off"), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]byte{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]string{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([2]int{1, 2}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]interface{}{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]map[int]int{}), uint16(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint16(countryCapitalMap), uint16(0)) + + gtest.AssertEQ(gconv.Uint16("1"), uint16(1)) + gtest.AssertEQ(gconv.Uint16("on"), uint16(0)) + gtest.AssertEQ(gconv.Uint16(int16(1)), uint16(1)) + gtest.AssertEQ(gconv.Uint16(123.456), uint16(123)) + gtest.AssertEQ(gconv.Uint16(boolStruct{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16(&boolStruct{}), uint16(0)) + }) +} + +func Test_Uint32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint32(i), uint32(0)) + gtest.AssertEQ(gconv.Uint32(uint32(1)), uint32(1)) + gtest.AssertEQ(gconv.Uint32(false), uint32(0)) + gtest.AssertEQ(gconv.Uint32(nil), uint32(0)) + gtest.AssertEQ(gconv.Uint32(0), uint32(0)) + gtest.AssertEQ(gconv.Uint32("0"), uint32(0)) + gtest.AssertEQ(gconv.Uint32(""), uint32(0)) + gtest.AssertEQ(gconv.Uint32("false"), uint32(0)) + gtest.AssertEQ(gconv.Uint32("off"), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]byte{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]string{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([2]int{1, 2}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]interface{}{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]map[int]int{}), uint32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint32(countryCapitalMap), uint32(0)) + + gtest.AssertEQ(gconv.Uint32("1"), uint32(1)) + gtest.AssertEQ(gconv.Uint32("on"), uint32(0)) + gtest.AssertEQ(gconv.Uint32(int32(1)), uint32(1)) + gtest.AssertEQ(gconv.Uint32(123.456), uint32(123)) + gtest.AssertEQ(gconv.Uint32(boolStruct{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32(&boolStruct{}), uint32(0)) + }) +} + +func Test_Uint64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Uint64("0x00e"), uint64(14)) + gtest.Assert(gconv.Uint64("022"), uint64(18)) + + gtest.AssertEQ(gconv.Uint64(i), uint64(0)) + gtest.AssertEQ(gconv.Uint64(true), uint64(1)) + gtest.Assert(gconv.Uint64("1"), int64(1)) + gtest.Assert(gconv.Uint64("0"), uint64(0)) + gtest.Assert(gconv.Uint64("X"), uint64(0)) + gtest.Assert(gconv.Uint64("x"), uint64(0)) + gtest.Assert(gconv.Uint64(int64(1)), uint64(1)) + gtest.Assert(gconv.Uint64(int(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int8(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int16(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int32(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint64(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint32(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint16(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint8(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint(0)), uint64(0)) + gtest.Assert(gconv.Uint64(float32(0)), uint64(0)) + + gtest.AssertEQ(gconv.Uint64(false), uint64(0)) + gtest.AssertEQ(gconv.Uint64(nil), uint64(0)) + gtest.AssertEQ(gconv.Uint64(0), uint64(0)) + gtest.AssertEQ(gconv.Uint64("0"), uint64(0)) + gtest.AssertEQ(gconv.Uint64(""), uint64(0)) + gtest.AssertEQ(gconv.Uint64("false"), uint64(0)) + gtest.AssertEQ(gconv.Uint64("off"), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]byte{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]string{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([2]int{1, 2}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]interface{}{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]map[int]int{}), uint64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint64(countryCapitalMap), uint64(0)) + + gtest.AssertEQ(gconv.Uint64("1"), uint64(1)) + gtest.AssertEQ(gconv.Uint64("on"), uint64(0)) + gtest.AssertEQ(gconv.Uint64(int64(1)), uint64(1)) + gtest.AssertEQ(gconv.Uint64(123.456), uint64(123)) + gtest.AssertEQ(gconv.Uint64(boolStruct{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64(&boolStruct{}), uint64(0)) + }) +} + +func Test_Float32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Float32(i), float32(0)) + gtest.AssertEQ(gconv.Float32(false), float32(0)) + gtest.AssertEQ(gconv.Float32(nil), float32(0)) + gtest.AssertEQ(gconv.Float32(0), float32(0)) + gtest.AssertEQ(gconv.Float32("0"), float32(0)) + gtest.AssertEQ(gconv.Float32(""), float32(0)) + gtest.AssertEQ(gconv.Float32("false"), float32(0)) + gtest.AssertEQ(gconv.Float32("off"), float32(0)) + gtest.AssertEQ(gconv.Float32([]byte{}), float32(0)) + gtest.AssertEQ(gconv.Float32([]string{}), float32(0)) + gtest.AssertEQ(gconv.Float32([2]int{1, 2}), float32(0)) + gtest.AssertEQ(gconv.Float32([]interface{}{}), float32(0)) + gtest.AssertEQ(gconv.Float32([]map[int]int{}), float32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Float32(countryCapitalMap), float32(0)) + + gtest.AssertEQ(gconv.Float32("1"), float32(1)) + gtest.AssertEQ(gconv.Float32("on"), float32(0)) + gtest.AssertEQ(gconv.Float32(float32(1)), float32(1)) + gtest.AssertEQ(gconv.Float32(123.456), float32(123.456)) + gtest.AssertEQ(gconv.Float32(boolStruct{}), float32(0)) + gtest.AssertEQ(gconv.Float32(&boolStruct{}), float32(0)) + }) +} + +func Test_Float64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Float64(i), float64(0)) + gtest.AssertEQ(gconv.Float64(false), float64(0)) + gtest.AssertEQ(gconv.Float64(nil), float64(0)) + gtest.AssertEQ(gconv.Float64(0), float64(0)) + gtest.AssertEQ(gconv.Float64("0"), float64(0)) + gtest.AssertEQ(gconv.Float64(""), float64(0)) + gtest.AssertEQ(gconv.Float64("false"), float64(0)) + gtest.AssertEQ(gconv.Float64("off"), float64(0)) + gtest.AssertEQ(gconv.Float64([]byte{}), float64(0)) + gtest.AssertEQ(gconv.Float64([]string{}), float64(0)) + gtest.AssertEQ(gconv.Float64([2]int{1, 2}), float64(0)) + gtest.AssertEQ(gconv.Float64([]interface{}{}), float64(0)) + gtest.AssertEQ(gconv.Float64([]map[int]int{}), float64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Float64(countryCapitalMap), float64(0)) + + gtest.AssertEQ(gconv.Float64("1"), float64(1)) + gtest.AssertEQ(gconv.Float64("on"), float64(0)) + gtest.AssertEQ(gconv.Float64(float64(1)), float64(1)) + gtest.AssertEQ(gconv.Float64(123.456), float64(123.456)) + gtest.AssertEQ(gconv.Float64(boolStruct{}), float64(0)) + gtest.AssertEQ(gconv.Float64(&boolStruct{}), float64(0)) + }) +} + +func Test_String_All(t *testing.T) { + gtest.Case(t, func() { + var s []rune + gtest.AssertEQ(gconv.String(s), "") + var i interface{} = nil + gtest.AssertEQ(gconv.String(i), "") + gtest.AssertEQ(gconv.String("1"), "1") + gtest.AssertEQ(gconv.String("0"), string("0")) + gtest.Assert(gconv.String("X"), string("X")) + gtest.Assert(gconv.String("x"), string("x")) + gtest.Assert(gconv.String(int64(1)), uint64(1)) + gtest.Assert(gconv.String(int(0)), string("0")) + gtest.Assert(gconv.String(int8(0)), string("0")) + gtest.Assert(gconv.String(int16(0)), string("0")) + gtest.Assert(gconv.String(int32(0)), string("0")) + gtest.Assert(gconv.String(uint64(0)), string("0")) + gtest.Assert(gconv.String(uint32(0)), string("0")) + gtest.Assert(gconv.String(uint16(0)), string("0")) + gtest.Assert(gconv.String(uint8(0)), string("0")) + gtest.Assert(gconv.String(uint(0)), string("0")) + gtest.Assert(gconv.String(float32(0)), string("0")) + gtest.AssertEQ(gconv.String(true), "true") + gtest.AssertEQ(gconv.String(false), "false") + gtest.AssertEQ(gconv.String(nil), "") + gtest.AssertEQ(gconv.String(0), string("0")) + gtest.AssertEQ(gconv.String("0"), string("0")) + gtest.AssertEQ(gconv.String(""), "") + gtest.AssertEQ(gconv.String("false"), "false") + gtest.AssertEQ(gconv.String("off"), string("off")) + gtest.AssertEQ(gconv.String([]byte{}), "") + gtest.AssertEQ(gconv.String([]string{}), "[]") + gtest.AssertEQ(gconv.String([2]int{1, 2}), "[1,2]") + gtest.AssertEQ(gconv.String([]interface{}{}), "[]") + gtest.AssertEQ(gconv.String(map[int]int{}), "{}") + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.String(countryCapitalMap), `{"France":"巴黎","India ":"新德里","Italy":"罗马","Japan":"东京"}`) + gtest.AssertEQ(gconv.String(int64(1)), "1") + gtest.AssertEQ(gconv.String(123.456), "123.456") + gtest.AssertEQ(gconv.String(boolStruct{}), "{}") + gtest.AssertEQ(gconv.String(&boolStruct{}), "{}") + + var info apiString + info = new(S) + gtest.AssertEQ(gconv.String(info), "22222") + var errinfo apiError + errinfo = new(S1) + gtest.AssertEQ(gconv.String(errinfo), "22222") + }) +} + +func Test_Runes_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Runes("www"), []int32{119, 119, 119}) + var s []rune + gtest.AssertEQ(gconv.Runes(s), nil) + }) +} + +func Test_Rune_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Rune("www"), int32(0)) + gtest.AssertEQ(gconv.Rune(int32(0)), int32(0)) + var s []rune + gtest.AssertEQ(gconv.Rune(s), int32(0)) + }) +} + +func Test_Bytes_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Bytes(nil), nil) + gtest.AssertEQ(gconv.Bytes(int32(0)), []uint8{0, 0, 0, 0}) + gtest.AssertEQ(gconv.Bytes("s"), []uint8{115}) + gtest.AssertEQ(gconv.Bytes([]byte("s")), []uint8{115}) + }) +} + +func Test_Byte_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Byte(uint8(0)), uint8(0)) + gtest.AssertEQ(gconv.Byte("s"), uint8(0)) + gtest.AssertEQ(gconv.Byte([]byte("s")), uint8(0)) + }) +} + +func Test_Convert_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Convert(i, "string"), "") + gtest.AssertEQ(gconv.Convert("1", "string"), "1") + gtest.Assert(gconv.Convert(int64(1), "int64"), int64(1)) + gtest.Assert(gconv.Convert(int(0), "int"), int(0)) + gtest.Assert(gconv.Convert(int8(0), "int8"), int8(0)) + gtest.Assert(gconv.Convert(int16(0), "int16"), int16(0)) + gtest.Assert(gconv.Convert(int32(0), "int32"), int32(0)) + gtest.Assert(gconv.Convert(uint64(0), "uint64"), uint64(0)) + gtest.Assert(gconv.Convert(uint32(0), "uint32"), uint32(0)) + gtest.Assert(gconv.Convert(uint16(0), "uint16"), uint16(0)) + gtest.Assert(gconv.Convert(uint8(0), "uint8"), uint8(0)) + gtest.Assert(gconv.Convert(uint(0), "uint"), uint(0)) + gtest.Assert(gconv.Convert(float32(0), "float32"), float32(0)) + gtest.Assert(gconv.Convert(float64(0), "float64"), float64(0)) + gtest.AssertEQ(gconv.Convert(true, "bool"), true) + gtest.AssertEQ(gconv.Convert([]byte{}, "[]byte"), []uint8{}) + gtest.AssertEQ(gconv.Convert([]string{}, "[]string"), []string{}) + gtest.AssertEQ(gconv.Convert([2]int{1, 2}, "[]int"), []int{0}) + gtest.AssertEQ(gconv.Convert("1989-01-02", "Time", "Y-m-d"), gconv.Time("1989-01-02", "Y-m-d")) + gtest.AssertEQ(gconv.Convert(1989, "Time"), gconv.Time("2033-01-11 04:00:00 +0800 CST")) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "gtime.Time", 1), nil) + gtest.AssertEQ(gconv.Convert(1989, "gtime.Time"), gtime.Time{gconv.Time("2033-01-11 04:00:00 +0800 CST")}) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "*gtime.Time", 1), nil) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "GTime", 1), nil) + gtest.AssertEQ(gconv.Convert(1989, "*gtime.Time"), gconv.GTime(1989)) + gtest.AssertEQ(gconv.Convert(1989, "Duration"), time.Duration(int64(1989))) + gtest.AssertEQ(gconv.Convert("1989", "Duration"), time.Duration(int64(1989))) + gtest.AssertEQ(gconv.Convert("1989", ""), "1989") + }) +} + +func Test_Time_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Duration(""), time.Duration(int64(0))) + gtest.AssertEQ(gconv.GTime(""), gtime.New()) + }) +} + +func Test_Slice_All(t *testing.T) { + gtest.Case(t, func() { + value := 123.456 + gtest.AssertEQ(gconv.Ints(value), []int{123}) + gtest.AssertEQ(gconv.Ints(nil), nil) + gtest.AssertEQ(gconv.Ints([]string{"1", "2"}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int{}), []int{}) + gtest.AssertEQ(gconv.Ints([]int8{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int16{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int64{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint{1}), []int{1}) + gtest.AssertEQ(gconv.Ints([]uint8{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint16{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint64{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]bool{true}), []int{1}) + gtest.AssertEQ(gconv.Ints([]float32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]float64{1, 2}), []int{1, 2}) + var inter []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Ints(inter), []int{0, 0}) + + gtest.AssertEQ(gconv.Strings(value), []string{"123.456"}) + gtest.AssertEQ(gconv.Strings(nil), nil) + gtest.AssertEQ(gconv.Strings([]string{"1", "2"}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int{1}), []string{"1"}) + gtest.AssertEQ(gconv.Strings([]int8{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int16{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int64{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint{1}), []string{"1"}) + gtest.AssertEQ(gconv.Strings([]uint8{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint16{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint64{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]bool{true}), []string{"true"}) + gtest.AssertEQ(gconv.Strings([]float32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]float64{1, 2}), []string{"1", "2"}) + var strer []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Strings(strer), []string{" "}) + + gtest.AssertEQ(gconv.Floats(value), []float64{123.456}) + gtest.AssertEQ(gconv.Floats(nil), nil) + gtest.AssertEQ(gconv.Floats([]string{"1", "2"}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int{1}), []float64{1}) + gtest.AssertEQ(gconv.Floats([]int8{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int16{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int64{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint{1}), []float64{1}) + gtest.AssertEQ(gconv.Floats([]uint8{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint16{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint64{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]bool{true}), []float64{0}) + gtest.AssertEQ(gconv.Floats([]float32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]float64{1, 2}), []float64{1, 2}) + var floer []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Floats(floer), []float64{0, 0}) + + gtest.AssertEQ(gconv.Interfaces(value), []interface{}{123.456}) + gtest.AssertEQ(gconv.Interfaces(nil), nil) + gtest.AssertEQ(gconv.Interfaces([]interface{}{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]string{"1"}), []interface{}{"1"}) + gtest.AssertEQ(gconv.Interfaces([]int{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int8{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int16{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint8{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint16{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]bool{true}), []interface{}{true}) + gtest.AssertEQ(gconv.Interfaces([]float32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]float64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([1]int{1}), []interface{}{1}) + + type interSlice []int + slices := interSlice{1} + gtest.AssertEQ(gconv.Interfaces(slices), []interface{}{1}) + + gtest.AssertEQ(gconv.Maps(nil), nil) + gtest.AssertEQ(gconv.Maps([]map[string]interface{}{{"a": "1"},}), []map[string]interface{}{{"a": "1"},}) + gtest.AssertEQ(gconv.Maps(1223), []map[string]interface{}{{}}) + gtest.AssertEQ(gconv.Maps([]int{}), nil) + }) +} + +// 私有属性不会进行转换 +func Test_Slice_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + Ad []interface{} + } + gtest.Case(t, func() { + user := &User{1, "john", []interface{}{2}} + gtest.Assert(gconv.Interfaces(user), g.Slice{1, []interface{}{2}}) + }) +} + +func Test_Map_Basic_All(t *testing.T) { + gtest.Case(t, func() { + m1 := map[string]string{ + "k": "v", + } + m2 := map[int]string{ + 3: "v", + } + m3 := map[float64]float32{ + 1.22: 3.1, + } + gtest.Assert(gconv.Map(m1), g.Map{ + "k": "v", + }) + gtest.Assert(gconv.Map(m2), g.Map{ + "3": "v", + }) + gtest.Assert(gconv.Map(m3), g.Map{ + "1.22": "3.1", + }) + gtest.AssertEQ(gconv.Map(nil), nil) + gtest.AssertEQ(gconv.Map(map[string]interface{}{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[int]interface{}{1: 1}), map[string]interface{}{"1": 1}) + gtest.AssertEQ(gconv.Map(map[uint]interface{}{1: 1}), map[string]interface{}{"1": 1}) + gtest.AssertEQ(gconv.Map(map[uint]string{1: "1"}), map[string]interface{}{"1": "1"}) + + gtest.AssertEQ(gconv.Map(map[interface{}]interface{}{"a": 1}), map[interface{}]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]string{"a": "1"}), map[interface{}]string{"a": "1"}) + gtest.AssertEQ(gconv.Map(map[interface{}]int{"a": 1}), map[interface{}]int{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]uint{"a": 1}), map[interface{}]uint{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]float32{"a": 1}), map[interface{}]float32{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]float64{"a": 1}), map[interface{}]float64{"a": 1}) + + gtest.AssertEQ(gconv.Map(map[string]bool{"a": true}), map[string]interface{}{"a": true}) + gtest.AssertEQ(gconv.Map(map[string]int{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]uint{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]float32{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]float64{"a": 1}), map[string]interface{}{"a": 1}) + + }) +} + +func Test_Map_StructWithGconvTag_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string `gconv:"-"` + NickName string `gconv:"nickname,omitempty"` + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + Ss []string `gconv:"ss"` + } + user1 := User{ + Uid: 100, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + } + user2 := &user1 + map1 := gconv.Map(user1) + map2 := gconv.Map(user2) + gtest.Assert(map1["Uid"], 100) + gtest.Assert(map1["Name"], "john") + gtest.Assert(map1["SiteUrl"], nil) + gtest.Assert(map1["NickName"], nil) + gtest.Assert(map1["nickname"], nil) + gtest.Assert(map1["password1"], "123") + gtest.Assert(map1["password2"], "456") + gtest.Assert(map2["Uid"], 100) + gtest.Assert(map2["Name"], "john") + gtest.Assert(map2["SiteUrl"], nil) + gtest.Assert(map2["NickName"], nil) + gtest.Assert(map2["nickname"], nil) + gtest.Assert(map2["password1"], "123") + gtest.Assert(map2["password2"], "456") + }) +} + +func Test_Map_StructWithJsonTag_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string `json:"-"` + NickName string `json:"nickname, omitempty"` + Pass1 string `json:"password1,newpassword"` + Pass2 string `json:"password2"` + Ss []string `json:"omitempty"` + ssb, ssa string + } + user1 := User{ + Uid: 100, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + ssb: "11", + ssa: "222", + } + user3 := User{ + Uid: 100, + Name: "john", + NickName: "SSS", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + ssb: "11", + ssa: "222", + } + user2 := &user1 + _ = gconv.Map(user1, "Ss") + map1 := gconv.Map(user1, "json", "json2") + map2 := gconv.Map(user2) + map3 := gconv.Map(user3) + gtest.Assert(map1["Uid"], 100) + gtest.Assert(map1["Name"], "john") + gtest.Assert(map1["SiteUrl"], nil) + gtest.Assert(map1["NickName"], nil) + gtest.Assert(map1["nickname"], nil) + gtest.Assert(map1["password1"], "123") + gtest.Assert(map1["password2"], "456") + gtest.Assert(map2["Uid"], 100) + gtest.Assert(map2["Name"], "john") + gtest.Assert(map2["SiteUrl"], nil) + gtest.Assert(map2["NickName"], nil) + gtest.Assert(map2["nickname"], nil) + gtest.Assert(map2["password1"], "123") + gtest.Assert(map2["password2"], "456") + gtest.Assert(map3["NickName"], nil) + }) +} + +func Test_Map_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + } + gtest.Case(t, func() { + user := &User{1, "john"} + gtest.Assert(gconv.Map(user), g.Map{"Id": 1}) + }) +} + +func Test_Map_StructInherit_All(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + S *string `json:"nickname2"` + } + + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + var s = "s" + user.S = &s + + m := gconv.MapDeep(user) + gtest.Assert(m["id"], user.Id) + gtest.Assert(m["nickname"], user.Nickname) + gtest.Assert(m["create_time"], user.CreateTime) + gtest.Assert(m["nickname2"], user.S) + }) +} + +func Test_Struct_Basic1_All(t *testing.T) { + gtest.Case(t, func() { + + type Score struct { + Name int + Result string + } + + type Score2 struct { + Name int + Result string + } + + type User struct { + Uid int + Name string + Site_Url string + NickName string + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + As *Score + Ass Score + Assb []interface{} + } + // 使用默认映射规则绑定属性值到对象 + user := new(User) + params1 := g.Map{ + "uid": 1, + "Name": "john", + "siteurl": "https://goframe.org", + "nick_name": "johng", + "PASS1": "123", + "PASS2": "456", + "As": g.Map{"Name": 1, "Result": "22222"}, + "Ass": &Score{11,"11"}, + "Assb": []string{"wwww"}, + } + _ = gconv.Struct(nil, user) + _ = gconv.Struct(params1, nil) + _ = gconv.Struct([]interface{}{nil}, user) + _ = gconv.Struct(user, []interface{}{nil}) + + var a = []interface{}{nil} + ab := &a + _ = gconv.Struct(params1, *ab) + var pi *int = nil + _ = gconv.Struct(params1, pi) + + _ = gconv.Struct(params1, user) + _ = gconv.Struct(params1, user, map[string]string{"uid": "Names"}) + _ = gconv.Struct(params1, user, map[string]string{"uid": "as"}) + + // 使用struct tag映射绑定属性值到对象 + user = new(User) + params2 := g.Map{ + "uid": 2, + "name": "smith", + "site-url": "https://goframe.org", + "nick name": "johng", + "password1": "111", + "password2": "222", + } + if err := gconv.Struct(params2, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid: 2, + Name: "smith", + Site_Url: "https://goframe.org", + NickName: "johng", + Pass1: "111", + Pass2: "222", + }) + }) +} + +// 使用默认映射规则绑定属性值到对象 +func Test_Struct_Basic2_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string + Pass1 string + Pass2 string + } + user := new(User) + params := g.Map{ + "uid": 1, + "Name": "john", + "site_url": "https://goframe.org", + "PASS1": "123", + "PASS2": "456", + } + if err := gconv.Struct(params, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid: 1, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + }) + }) +} + +// 带有指针的基础类型属性 +func Test_Struct_Basic3_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name *string + } + user := new(User) + params := g.Map{ + "uid": 1, + "Name": "john", + } + if err := gconv.Struct(params, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user.Uid, 1) + gtest.Assert(*user.Name, "john") + }) +} + +// slice类型属性的赋值 +func Test_Struct_Attr_Slice_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Scores []int + } + scores := []interface{}{99, 100, 60, 140} + user := new(User) + if err := gconv.Struct(g.Map{"Scores": scores}, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user, &User{ + Scores: []int{99, 100, 60, 140}, + }) + } + }) +} + +// 属性为struct对象 +func Test_Struct_Attr_Struct_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user, &User{ + Scores: Score{ + Name: "john", + Result: 100, + }, + }) + } + }) +} + +// 属性为struct对象指针 +func Test_Struct_Attr_Struct_Ptr_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores *Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, &Score{ + Name: "john", + Result: 100, + }) + } + }) +} + +// 属性为struct对象slice +func Test_Struct_Attr_Struct_Slice1_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, []Score{ + { + Name: "john", + Result: 100, + }, + }) + } + }) +} + +// 属性为struct对象slice +func Test_Struct_Attr_Struct_Slice2_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": []interface{}{ + map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + map[string]interface{}{ + "Name": "smith", + "Result": 60, + }, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为slice map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, []Score{ + { + Name: "john", + Result: 100, + }, + { + Name: "smith", + Result: 60, + }, + }) + } + }) +} + +// 属性为struct对象slice ptr +func Test_Struct_Attr_Struct_Slice_Ptr_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []*Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": []interface{}{ + map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + map[string]interface{}{ + "Name": "smith", + "Result": 60, + }, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为slice map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(len(user.Scores), 2) + gtest.Assert(user.Scores[0], &Score{ + Name: "john", + Result: 100, + }) + gtest.Assert(user.Scores[1], &Score{ + Name: "smith", + Result: 60, + }) + } + }) +} + +func Test_Struct_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + } + gtest.Case(t, func() { + user := new(User) + err := gconv.Struct(g.Map{"id": 1, "name": "john"}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Id, 1) + gtest.Assert(user.name, "") + }) +} + +func Test_Struct_Deep_All(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id": 100, + "uid": 101, + "passport": "t1", + "password": "123456", + "nickname": "T1", + "create_time": "2019", + } + user := new(User) + gconv.StructDeep(data, user) + gtest.Assert(user.Id, 100) + gtest.Assert(user.Uid, 101) + gtest.Assert(user.Nickname, "T1") + gtest.Assert(user.CreateTime, "2019") + }) +} + +func Test_Struct_Time_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + CreateTime time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) +} From 05c7ba5f298e23dbcb739b4df91cbabf596ca9ce Mon Sep 17 00:00:00 2001 From: John Date: Mon, 24 Jun 2019 19:05:07 +0800 Subject: [PATCH 14/14] improve ghttp.CORSDefault --- .gitignore | 1 - g/net/ghttp/ghttp_response_cors.go | 3 +-- geg/net/ghttp/server/log/log.go | 25 +++++++++++++++++++++++++ geg/net/ghttp/server/log/log_error.go | 17 +++++++++++++++++ geg/other/test.go | 21 ++++++++++++++++++++- 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 geg/net/ghttp/server/log/log.go create mode 100644 geg/net/ghttp/server/log/log_error.go diff --git a/.gitignore b/.gitignore index 93456099d..8470fac8f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ .settings/ .vscode/ vender/ -log/ composer.lock gitpush.sh pkg/ diff --git a/g/net/ghttp/ghttp_response_cors.go b/g/net/ghttp/ghttp_response_cors.go index 708cfed8d..d5dcc9d36 100644 --- a/g/net/ghttp/ghttp_response_cors.go +++ b/g/net/ghttp/ghttp_response_cors.go @@ -8,7 +8,6 @@ package ghttp import ( - "github.com/gogf/gf/g/text/gstr" "github.com/gogf/gf/g/util/gconv" ) @@ -26,7 +25,7 @@ type CORSOptions struct { // 默认的CORS配置 func (r *Response) DefaultCORSOptions() CORSOptions { return CORSOptions{ - AllowOrigin: gstr.TrimRight(r.request.Referer(), "/"), + AllowOrigin: "*", AllowMethods: HTTP_METHODS, AllowCredentials: "true", MaxAge: 3628800, diff --git a/geg/net/ghttp/server/log/log.go b/geg/net/ghttp/server/log/log.go new file mode 100644 index 000000000..55ec3fb85 --- /dev/null +++ b/geg/net/ghttp/server/log/log.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/gogf/gf/g/net/ghttp" + "net/http" +) + +func main() { + s := ghttp.GetServer() + s.BindHandler("/log/handler", func(r *ghttp.Request) { + r.Response.WriteStatus(http.StatusNotFound, "文件找不到了") + }) + s.SetAccessLogEnabled(true) + s.SetErrorLogEnabled(true) + //s.SetLogHandler(func(r *ghttp.Request, error ...interface{}) { + // if len(error) > 0 { + // // 如果是错误日志 + // fmt.Println("错误产生了:", error[0]) + // } + // // 这里是请求日志 + // fmt.Println("请求处理完成,请求地址:", r.URL.String(), "请求结果:", r.Response.Status) + //}) + s.SetPort(8199) + s.Run() +} diff --git a/geg/net/ghttp/server/log/log_error.go b/geg/net/ghttp/server/log/log_error.go new file mode 100644 index 000000000..02cc6cd11 --- /dev/null +++ b/geg/net/ghttp/server/log/log_error.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/gogf/gf/g/net/ghttp" +) + +func main() { + s := ghttp.GetServer() + s.BindHandler("/log/error", func(r *ghttp.Request) { + if j := r.GetJson(); j != nil { + r.Response.Write(j.Get("test")) + } + }) + s.SetErrorLogEnabled(true) + s.SetPort(8199) + s.Run() +} diff --git a/geg/other/test.go b/geg/other/test.go index 790580777..1b134b6c5 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,5 +1,24 @@ package main -func main() { +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" +) +type Order struct{} + +func (order *Order) Get(r *ghttp.Request) { + r.Response.Write("GET") +} + +func main() { + s := g.Server() + s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc{ + "BeforeServe": func(r *ghttp.Request) { + r.Response.CORSDefault() + }, + }) + s.BindObjectRest("/api.v1/{.struct}", new(Order)) + s.SetPort(8199) + s.Run() }