diff --git a/.example/os/gcache/getorset_func_lock.go b/.example/os/gcache/getorset_func_lock.go new file mode 100644 index 000000000..b5e3760bc --- /dev/null +++ b/.example/os/gcache/getorset_func_lock.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" + "time" +) + +func main() { + var ( + ch = make(chan struct{}, 0) + ctx = gctx.New() + key = `key` + value = `value` + ) + for i := 0; i < 10; i++ { + go func(index int) { + <-ch + _, _ = gcache.Ctx(ctx).GetOrSetFuncLock(key, func() (interface{}, error) { + fmt.Println(index, "entered") + return value, nil + }, 0) + }(i) + } + close(ch) + time.Sleep(time.Second) +} diff --git a/.example/os/gcache/note_interface_key.go b/.example/os/gcache/note_interface_key.go new file mode 100644 index 000000000..4f5e1ee44 --- /dev/null +++ b/.example/os/gcache/note_interface_key.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" +) + +func main() { + var ( + ctx = gctx.New() + key1 int32 = 1 + key2 float64 = 1 + value = `value` + ) + _ = gcache.Ctx(ctx).Set(key1, value, 0) + fmt.Println(gcache.Ctx(ctx).Get(key1)) + fmt.Println(gcache.Ctx(ctx).Get(key2)) +} diff --git a/.example/os/gcache/note_interface_value.go b/.example/os/gcache/note_interface_value.go new file mode 100644 index 000000000..b102e86f0 --- /dev/null +++ b/.example/os/gcache/note_interface_value.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" +) + +func main() { + type User struct { + Id int + Name string + Site string + } + var ( + ctx = gctx.New() + user *User + key = `UserKey` + value = &User{ + Id: 1, + Name: "GoFrame", + Site: "https://goframe.org", + } + ) + _ = gcache.Ctx(ctx).Set(key, value, 0) + v, _ := gcache.Ctx(ctx).GetVar(key) + _ = v.Scan(&user) + fmt.Printf(`%#v`, user) +} diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 251c37aed..7caeefdbc 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -11,14 +11,13 @@ import ( "database/sql" "fmt" "github.com/gogf/gf/errors/gcode" - "net/url" - "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" + "net/url" - _ "github.com/gogf/mysql" + _ "github.com/go-sql-driver/mysql" ) // DriverMysql is the driver for mysql database. @@ -34,7 +33,7 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) { }, nil } -// Open creates and returns a underlying sql.DB object for mysql. +// Open creates and returns an underlying sql.DB object for mysql. // Note that it converts time.Time argument to local timezone in default. func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { var source string diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 37ae7a8c3..87c6e8763 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -209,7 +209,12 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { data[k] = nil } - case *time.Time, *gtime.Time: + case *gtime.Time: + if r.IsZero() { + data[k] = nil + } + + case *time.Time: continue case Counter, *Counter: diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 5c40a7eba..78099b4e6 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -83,8 +83,10 @@ func (m *Model) Data(data ...interface{}) *Model { list[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) } model.data = list + case reflect.Map: model.data = ConvertDataForTableRecord(data[0]) + case reflect.Struct: if v, ok := data[0].(apiInterfaces); ok { var ( @@ -98,6 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model { } else { model.data = ConvertDataForTableRecord(data[0]) } + default: model.data = data[0] } diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 542f302a9..36782fffc 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -18,7 +18,7 @@ import ( "github.com/gogf/gf/text/gstr" ) -// With creates and returns an ORM model based on meta data of given object. +// With creates and returns an ORM model based on metadata of given object. // It also enables model association operations feature on given `object`. // It can be called multiple times to add one or more objects to model and enable // their mode association operations feature. @@ -64,7 +64,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{ Pointer: pointer, PriorityTagArray: nil, RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, @@ -74,7 +74,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } // It checks the with array and automatically calls the ScanList to complete association querying. if !m.withAll { - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { for _, withItem := range m.withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { @@ -91,7 +91,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } } } - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { var ( fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") parsedTagOutput = m.parseWithTagInFieldStruct(field) @@ -99,43 +99,40 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if parsedTagOutput.With == "" { continue } - // Just handler "with" type attribute struct. + // It just handlers "with" type attribute struct, so it ignores other struct types. if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { continue } array := gstr.SplitAndTrim(parsedTagOutput.With, "=") if len(array) == 1 { - // It supports using only one column name + // It also supports using only one column name // if both tables associates using the same column name. array = append(array, parsedTagOutput.With) } var ( - model *Model - fieldKeys []string - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} + model *Model + fieldKeys []string + relatedSourceName = array[0] + relatedTargetName = array[1] + relatedTargetValue interface{} ) // Find the value of related attribute from `pointer`. - for attributeName, attributeValue := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = attributeValue.Value.Interface() + for attributeName, attributeValue := range currentStructFieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) { + relatedTargetValue = attributeValue.Value.Interface() break } } - if relatedFieldValue == nil { + if relatedTargetValue == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, - `cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`, - relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), + `cannot find the target related value of name "%s" in with tag "%s" for attribute "%s.%s"`, + relatedTargetName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), ) } bindToReflectValue := field.Value - switch bindToReflectValue.Kind() { - case reflect.Array, reflect.Slice: - if bindToReflectValue.CanAddr() { - bindToReflectValue = bindToReflectValue.Addr() - } + if bindToReflectValue.Kind() != reflect.Ptr && bindToReflectValue.CanAddr() { + bindToReflectValue = bindToReflectValue.Addr() } // It automatically retrieves struct field names from current attribute struct/slice. @@ -159,7 +156,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { model = model.Order(parsedTagOutput.Order) } - err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue) + err = model.Fields(fieldKeys).Where(relatedSourceName, relatedTargetValue).Scan(bindToReflectValue) if err != nil { return err } @@ -179,7 +176,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{ Pointer: pointer, PriorityTagArray: nil, RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, @@ -189,7 +186,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } // It checks the with array and automatically calls the ScanList to complete association querying. if !m.withAll { - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { for _, withItem := range m.withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { @@ -207,7 +204,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } } - for fieldName, field := range fieldMap { + for fieldName, field := range currentStructFieldMap { var ( fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") parsedTagOutput = m.parseWithTagInFieldStruct(field) @@ -225,24 +222,24 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { array = append(array, parsedTagOutput.With) } var ( - model *Model - fieldKeys []string - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} + model *Model + fieldKeys []string + relatedSourceName = array[0] + relatedTargetName = array[1] + relatedTargetValue interface{} ) // Find the value slice of related attribute from `pointer`. - for attributeName, _ := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = ListItemValuesUnique(pointer, attributeName) + for attributeName, _ := range currentStructFieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) { + relatedTargetValue = ListItemValuesUnique(pointer, attributeName) break } } - if relatedFieldValue == nil { + if relatedTargetValue == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, parsedTagOutput.With, + relatedTargetName, parsedTagOutput.With, ) } @@ -267,7 +264,9 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { model = model.Order(parsedTagOutput.Order) } - err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With) + err = model.Fields(fieldKeys). + Where(relatedSourceName, relatedTargetValue). + ScanList(pointer, fieldName, parsedTagOutput.With) if err != nil { return err } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index d1b98727e..7345221d1 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -1867,3 +1867,127 @@ func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) { t.Assert(tableA[1].TableB.TableC.Id, 300) }) } + +func Test_Table_Relation_WithAll_Embedded_Meta_NameMatchingRule(t *testing.T) { + var ( + tableUser = "user1" + tableUserDetail = "user_detail1" + tableUserScores = "user_scores1" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +user_id int(10) unsigned NOT NULL, +address varchar(45) NOT NULL, +PRIMARY KEY (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +user_id int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail1"` + UserID int `json:"user_id"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores1"` + ID int `json:"id"` + UserID int `json:"user_id"` + Score int `json:"score"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:user1"` + UserEmbedded + UserDetail UserDetail `orm:"with:user_id=id"` + UserScores []*UserScores `orm:"with:user_id=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "user_id": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.ID, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.UserID, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].UserID, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].UserID, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.ID, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.UserID, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].UserID, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].UserID, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} diff --git a/database/gdb/gdb_z_mysql_basic_test.go b/database/gdb/gdb_z_mysql_basic_test.go index b4a1a0083..610592ac3 100644 --- a/database/gdb/gdb_z_mysql_basic_test.go +++ b/database/gdb/gdb_z_mysql_basic_test.go @@ -7,10 +7,10 @@ package gdb_test import ( - "testing" - + "github.com/go-sql-driver/mysql" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/test/gtest" + "testing" ) func Test_Instance(t *testing.T) { @@ -27,3 +27,16 @@ func Test_Instance(t *testing.T) { t.Assert(err2, nil) }) } + +// Fix issue: https://github.com/gogf/gf/issues/819 +func Test_Func_ConvertDataForTableRecord(t *testing.T) { + type Test struct { + ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"` + } + gtest.C(t, func(t *gtest.T) { + m := gdb.ConvertDataForTableRecord(new(Test)) + t.Assert(len(m), 1) + t.AssertNE(m["reset_password_token_at"], nil) + t.Assert(m["reset_password_token_at"], new(mysql.NullTime)) + }) +} diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 016aef2cb..d823b1253 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -12,7 +12,6 @@ import ( "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" - "github.com/gogf/mysql" "testing" ) @@ -292,19 +291,6 @@ CREATE TABLE %s ( }) } -// Fix issue: https://github.com/gogf/gf/issues/819 -func Test_Func_ConvertDataForTableRecord(t *testing.T) { - type Test struct { - ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"` - } - gtest.C(t, func(t *gtest.T) { - m := ConvertDataForTableRecord(new(Test)) - t.Assert(len(m), 1) - t.AssertNE(m["reset_password_token_at"], nil) - t.Assert(m["reset_password_token_at"], new(mysql.NullTime)) - }) -} - func Test_isSubQuery(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(isSubQuery("user"), false) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index ee079006a..f9f95fcef 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3871,3 +3871,44 @@ func Test_Model_FieldAvg(t *testing.T) { t.Assert(all[0]["total"], 1) }) } + +// https://github.com/gogf/gf/issues/1387 +func Test_Model_GTime_DefaultValue(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Passport string + Password string + Nickname string + CreateTime *gtime.Time + } + data := User{ + Id: 1, + Passport: "user_1", + Password: "pass_1", + Nickname: "name_1", + } + // Insert + _, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + + // Select + var ( + user *User + ) + err = db.Model(table).Scan(&user) + t.AssertNil(err) + t.Assert(user.Passport, data.Passport) + t.Assert(user.Password, data.Password) + t.Assert(user.CreateTime, data.CreateTime) + t.Assert(user.Nickname, data.Nickname) + + // Insert + user.Id = 2 + _, err = db.Model(table).Data(user).Insert() + t.AssertNil(err) + }) +} diff --git a/encoding/gjson/gjson_api_encoding.go b/encoding/gjson/gjson_api_encoding.go index a0eabdb90..cea7a8a1b 100644 --- a/encoding/gjson/gjson_api_encoding.go +++ b/encoding/gjson/gjson_api_encoding.go @@ -169,14 +169,16 @@ func (j *Json) MustToTomlString() string { // INI // ======================================================================== +// ToIni json to ini func (j *Json) ToIni() ([]byte, error) { j.mu.RLock() defer j.mu.RUnlock() return gini.Encode((*(j.p)).(map[string]interface{})) } +// ToIniString ini to string func (j *Json) ToIniString() (string, error) { - b, e := j.ToToml() + b, e := j.ToIni() return string(b), e } @@ -188,6 +190,7 @@ func (j *Json) MustToIni() []byte { return result } +// MustToIniString . func (j *Json) MustToIniString() string { return gconv.UnsafeBytesToStr(j.MustToIni()) } diff --git a/go.mod b/go.mod index 264567cea..bbdb48b3a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 - github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 + github.com/go-sql-driver/mysql v1.6.0 github.com/gomodule/redigo v1.8.5 github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf diff --git a/go.sum b/go.sum index 45bd2270a..72133957f 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc= github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index 6bfb36d48..8a0b1c07d 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -5,7 +5,7 @@ // You can obtain one at https://github.com/gogf/gf. // Package gcache provides kinds of cache management for process. -// It default provides a concurrent-safe in-memory cache adapter for process. +// It provides a concurrent-safe in-memory cache adapter for process in default. package gcache import ( @@ -23,62 +23,62 @@ func Ctx(ctx context.Context) *Cache { return defaultCache.Ctx(ctx) } -// Set sets cache with - pair, which is expired after . -// It does not expire if == 0. -func Set(key interface{}, value interface{}, duration time.Duration) { - defaultCache.Set(key, value, duration) +// Set sets cache with `key`-`value` pair, which is expired after `duration`. +// It does not expire if `duration` == 0. +func Set(key interface{}, value interface{}, duration time.Duration) error { + return defaultCache.Set(key, value, duration) } -// SetIfNotExist sets cache with - pair if does not exist in the cache, -// which is expired after . It does not expire if == 0. +// SetIfNotExist sets cache with `key`-`value` pair if `key` does not exist in the cache, +// which is expired after `duration`. It does not expire if `duration` == 0. func SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { return defaultCache.SetIfNotExist(key, value, duration) } -// Sets batch sets cache with key-value pairs by , which is expired after . +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if == 0. +// It does not expire if `duration` == 0. func Sets(data map[interface{}]interface{}, duration time.Duration) error { return defaultCache.Sets(data, duration) } -// Get returns the value of . +// Get returns the value of `key`. // It returns nil if it does not exist or its value is nil. func Get(key interface{}) (interface{}, error) { return defaultCache.Get(key) } -// GetVar retrieves and returns the value of as gvar.Var. +// GetVar retrieves and returns the value of `key` as gvar.Var. func GetVar(key interface{}) (*gvar.Var, error) { return defaultCache.GetVar(key) } -// GetOrSet returns the value of , -// or sets - pair and returns if does not exist in the cache. -// The key-value pair expires after . +// GetOrSet returns the value of `key`, +// or sets `key`-`value` pair and returns `value` if `key` does not exist in the cache. +// The key-value pair expires after `duration`. // -// It does not expire if == 0. +// It does not expire if `duration` == 0. func GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { return defaultCache.GetOrSet(key, value, duration) } -// GetOrSetFunc returns the value of , or sets with result of function -// and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if == 0. +// GetOrSetFunc returns the value of `key`, or sets `key` with result of function `f` +// and returns its result if `key` does not exist in the cache. The key-value pair expires +// after `duration`. It does not expire if `duration` == 0. func GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return defaultCache.GetOrSetFunc(key, f, duration) } -// GetOrSetFuncLock returns the value of , or sets with result of function -// and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if == 0. +// GetOrSetFuncLock returns the value of `key`, or sets `key` with result of function `f` +// and returns its result if `key` does not exist in the cache. The key-value pair expires +// after `duration`. It does not expire if `duration` == 0. // -// Note that the function is executed within writing mutex lock. +// Note that the function `f` is executed within writing mutex lock. func GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return defaultCache.GetOrSetFuncLock(key, f, duration) } -// Contains returns true if exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func Contains(key interface{}) (bool, error) { return defaultCache.Contains(key) } @@ -89,10 +89,10 @@ func Remove(keys ...interface{}) (value interface{}, err error) { return defaultCache.Remove(keys...) } -// Removes deletes in the cache. +// Removes deletes `keys` in the cache. // Deprecated, use Remove instead. func Removes(keys []interface{}) { - defaultCache.Removes(keys) + defaultCache.Remove(keys...) } // Data returns a copy of all key-value pairs in the cache as map type. @@ -120,20 +120,20 @@ func Size() (int, error) { return defaultCache.Size() } -// GetExpire retrieves and returns the expiration of . -// It returns -1 if the does not exist in the cache. +// GetExpire retrieves and returns the expiration of `key`. +// It returns -1 if the `key` does not exist in the cache. func GetExpire(key interface{}) (time.Duration, error) { return defaultCache.GetExpire(key) } -// Update updates the value of without changing its expiration and returns the old value. -// The returned value is false if the does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned `exist` value is false if the `key` does not exist in the cache. func Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return defaultCache.Update(key, value) } -// UpdateExpire updates the expiration of and returns the old expiration duration value. -// It returns -1 if the does not exist in the cache. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. +// It returns -1 if the `key` does not exist in the cache. func UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { return defaultCache.UpdateExpire(key, duration) } diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index f12c32570..33f179058 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -11,95 +11,95 @@ import ( "time" ) -// Adapter is the adapter for cache features implements. +// Adapter is the core adapter for cache features implements. type Adapter interface { - // Set sets cache with - pair, which is expired after . + // Set sets cache with `key`-`value` pair, which is expired after `duration`. // - // It does not expire if == 0. - // It deletes the if < 0. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0. Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error - // Sets batch sets cache with key-value pairs by , which is expired after . + // Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // - // It does not expire if == 0. - // It deletes the keys of if < 0 or given is nil. + // It does not expire if `duration` == 0. + // It deletes the keys of `data` if `duration` < 0 or given `value` is nil. Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error - // SetIfNotExist sets cache with - pair which is expired after - // if does not exist in the cache. It returns true the dose not exist in the - // cache and it sets successfully to the cache, or else it returns false. + // SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` + // if `key` does not exist in the cache. It returns true the `key` does not exist in the + // cache, and it sets `value` successfully to the cache, or else it returns false. // - // The parameter can be type of , but it dose nothing if its + // The parameter `value` can be type of `func() interface{}`, but it does nothing if its // result is nil. // - // It does not expire if == 0. - // It deletes the if < 0 or given is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil. SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) - // Get retrieves and returns the associated value of given . - // It returns nil if it does not exist, its value is nil or it's expired. + // Get retrieves and returns the associated value of given `key`. + // It returns nil if it does not exist, its value is nil, or it's expired. Get(ctx context.Context, key interface{}) (interface{}, error) - // GetOrSet retrieves and returns the value of , or sets - pair and - // returns if does not exist in the cache. The key-value pair expires - // after . + // GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and + // returns `value` if `key` does not exist in the cache. The key-value pair expires + // after `duration`. // - // It does not expire if == 0. - // It deletes the if < 0 or given is nil, but it does nothing - // if is a function and the function result is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing + // if `value` is a function and the function result is nil. GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) - // GetOrSetFunc retrieves and returns the value of , or sets with result of - // function and returns its result if does not exist in the cache. The key-value - // pair expires after . + // GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of + // function `f` and returns its result if `key` does not exist in the cache. The key-value + // pair expires after `duration`. // - // It does not expire if == 0. - // It deletes the if < 0 or given is nil, but it does nothing - // if is a function and the function result is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing + // if `value` is a function and the function result is nil. GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) - // GetOrSetFuncLock retrieves and returns the value of , or sets with result of - // function and returns its result if does not exist in the cache. The key-value - // pair expires after . + // GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of + // function `f` and returns its result if `key` does not exist in the cache. The key-value + // pair expires after `duration`. // - // It does not expire if == 0. - // It does nothing if function returns nil. + // It does not expire if `duration` == 0. + // It does nothing if function `f` returns nil. // - // Note that the function should be executed within writing mutex lock for concurrent + // Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) - // Contains returns true if exists in the cache, or else returns false. + // Contains returns true if `key` exists in the cache, or else returns false. Contains(ctx context.Context, key interface{}) (bool, error) - // GetExpire retrieves and returns the expiration of in the cache. + // GetExpire retrieves and returns the expiration of `key` in the cache. // - // It returns 0 if the does not expire. - // It returns -1 if the does not exist in the cache. + // It returns 0 if the `key` does not expire. + // It returns -1 if the `key` does not exist in the cache. GetExpire(ctx context.Context, key interface{}) (time.Duration, error) // Remove deletes one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the last deleted item. Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error) - // Update updates the value of without changing its expiration and returns the old value. - // The returned value is false if the does not exist in the cache. + // Update updates the value of `key` without changing its expiration and returns the old value. + // The returned value `exist` is false if the `key` does not exist in the cache. // - // It deletes the if given is nil. - // It does nothing if does not exist in the cache. + // It deletes the `key` if given `value` is nil. + // It does nothing if `key` does not exist in the cache. Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) - // UpdateExpire updates the expiration of and returns the old expiration duration value. + // UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // - // It returns -1 and does nothing if the does not exist in the cache. - // It deletes the if < 0. + // It returns -1 and does nothing if the `key` does not exist in the cache. + // It deletes the `key` if `duration` < 0. UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) // Size returns the number of items in the cache. Size(ctx context.Context) (size int, err error) // Data returns a copy of all key-value pairs in the cache as map type. - // Note that this function may leads lots of memory usage, you can implement this function + // Note that this function may lead lots of memory usage, you can implement this function // if necessary. Data(ctx context.Context) (map[interface{}]interface{}, error) diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index cc8dba5b2..c70b2b3e3 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -29,7 +29,7 @@ type adapterMemory struct { expireTimes *adapterMemoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. expireSets *adapterMemoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. lru *adapterMemoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. - lruGetList *glist.List // lruGetList is the LRU history according with Get function. + lruGetList *glist.List // lruGetList is the LRU history according to Get function. eventList *glist.List // eventList is the asynchronous event list for internal data synchronization. closed *gtype.Bool // closed controls the cache closed or not. } @@ -69,10 +69,10 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { return c } -// Set sets cache with - pair, which is expired after . +// Set sets cache with `key`-`value` pair, which is expired after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0. func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error { expireTime := c.getInternalExpire(duration) c.data.Set(key, adapterMemoryItem{ @@ -86,19 +86,19 @@ func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interfac return nil } -// Update updates the value of without changing its expiration and returns the old value. -// The returned value is false if the does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the if given is nil. -// It does nothing if does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (c *adapterMemory) Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return c.data.Update(key, value) } -// UpdateExpire updates the expiration of and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the does not exist in the cache. -// It deletes the if < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { newExpireTime := c.getInternalExpire(duration) oldDuration, err = c.data.UpdateExpire(key, newExpireTime) @@ -114,10 +114,10 @@ func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, durat return } -// GetExpire retrieves and returns the expiration of in the cache. +// GetExpire retrieves and returns the expiration of `key` in the cache. // -// It returns 0 if the does not expire. -// It returns -1 if the does not exist in the cache. +// It returns 0 if the `key` does not expire. +// It returns -1 if the `key` does not exist in the cache. func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) { if item, ok := c.data.Get(key); ok { return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil @@ -125,14 +125,14 @@ func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Du return -1, nil } -// SetIfNotExist sets cache with - pair which is expired after -// if does not exist in the cache. It returns true the dose not exist in the -// cache and it sets successfully to the cache, or else it returns false. -// The parameter can be type of , but it dose nothing if its +// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` +// if `key` does not exist in the cache. It returns true the `key` does not exist in the +// cache, and it sets `value` successfully to the cache, or else it returns false. +// The parameter `value` can be type of , but it dose nothing if its // result is nil. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil. func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) { isContained, err := c.Contains(ctx, key) if err != nil { @@ -148,10 +148,10 @@ func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, valu return false, nil } -// Sets batch sets cache with key-value pairs by , which is expired after . +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if == 0. -// It deletes the keys of if < 0 or given is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error { var ( expireTime = c.getInternalExpire(duration) @@ -169,7 +169,7 @@ func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface return nil } -// Get retrieves and returns the associated value of given . +// Get retrieves and returns the associated value of given `key`. // It returns nil if it does not exist or its value is nil. func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, error) { item, ok := c.data.Get(key) @@ -183,13 +183,13 @@ func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, return nil, nil } -// GetOrSet retrieves and returns the value of , or sets - pair and -// returns if does not exist in the cache. The key-value pair expires -// after . +// GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and +// returns `value` if `key` does not exist in the cache. The key-value pair expires +// after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil, but it does nothing -// if is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) if err != nil { @@ -202,13 +202,13 @@ func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value int } } -// GetOrSetFunc retrieves and returns the value of , or sets with result of -// function and returns its result if does not exist in the cache. The key-value -// pair expires after . +// GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil, but it does nothing -// if is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) if err != nil { @@ -228,14 +228,14 @@ func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f fun } } -// GetOrSetFuncLock retrieves and returns the value of , or sets with result of -// function and returns its result if does not exist in the cache. The key-value -// pair expires after . +// GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if == 0. -// It does nothing if function returns nil. +// It does not expire if `duration` == 0. +// It does nothing if function `f` returns nil. // -// Note that the function should be executed within writing mutex lock for concurrent +// Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) @@ -249,7 +249,7 @@ func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f } } -// Contains returns true if exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func (c *adapterMemory) Contains(ctx context.Context, key interface{}) (bool, error) { v, err := c.Get(ctx, key) if err != nil { @@ -310,14 +310,14 @@ func (c *adapterMemory) Close(ctx context.Context) error { return nil } -// doSetWithLockCheck sets cache with - pair if does not exist in the -// cache, which is expired after . +// doSetWithLockCheck sets cache with `key`-`value` pair if `key` does not exist in the +// cache, which is expired after `duration`. // -// It does not expire if == 0. -// The parameter can be type of , but it dose nothing if the +// It does not expire if `duration` == 0. +// The parameter `value` can be type of , but it dose nothing if the // function result is nil. // -// It doubly checks the whether exists in the cache using mutex writing lock +// It doubly checks the `key` whether exists in the cache using mutex writing lock // before setting it to the cache. func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (result interface{}, err error) { expireTimestamp := c.getInternalExpire(duration) @@ -335,14 +335,14 @@ func (c *adapterMemory) getInternalExpire(duration time.Duration) int64 { } } -// makeExpireKey groups the in milliseconds to its according seconds. +// makeExpireKey groups the `expire` in milliseconds to its according seconds. func (c *adapterMemory) makeExpireKey(expire int64) int64 { return int64(math.Ceil(float64(expire/1000)+1) * 1000) } // syncEventAndClearExpired does the asynchronous task loop: // 1. Asynchronously process the data in the event list, -// and synchronize the results to the and properties. +// and synchronize the results to the `expireTimes` and `expireSets` properties. // 2. Clean up the expired key-value pair data. func (c *adapterMemory) syncEventAndClearExpired() { if c.closed.Val() { @@ -411,13 +411,13 @@ func (c *adapterMemory) syncEventAndClearExpired() { } } -// clearByKey deletes the key-value pair with given . -// The parameter specifies whether doing this deleting forcibly. +// clearByKey deletes the key-value pair with given `key`. +// The parameter `force` specifies whether doing this deleting forcibly. func (c *adapterMemory) clearByKey(key interface{}, force ...bool) { // Doubly check before really deleting it from cache. c.data.DeleteWithDoubleCheck(key, force...) - // Deleting its expire time from . + // Deleting its expire time from `expireTimes`. c.expireTimes.Delete(key) // Deleting it from LRU. diff --git a/os/gcache/gcache_adapter_memory_data.go b/os/gcache/gcache_adapter_memory_data.go index 839922e41..86b20e0fc 100644 --- a/os/gcache/gcache_adapter_memory_data.go +++ b/os/gcache/gcache_adapter_memory_data.go @@ -23,11 +23,11 @@ func newAdapterMemoryData() *adapterMemoryData { } } -// Update updates the value of without changing its expiration and returns the old value. -// The returned value is false if the does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the if given is nil. -// It does nothing if does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { d.mu.Lock() defer d.mu.Unlock() @@ -41,10 +41,10 @@ func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue return nil, false, nil } -// UpdateExpire updates the expiration of and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the does not exist in the cache. -// It deletes the if < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (d *adapterMemoryData) UpdateExpire(key interface{}, expireTime int64) (oldDuration time.Duration, err error) { d.mu.Lock() defer d.mu.Unlock() @@ -152,10 +152,10 @@ func (d *adapterMemoryData) Set(key interface{}, value adapterMemoryItem) { d.mu.Unlock() } -// Sets batch sets cache with key-value pairs by , which is expired after . +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if == 0. -// It deletes the keys of if < 0 or given is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (d *adapterMemoryData) Sets(data map[interface{}]interface{}, expireTime int64) error { d.mu.Lock() for k, v := range data { diff --git a/os/gcache/gcache_adapter_memory_item.go b/os/gcache/gcache_adapter_memory_item.go index 44b72a5ef..ce411be8f 100644 --- a/os/gcache/gcache_adapter_memory_item.go +++ b/os/gcache/gcache_adapter_memory_item.go @@ -10,7 +10,7 @@ import ( "github.com/gogf/gf/os/gtime" ) -// IsExpired checks whether is expired. +// IsExpired checks whether `item` is expired. func (item *adapterMemoryItem) IsExpired() bool { // Note that it should use greater than or equal judgement here // imagining that the cache time is only 1 millisecond. diff --git a/os/gcache/gcache_adapter_memory_lru.go b/os/gcache/gcache_adapter_memory_lru.go index 455ce6d7d..c2f2a64d5 100644 --- a/os/gcache/gcache_adapter_memory_lru.go +++ b/os/gcache/gcache_adapter_memory_lru.go @@ -43,7 +43,7 @@ func (lru *adapterMemoryLru) Close() { lru.closed.Set(true) } -// Remove deletes the FROM . +// Remove deletes the `key` FROM `lru`. func (lru *adapterMemoryLru) Remove(key interface{}) { if v := lru.data.Get(key); v != nil { lru.data.Remove(key) @@ -51,17 +51,17 @@ func (lru *adapterMemoryLru) Remove(key interface{}) { } } -// Size returns the size of . +// Size returns the size of `lru`. func (lru *adapterMemoryLru) Size() int { return lru.data.Size() } -// Push pushes to the tail of . +// Push pushes `key` to the tail of `lru`. func (lru *adapterMemoryLru) Push(key interface{}) { lru.rawList.PushBack(key) } -// Pop deletes and returns the key from tail of . +// Pop deletes and returns the key from tail of `lru`. func (lru *adapterMemoryLru) Pop() interface{} { if v := lru.list.PopBack(); v != nil { lru.data.Remove(v) @@ -78,7 +78,7 @@ func (lru *adapterMemoryLru) Pop() interface{} { // fmt.Println() //} -// SyncAndClear synchronizes the keys from to and +// SyncAndClear synchronizes the keys from `rawList` to `list` and `data` // using Least Recently Used algorithm. func (lru *adapterMemoryLru) SyncAndClear() { if lru.closed.Val() { diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index 54f5ea626..216549bd9 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -56,13 +56,13 @@ func (c *Cache) SetAdapter(adapter Adapter) { c.adapter = adapter } -// GetVar retrieves and returns the value of as gvar.Var. +// GetVar retrieves and returns the value of `key` as gvar.Var. func (c *Cache) GetVar(key interface{}) (*gvar.Var, error) { v, err := c.Get(key) return gvar.New(v), err } -// Removes deletes in the cache. +// Removes deletes `keys` in the cache. // Deprecated, use Remove instead. func (c *Cache) Removes(keys []interface{}) error { _, err := c.Remove(keys...) diff --git a/os/gcache/gcache_cache_adapter.go b/os/gcache/gcache_cache_adapter.go index 0f4f50852..dce6b19ef 100644 --- a/os/gcache/gcache_cache_adapter.go +++ b/os/gcache/gcache_cache_adapter.go @@ -10,85 +10,85 @@ import ( "time" ) -// Set sets cache with - pair, which is expired after . +// Set sets cache with `key`-`value` pair, which is expired after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0. func (c *Cache) Set(key interface{}, value interface{}, duration time.Duration) error { return c.adapter.Set(c.getCtx(), key, value, duration) } -// Sets batch sets cache with key-value pairs by , which is expired after . +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if == 0. -// It deletes the keys of if < 0 or given is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) error { return c.adapter.Sets(c.getCtx(), data, duration) } -// SetIfNotExist sets cache with - pair which is expired after -// if does not exist in the cache. It returns true the dose not exist in the -// cache and it sets successfully to the cache, or else it returns false. +// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` +// if `key` does not exist in the cache. It returns true the `key` does not exist in the +// cache, and it sets `value` successfully to the cache, or else it returns false. // -// The parameter can be type of , but it dose nothing if its +// The parameter `value` can be type of , but it does nothing if its // result is nil. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil. func (c *Cache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { return c.adapter.SetIfNotExist(c.getCtx(), key, value, duration) } -// Get retrieves and returns the associated value of given . +// Get retrieves and returns the associated value of given `key`. // It returns nil if it does not exist, its value is nil or it's expired. func (c *Cache) Get(key interface{}) (interface{}, error) { return c.adapter.Get(c.getCtx(), key) } -// GetOrSet retrieves and returns the value of , or sets - pair and -// returns if does not exist in the cache. The key-value pair expires -// after . +// GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and +// returns `value` if `key` does not exist in the cache. The key-value pair expires +// after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil, but it does nothing -// if is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *Cache) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { return c.adapter.GetOrSet(c.getCtx(), key, value, duration) } -// GetOrSetFunc retrieves and returns the value of , or sets with result of -// function and returns its result if does not exist in the cache. The key-value -// pair expires after . +// GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if == 0. -// It deletes the if < 0 or given is nil, but it does nothing -// if is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *Cache) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return c.adapter.GetOrSetFunc(c.getCtx(), key, f, duration) } -// GetOrSetFuncLock retrieves and returns the value of , or sets with result of -// function and returns its result if does not exist in the cache. The key-value -// pair expires after . +// GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if == 0. -// It does nothing if function returns nil. +// It does not expire if `duration` == 0. +// It does nothing if function `f` returns nil. // -// Note that the function should be executed within writing mutex lock for concurrent +// Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. func (c *Cache) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return c.adapter.GetOrSetFuncLock(c.getCtx(), key, f, duration) } -// Contains returns true if exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func (c *Cache) Contains(key interface{}) (bool, error) { return c.adapter.Contains(c.getCtx(), key) } -// GetExpire retrieves and returns the expiration of in the cache. +// GetExpire retrieves and returns the expiration of `key` in the cache. // -// It returns 0 if the does not expire. -// It returns -1 if the does not exist in the cache. +// It returns 0 if the `key` does not expire. +// It returns -1 if the `key` does not exist in the cache. func (c *Cache) GetExpire(key interface{}) (time.Duration, error) { return c.adapter.GetExpire(c.getCtx(), key) } @@ -99,19 +99,19 @@ func (c *Cache) Remove(keys ...interface{}) (value interface{}, err error) { return c.adapter.Remove(c.getCtx(), keys...) } -// Update updates the value of without changing its expiration and returns the old value. -// The returned value is false if the does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the if given is nil. -// It does nothing if does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (c *Cache) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return c.adapter.Update(c.getCtx(), key, value) } -// UpdateExpire updates the expiration of and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the does not exist in the cache. -// It deletes the if < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (c *Cache) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { return c.adapter.UpdateExpire(c.getCtx(), key, duration) } @@ -122,7 +122,7 @@ func (c *Cache) Size() (size int, err error) { } // Data returns a copy of all key-value pairs in the cache as map type. -// Note that this function may leads lots of memory usage, you can implement this function +// Note that this function may lead lots of memory usage, you can implement this function // if necessary. func (c *Cache) Data() (map[interface{}]interface{}, error) { return c.adapter.Data(c.getCtx()) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index aeb63bc8b..66c5692aa 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -133,11 +133,13 @@ func Entries() []*Entry { } // Start starts running the specified timed task named `name`. -func Start(name string) { - defaultCron.Start(name) +// If no`name` specified, it starts the entire cron. +func Start(name ...string) { + defaultCron.Start(name...) } // Stop stops running the specified timed task named `name`. -func Stop(name string) { - defaultCron.Stop(name) +// If no`name` specified, it stops the entire cron. +func Stop(name ...string) { + defaultCron.Stop(name...) } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index cace246d2..4b954810d 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -184,6 +184,7 @@ func (c *Cron) Search(name string) *Entry { } // Start starts running the specified timed task named `name`. +// If no`name` specified, it starts the entire cron. func (c *Cron) Start(name ...string) { if len(name) > 0 { for _, v := range name { @@ -197,6 +198,7 @@ func (c *Cron) Start(name ...string) { } // Stop stops running the specified timed task named `name`. +// If no`name` specified, it stops the entire cron. func (c *Cron) Stop(name ...string) { if len(name) > 0 { for _, v := range name { diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index e1354f19f..c4c28d490 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -33,7 +33,7 @@ func GetContentsWithCache(path string, duration ...time.Duration) string { return string(GetBytesWithCache(path, duration...)) } -// GetBinContents returns []byte content of given file by from cache. +// GetBytesWithCache returns []byte content of given file by from cache. // If there's no content in the cache, it will read it from disk file specified by . // The parameter specifies the caching time for this file content in seconds. func GetBytesWithCache(path string, duration ...time.Duration) []byte { diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index 6eb1e1a7d..3c47a7f0a 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -12,7 +12,7 @@ import ( "github.com/gogf/gf/internal/intlog" ) -// watchLoop starts the loop for event listening fro underlying inotify monitor. +// watchLoop starts the loop for event listening from underlying inotify monitor. func (w *Watcher) watchLoop() { go func() { for { diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 28342804b..b52b860b1 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -277,8 +277,9 @@ func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) *bytes. ) // This will lose color in Windows os system. // if _, err := os.Stdout.Write(input.getRealBuffer(true).Bytes()); err != nil { + // This will print color in Windows os system. - if _, err := fmt.Fprintf(color.Output, buffer.String()); err != nil { + if _, err := fmt.Fprint(color.Output, buffer.String()); err != nil { intlog.Error(ctx, err) } return buffer diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 51a40f2bd..4ab499ca6 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -7,10 +7,12 @@ package gproc import ( + "context" "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" @@ -32,9 +34,10 @@ type MsgResponse struct { } const ( - defaultGroupNameFoProcComm = "" // Default group name. - defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening. - maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group. + defaultFolderNameForProcComm = "gf_pid_port_mapping" // Default folder name for storing pid to port mapping files. + defaultGroupNameForProcComm = "" // Default group name. + defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening. + maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group. ) var ( @@ -43,17 +46,36 @@ var ( commReceiveQueues = gmap.NewStrAnyMap(true) // commPidFolderPath specifies the folder path storing pid to port mapping files. - commPidFolderPath = gfile.TempDir("gproc") + commPidFolderPath string ) func init() { - // Automatically create the storage folder. - if !gfile.Exists(commPidFolderPath) { - err := gfile.Mkdir(commPidFolderPath) - if err != nil { - panic(fmt.Errorf(`create gproc folder failed: %v`, err)) + availablePaths := []string{ + "/var/tmp", + "/var/run", + } + if homePath, _ := gfile.Home(); homePath != "" { + availablePaths = append(availablePaths, gfile.Join(homePath, ".config")) + } + availablePaths = append(availablePaths, gfile.TempDir()) + for _, availablePath := range availablePaths { + checkPath := gfile.Join(availablePath, defaultFolderNameForProcComm) + if !gfile.Exists(checkPath) && gfile.Mkdir(checkPath) != nil { + continue + } + if gfile.IsWritable(checkPath) { + commPidFolderPath = checkPath + break } } + if commPidFolderPath == "" { + intlog.Errorf( + context.TODO(), + `cannot find available folder for storing pid to port mapping files in paths: %+v, process communication feature will fail`, + availablePaths, + ) + } + } // getConnByPid creates and returns a TCP connection for specified pid. @@ -72,9 +94,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) { // getPortByPid returns the listening port for specified pid. // It returns 0 if no port found for the specified pid. func getPortByPid(pid int) int { - path := getCommFilePath(pid) - content := gfile.GetContentsWithCache(path) - return gconv.Int(content) + return gconv.Int(gfile.GetContentsWithCache(getCommFilePath(pid))) } // getCommFilePath returns the pid to port mapping file path for given pid. diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index df4f52412..f358db822 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -35,7 +35,7 @@ func Receive(group ...string) *MsgRequest { if len(group) > 0 { groupName = group[0] } else { - groupName = defaultGroupNameFoProcComm + groupName = defaultGroupNameForProcComm } queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} { return gqueue.New(maxLengthForProcMsgQueue) @@ -79,8 +79,10 @@ func receiveTcpListening() { // receiveTcpHandler is the connection handler for receiving data. func receiveTcpHandler(conn *gtcp.Conn) { - var result []byte - var response MsgResponse + var ( + result []byte + response MsgResponse + ) for { response.Code = 0 response.Message = "" diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 5a06cef62..9e05d5437 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -18,7 +18,7 @@ func Send(pid int, data []byte, group ...string) error { msg := MsgRequest{ SendPid: Pid(), RecvPid: pid, - Group: defaultGroupNameFoProcComm, + Group: defaultGroupNameForProcComm, Data: data, } if len(group) > 0 { diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index cb164da44..318704c83 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -197,7 +197,7 @@ func ISO8601() string { return time.Now().Format("2006-01-02T15:04:05-07:00") } -// ISO8601 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST". +// RFC822 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST". func RFC822() string { return time.Now().Format("Mon, 02 Jan 06 15:04 MST") } @@ -238,6 +238,9 @@ func parseDateStr(s string) (year, month, day int) { // If is not given, it converts string as a "standard" datetime string. // Note that, it fails and returns error if there's no date string in . func StrToTime(str string, format ...string) (*Time, error) { + if str == "" { + return &Time{wrapper{time.Time{}}}, nil + } if len(format) > 0 { return StrToTimeFormat(str, format[0]) } diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 185c9dbd9..33e0de5e4 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -33,10 +33,13 @@ func New(param ...interface{}) *Time { return NewFromTime(r) case *time.Time: return NewFromTime(*r) + case Time: return &r + case *Time: return r + case string: if len(param) > 1 { switch t := param[1].(type) { @@ -47,6 +50,7 @@ func New(param ...interface{}) *Time { } } return NewFromStr(r) + case []byte: if len(param) > 1 { switch t := param[1].(type) { @@ -57,10 +61,13 @@ func New(param ...interface{}) *Time { } } return NewFromStr(string(r)) + case int: return NewFromTimeStamp(int64(r)) + case int64: return NewFromTimeStamp(r) + default: if v, ok := r.(apiUnixNano); ok { return NewFromTimeStamp(v.UnixNano()) diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 903d609df..4a81426d4 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -28,95 +28,22 @@ const ( NotFoundIndex = -1 ) -// Replace returns a copy of the string -// in which string replaced by case-sensitively. -func Replace(origin, search, replace string, count ...int) string { - n := -1 - if len(count) > 0 { - n = count[0] - } - return strings.Replace(origin, search, replace, n) -} +const ( + defaultSuffixForStrLimit = "..." +) -// Replace returns a copy of the string -// in which string replaced by case-insensitively. -func ReplaceI(origin, search, replace string, count ...int) string { - n := -1 - if len(count) > 0 { - n = count[0] - } - if n == 0 { - return origin - } - var ( - length = len(search) - searchLower = strings.ToLower(search) - ) - for { - originLower := strings.ToLower(origin) - if pos := strings.Index(originLower, searchLower); pos != -1 { - origin = origin[:pos] + replace + origin[pos+length:] - if n--; n == 0 { - break - } - } else { - break - } - } - return origin -} - -// Count counts the number of appears in . -// It returns 0 if no found in . +// Count counts the number of `substr` appears in `s`. +// It returns 0 if no `substr` found in `s`. func Count(s, substr string) int { return strings.Count(s, substr) } -// CountI counts the number of appears in , case-insensitively. -// It returns 0 if no found in . +// CountI counts the number of `substr` appears in `s`, case-insensitively. +// It returns 0 if no `substr` found in `s`. func CountI(s, substr string) int { return strings.Count(ToLower(s), ToLower(substr)) } -// ReplaceByArray returns a copy of , -// which is replaced by a slice in order, case-sensitively. -func ReplaceByArray(origin string, array []string) string { - for i := 0; i < len(array); i += 2 { - if i+1 >= len(array) { - break - } - origin = Replace(origin, array[i], array[i+1]) - } - return origin -} - -// ReplaceIByArray returns a copy of , -// which is replaced by a slice in order, case-insensitively. -func ReplaceIByArray(origin string, array []string) string { - for i := 0; i < len(array); i += 2 { - if i+1 >= len(array) { - break - } - origin = ReplaceI(origin, array[i], array[i+1]) - } - return origin -} - -// ReplaceByMap returns a copy of , -// which is replaced by a map in unordered way, case-sensitively. -func ReplaceByMap(origin string, replaces map[string]string) string { - return utils.ReplaceByMap(origin, replaces) -} - -// ReplaceIByMap returns a copy of , -// which is replaced by a map in unordered way, case-insensitively. -func ReplaceIByMap(origin string, replaces map[string]string) string { - for k, v := range replaces { - origin = ReplaceI(origin, k, v) - } - return origin -} - // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. func ToLower(s string) string { return strings.ToLower(s) @@ -163,86 +90,7 @@ func IsNumeric(s string) bool { return utils.IsNumeric(s) } -// SubStr returns a portion of string specified by the and parameters. -func SubStr(str string, start int, length ...int) (substr string) { - lth := len(str) - - // Simple border checks. - if start < 0 { - start = 0 - } - if start >= lth { - start = lth - } - end := lth - if len(length) > 0 { - end = start + length[0] - if end < start { - end = lth - } - } - if end > lth { - end = lth - } - return str[start:end] -} - -// SubStrRune returns a portion of string specified by the and parameters. -// SubStrRune considers parameter as unicode string. -func SubStrRune(str string, start int, length ...int) (substr string) { - // Converting to []rune to support unicode. - rs := []rune(str) - lth := len(rs) - - // Simple border checks. - if start < 0 { - start = 0 - } - if start >= lth { - start = lth - } - end := lth - if len(length) > 0 { - end = start + length[0] - if end < start { - end = lth - } - } - if end > lth { - end = lth - } - return string(rs[start:end]) -} - -// StrLimit returns a portion of string specified by parameters, if the length -// of is greater than , then the will be appended to the result string. -func StrLimit(str string, length int, suffix ...string) string { - if len(str) < length { - return str - } - addStr := "..." - if len(suffix) > 0 { - addStr = suffix[0] - } - return str[0:length] + addStr -} - -// StrLimitRune returns a portion of string specified by parameters, if the length -// of is greater than , then the will be appended to the result string. -// StrLimitRune considers parameter as unicode string. -func StrLimitRune(str string, length int, suffix ...string) string { - rs := []rune(str) - if len(rs) < length { - return str - } - addStr := "..." - if len(suffix) > 0 { - addStr = suffix[0] - } - return string(rs[0:length]) + addStr -} - -// Reverse returns a string which is the reverse of . +// Reverse returns a string which is the reverse of `str`. func Reverse(str string) string { runes := []rune(str) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { @@ -252,9 +100,9 @@ func Reverse(str string) string { } // NumberFormat formats a number with grouped thousands. -// : Sets the number of decimal points. -// : Sets the separator for the decimal point. -// : Sets the thousands separator. +// `decimals`: Sets the number of decimal points. +// `decPoint`: Sets the separator for the decimal point. +// `thousandsSep`: Sets the thousands' separator. // See http://php.net/manual/en/function.number-format.php. func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string { neg := false @@ -301,7 +149,7 @@ func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) s // Can be used to split a string into smaller chunks which is useful for // e.g. converting BASE64 string output to match RFC 2045 semantics. // It inserts end every chunkLen characters. -// It considers parameter and as unicode string. +// It considers parameter `body` and `end` as unicode string. func ChunkSplit(body string, chunkLen int, end string) string { if end == "" { end = "\r\n" @@ -329,7 +177,7 @@ func Compare(a, b string) int { return strings.Compare(a, b) } -// Equal reports whether and , interpreted as UTF-8 strings, +// Equal reports whether `a` and `b`, interpreted as UTF-8 strings, // are equal under Unicode case-folding, case-insensitively. func Equal(a, b string) bool { return strings.EqualFold(a, b) @@ -351,7 +199,7 @@ func HasSuffix(s, suffix string) bool { } // CountWords returns information about words' count used in a string. -// It considers parameter as unicode string. +// It considers parameter `str` as unicode string. func CountWords(str string) map[string]int { m := make(map[string]int) buffer := bytes.NewBuffer(nil) @@ -372,7 +220,7 @@ func CountWords(str string) map[string]int { } // CountChars returns information about chars' count used in a string. -// It considers parameter as unicode string. +// It considers parameter `str` as unicode string. func CountChars(str string, noSpace ...bool) map[string]int { m := make(map[string]int) countSpace := true @@ -465,51 +313,8 @@ func Repeat(input string, multiplier int) string { return strings.Repeat(input, multiplier) } -// Str returns part of string starting from and including -// the first occurrence of to the end of . -// See http://php.net/manual/en/function.strstr.php. -func Str(haystack string, needle string) string { - if needle == "" { - return "" - } - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex { - return "" - } - return haystack[pos+len([]byte(needle))-1:] -} - -// StrEx returns part of string starting from and excluding -// the first occurrence of to the end of . -func StrEx(haystack string, needle string) string { - if s := Str(haystack, needle); s != "" { - return s[1:] - } - return "" -} - -// StrTill returns part of string ending to and including -// the first occurrence of from the start of . -func StrTill(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos+1] -} - -// StrTillEx returns part of string ending to and excluding -// the first occurrence of from the start of . -func StrTillEx(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos] -} - // Shuffle randomly shuffles a string. -// It considers parameter as unicode string. +// It considers parameter `str` as unicode string. func Shuffle(str string) string { runes := []rune(str) s := make([]rune, len(runes)) @@ -519,12 +324,12 @@ func Shuffle(str string) string { return string(s) } -// Split splits string by a string , to an array. +// Split splits string `str` by a string `delimiter`, to an array. func Split(str, delimiter string) []string { return strings.Split(str, delimiter) } -// SplitAndTrim splits string by a string to an array, +// SplitAndTrim splits string `str` by a string `delimiter` to an array, // and calls Trim to every element of this array. It ignores the elements // which are empty after Trim. func SplitAndTrim(str, delimiter string, characterMask ...string) []string { @@ -538,7 +343,7 @@ func SplitAndTrim(str, delimiter string, characterMask ...string) []string { return array } -// SplitAndTrimSpace splits string by a string to an array, +// SplitAndTrimSpace splits string `str` by a string `delimiter` to an array, // and calls TrimSpace to every element of this array. // Deprecated, use SplitAndTrim instead. func SplitAndTrimSpace(str, delimiter string) []string { @@ -552,27 +357,27 @@ func SplitAndTrimSpace(str, delimiter string) []string { return array } -// Join concatenates the elements of to create a single string. The separator string -// is placed between elements in the resulting string. +// Join concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. func Join(array []string, sep string) string { return strings.Join(array, sep) } -// JoinAny concatenates the elements of to create a single string. The separator string -// is placed between elements in the resulting string. +// JoinAny concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. // -// The parameter can be any type of slice, which be converted to string array. +// The parameter `array` can be any type of slice, which be converted to string array. func JoinAny(array interface{}, sep string) string { return strings.Join(gconv.Strings(array), sep) } -// Explode splits string by a string , to an array. +// Explode splits string `str` by a string `delimiter`, to an array. // See http://php.net/manual/en/function.explode.php. func Explode(delimiter, str string) []string { return Split(str, delimiter) } -// Implode joins array elements with a string . +// Implode joins array elements `pieces` with a string `glue`. // http://php.net/manual/en/function.implode.php func Implode(glue string, pieces []string) string { return strings.Join(pieces, glue) @@ -588,8 +393,8 @@ func Ord(char string) int { return int(char[0]) } -// HideStr replaces part of the the string to by from the . -// It considers parameter as unicode string. +// HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`. +// It considers parameter `str` as unicode string. func HideStr(str string, percent int, hide string) string { array := strings.Split(str, "@") if len(array) > 1 { @@ -619,7 +424,7 @@ func HideStr(str string, percent int, hide string) string { // Nl2Br inserts HTML line breaks(
|
) before all newlines in a string: // \n\r, \r\n, \r, \n. -// It considers parameter as unicode string. +// It considers parameter `str` as unicode string. func Nl2Br(str string, isXhtml ...bool) string { r, n, runes := '\r', '\n', []rune(str) var br []byte @@ -705,9 +510,9 @@ func QuoteMeta(str string, chars ...string) string { return buf.String() } -// SearchArray searches string in string slice
case-sensitively, -// returns its index in . -// If is not found in , it returns -1. +// SearchArray searches string `s` in string slice `a` case-sensitively, +// returns its index in `a`. +// If `s` is not found in `a`, it returns -1. func SearchArray(a []string, s string) int { for i, v := range a { if s == v { @@ -717,7 +522,7 @@ func SearchArray(a []string, s string) int { return NotFoundIndex } -// InArray checks whether string in slice . +// InArray checks whether string `s` in slice `a`. func InArray(a []string, s string) bool { return SearchArray(a, s) != NotFoundIndex } diff --git a/text/gstr/gstr_pos.go b/text/gstr/gstr_pos.go index 6009cd7cb..e279793e6 100644 --- a/text/gstr/gstr_pos.go +++ b/text/gstr/gstr_pos.go @@ -8,8 +8,8 @@ package gstr import "strings" -// Pos returns the position of the first occurrence of -// in from , case-sensitively. +// Pos returns the position of the first occurrence of `needle` +// in `haystack` from , case-sensitively. // It returns -1, if not found. func Pos(haystack, needle string, startOffset ...int) int { length := len(haystack) diff --git a/text/gstr/gstr_replace.go b/text/gstr/gstr_replace.go new file mode 100644 index 000000000..98ffa1957 --- /dev/null +++ b/text/gstr/gstr_replace.go @@ -0,0 +1,89 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +import ( + "github.com/gogf/gf/internal/utils" + "strings" +) + +// Replace returns a copy of the string `origin` +// in which string `search` replaced by `replace` case-sensitively. +func Replace(origin, search, replace string, count ...int) string { + n := -1 + if len(count) > 0 { + n = count[0] + } + return strings.Replace(origin, search, replace, n) +} + +// ReplaceI returns a copy of the string `origin` +// in which string `search` replaced by `replace` case-insensitively. +func ReplaceI(origin, search, replace string, count ...int) string { + n := -1 + if len(count) > 0 { + n = count[0] + } + if n == 0 { + return origin + } + var ( + length = len(search) + searchLower = strings.ToLower(search) + ) + for { + originLower := strings.ToLower(origin) + if pos := strings.Index(originLower, searchLower); pos != -1 { + origin = origin[:pos] + replace + origin[pos+length:] + if n--; n == 0 { + break + } + } else { + break + } + } + return origin +} + +// ReplaceByArray returns a copy of `origin`, +// which is replaced by a slice in order, case-sensitively. +func ReplaceByArray(origin string, array []string) string { + for i := 0; i < len(array); i += 2 { + if i+1 >= len(array) { + break + } + origin = Replace(origin, array[i], array[i+1]) + } + return origin +} + +// ReplaceIByArray returns a copy of `origin`, +// which is replaced by a slice in order, case-insensitively. +func ReplaceIByArray(origin string, array []string) string { + for i := 0; i < len(array); i += 2 { + if i+1 >= len(array) { + break + } + origin = ReplaceI(origin, array[i], array[i+1]) + } + return origin +} + +// ReplaceByMap returns a copy of `origin`, +// which is replaced by a map in unordered way, case-sensitively. +func ReplaceByMap(origin string, replaces map[string]string) string { + return utils.ReplaceByMap(origin, replaces) +} + +// ReplaceIByMap returns a copy of `origin`, +// which is replaced by a map in unordered way, case-insensitively. +func ReplaceIByMap(origin string, replaces map[string]string) string { + for k, v := range replaces { + origin = ReplaceI(origin, k, v) + } + return origin +} diff --git a/text/gstr/gstr_str.go b/text/gstr/gstr_str.go new file mode 100644 index 000000000..8b316efd1 --- /dev/null +++ b/text/gstr/gstr_str.go @@ -0,0 +1,52 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +import "strings" + +// Str returns part of `haystack` string starting from and including +// the first occurrence of `needle` to the end of `haystack`. +// See http://php.net/manual/en/function.strstr.php. +func Str(haystack string, needle string) string { + if needle == "" { + return "" + } + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex { + return "" + } + return haystack[pos+len([]byte(needle))-1:] +} + +// StrEx returns part of `haystack` string starting from and excluding +// the first occurrence of `needle` to the end of `haystack`. +func StrEx(haystack string, needle string) string { + if s := Str(haystack, needle); s != "" { + return s[1:] + } + return "" +} + +// StrTill returns part of `haystack` string ending to and including +// the first occurrence of `needle` from the start of `haystack`. +func StrTill(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos+1] +} + +// StrTillEx returns part of `haystack` string ending to and excluding +// the first occurrence of `needle` from the start of `haystack`. +func StrTillEx(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos] +} diff --git a/text/gstr/gstr_substr.go b/text/gstr/gstr_substr.go new file mode 100644 index 000000000..d4d422c16 --- /dev/null +++ b/text/gstr/gstr_substr.go @@ -0,0 +1,89 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +// SubStr returns a portion of string `str` specified by the `start` and `length` parameters. +// The parameter `length` is optional, it uses the length of `str` in default. +func SubStr(str string, start int, length ...int) (substr string) { + strLength := len(str) + // Simple border checks. + if start < 0 { + start = 0 + } + if start >= strLength { + start = strLength + } + end := strLength + if len(length) > 0 { + end = start + length[0] + if end < start { + end = strLength + } + } + if end > strLength { + end = strLength + } + return str[start:end] +} + +// SubStrRune returns a portion of string `str` specified by the `start` and `length` parameters. +// SubStrRune considers parameter `str` as unicode string. +// The parameter `length` is optional, it uses the length of `str` in default. +func SubStrRune(str string, start int, length ...int) (substr string) { + // Converting to []rune to support unicode. + var ( + runes = []rune(str) + runesLength = len(runes) + ) + + // Simple border checks. + if start < 0 { + start = 0 + } + if start >= runesLength { + start = runesLength + } + end := runesLength + if len(length) > 0 { + end = start + length[0] + if end < start { + end = runesLength + } + } + if end > runesLength { + end = runesLength + } + return string(runes[start:end]) +} + +// StrLimit returns a portion of string `str` specified by `length` parameters, if the length +// of `str` is greater than `length`, then the `suffix` will be appended to the result string. +func StrLimit(str string, length int, suffix ...string) string { + if len(str) < length { + return str + } + suffixStr := defaultSuffixForStrLimit + if len(suffix) > 0 { + suffixStr = suffix[0] + } + return str[0:length] + suffixStr +} + +// StrLimitRune returns a portion of string `str` specified by `length` parameters, if the length +// of `str` is greater than `length`, then the `suffix` will be appended to the result string. +// StrLimitRune considers parameter `str` as unicode string. +func StrLimitRune(str string, length int, suffix ...string) string { + runes := []rune(str) + if len(runes) < length { + return str + } + suffixStr := defaultSuffixForStrLimit + if len(suffix) > 0 { + suffixStr = suffix[0] + } + return string(runes[0:length]) + suffixStr +} diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index e7883e986..a566bf935 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -17,8 +17,8 @@ func Trim(str string, characterMask ...string) string { return utils.Trim(str, characterMask...) } -// TrimStr strips all of the given string from the beginning and end of a string. -// Note that it does not strips the whitespaces of its beginning or end. +// TrimStr strips all the given string from the beginning and end of a string. +// Note that it does not strip the whitespaces of its beginning or end. func TrimStr(str string, cut string, count ...int) string { return TrimLeftStr(TrimRightStr(str, cut, count...), cut, count...) } @@ -32,8 +32,8 @@ func TrimLeft(str string, characterMask ...string) string { return strings.TrimLeft(str, trimChars) } -// TrimLeftStr strips all of the given string from the beginning of a string. -// Note that it does not strips the whitespaces of its beginning. +// TrimLeftStr strips all the given string from the beginning of a string. +// Note that it does not strip the whitespaces of its beginning. func TrimLeftStr(str string, cut string, count ...int) string { var ( lenCut = len(cut) @@ -58,8 +58,8 @@ func TrimRight(str string, characterMask ...string) string { return strings.TrimRight(str, trimChars) } -// TrimRightStr strips all of the given string from the end of a string. -// Note that it does not strips the whitespaces of its end. +// TrimRightStr strips all the given string from the end of a string. +// Note that it does not strip the whitespaces of its end. func TrimRightStr(str string, cut string, count ...int) string { var ( lenStr = len(str) diff --git a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go index b7c0941f5..118022f82 100644 --- a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go +++ b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go @@ -46,7 +46,8 @@ func Test_Struct_UnmarshalValue1(t *testing.T) { gtest.C(t, func(t *gtest.T) { st := &MyTimeSt{} err := gconv.Struct(g.Map{"ServiceDate": nil}, st) - t.AssertNE(err, nil) + t.AssertNil(err) + t.Assert(st.ServiceDate.Time.IsZero(), true) }) gtest.C(t, func(t *gtest.T) { st := &MyTimeSt{} diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index d1d12d46d..9c7ff0713 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -639,6 +639,22 @@ func Test_Struct_Time(t *testing.T) { }) } +func Test_Struct_GTime(t *testing.T) { + // https://github.com/gogf/gf/issues/1387 + gtest.C(t, func(t *gtest.T) { + type User struct { + Name string + CreateTime *gtime.Time + } + var user *User + err := gconv.Struct(`{"Name":"John","CreateTime":""}`, &user) + t.AssertNil(err) + t.AssertNE(user, nil) + t.Assert(user.Name, `John`) + t.Assert(user.CreateTime, nil) + }) +} + // Auto create struct when given pointer. func Test_Struct_Create(t *testing.T) { gtest.C(t, func(t *gtest.T) { diff --git a/util/gmeta/gmeta.go b/util/gmeta/gmeta.go index 4969537c2..c522fcd7b 100644 --- a/util/gmeta/gmeta.go +++ b/util/gmeta/gmeta.go @@ -8,7 +8,6 @@ package gmeta import ( - "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/structs" ) @@ -21,34 +20,27 @@ const ( metaAttributeName = "Meta" ) -var ( - // metaDataCacheMap is a cache map for struct type to enhance the performance. - metaDataCacheMap = gmap.NewStrAnyMap(true) -) - -// Data retrieves and returns all meta data from `object`. +// Data retrieves and returns all metadata from `object`. // It automatically parses and caches the tag string from "Mata" attribute as its meta data. func Data(object interface{}) map[string]interface{} { reflectType, err := structs.StructType(object) if err != nil { panic(err) } - return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} { - if field, ok := reflectType.FieldByName(metaAttributeName); ok { - var ( - tags = structs.ParseTag(string(field.Tag)) - data = make(map[string]interface{}, len(tags)) - ) - for k, v := range tags { - data[k] = v - } - return data + if field, ok := reflectType.FieldByName(metaAttributeName); ok { + var ( + tags = structs.ParseTag(string(field.Tag)) + data = make(map[string]interface{}, len(tags)) + ) + for k, v := range tags { + data[k] = v } - return map[string]interface{}{} - }).(map[string]interface{}) + return data + } + return map[string]interface{}{} } -// Get retrieves and returns specified meta data by `key` from `object`. +// Get retrieves and returns specified metadata by `key` from `object`. func Get(object interface{}, key string) *gvar.Var { return gvar.New(Data(object)[key]) } diff --git a/util/guid/guid_string.go b/util/guid/guid_string.go index 2927b9e62..666d0ec8d 100644 --- a/util/guid/guid_string.go +++ b/util/guid/guid_string.go @@ -17,12 +17,15 @@ import ( "time" ) -var ( - sequence gtype.Uint32 // Sequence for unique purpose of current process. +const ( sequenceMax = uint32(46655) // Sequence max("zzz"). randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes). - macAddrStr = "0000000" // MAC addresses hash result in 7 bytes. - processIdStr = "0000" // Process id in 4 bytes. +) + +var ( + sequence gtype.Uint32 // Sequence for unique purpose of current process. + macAddrStr = "0000000" // MAC addresses hash result in 7 bytes. + processIdStr = "0000" // Process id in 4 bytes. ) // init initializes several fixed local variable. diff --git a/version.go b/version.go index f5adfbbf9..e2d7525e5 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.17.0" +const VERSION = "v1.16.6" const AUTHORS = "john"