Merge branch 'gogf:master' into master

This commit is contained in:
kingeasternsun
2021-09-03 14:39:58 +08:00
committed by GitHub
43 changed files with 871 additions and 538 deletions

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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:

View File

@ -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]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

View File

@ -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 <key>-<value> pair, which is expired after <duration>.
// It does not expire if <duration> == 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 <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <duration>. It does not expire if <duration> == 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 <data>, which is expired after <duration>.
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
//
// It does not expire if <duration> == 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 <key>.
// 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 <key> 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 <key>,
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <duration>.
// 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 <duration> == 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 <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.
// 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 <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.
// 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 <f> 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 <key> 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 <keys> 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 <key>.
// It returns -1 if the <key> 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 <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.
// 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 <key> and returns the old expiration duration value.
// It returns -1 if the <key> 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)
}

View File

@ -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 <key>-<value> pair, which is expired after <duration>.
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 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 <data>, which is expired after <duration>.
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> 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 <key>-<value> pair which is expired after <duration>
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
// cache and it sets <value> 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 <value> can be type of <func() interface{}>, 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 <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> 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 <key>.
// 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 <key>, or sets <key>-<value> pair and
// returns <value> if <key> does not exist in the cache. The key-value pair expires
// after <duration>.
// 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 <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.
// 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 <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>.
// 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 <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.
// 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 <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>.
// 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 <duration> == 0.
// It does nothing if function <f> returns nil.
// It does not expire if `duration` == 0.
// It does nothing if function `f` returns nil.
//
// Note that the function <f> 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 <key> 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 <key> in the cache.
// GetExpire retrieves and returns the expiration of `key` in the cache.
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> 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 <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.
// 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 <key> if given <value> is nil.
// It does nothing if <key> 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 <key> 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 <key> does not exist in the cache.
// It deletes the <key> if <duration> < 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)

View File

@ -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 <key>-<value> pair, which is expired after <duration>.
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 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 <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.
// 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 <key> if given <value> is nil.
// It does nothing if <key> 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 <key> 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 <key> does not exist in the cache.
// It deletes the <key> if <duration> < 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 <key> in the cache.
// GetExpire retrieves and returns the expiration of `key` in the cache.
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> 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 <key>-<value> pair which is expired after <duration>
// if <key> does not exist in the cache. It returns true the <key> dose 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 <func() interface{}>, 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 <func() interface{}>, but it dose nothing if its
// result is nil.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> 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 <data>, which is expired after <duration>.
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> 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 <key>.
// 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 <key>, or sets <key>-<value> pair and
// returns <value> if <key> does not exist in the cache. The key-value pair expires
// after <duration>.
// 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 <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.
// 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 <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>.
// 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 <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.
// 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 <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>.
// 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 <duration> == 0.
// It does nothing if function <f> returns nil.
// It does not expire if `duration` == 0.
// It does nothing if function `f` returns nil.
//
// Note that the function <f> 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 <key> 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 <key>-<value> pair if <key> does not exist in the
// cache, which is expired after <duration>.
// 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 <duration> == 0.
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if the
// It does not expire if `duration` == 0.
// The parameter `value` can be type of <func() interface{}>, but it dose nothing if the
// function result is nil.
//
// It doubly checks the <key> 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 <expire> 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 <expireTimes> and <expireSets> 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 <key>.
// The parameter <force> 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 <expireTimes>.
// Deleting its expire time from `expireTimes`.
c.expireTimes.Delete(key)
// Deleting it from LRU.

View File

@ -23,11 +23,11 @@ func newAdapterMemoryData() *adapterMemoryData {
}
}
// 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.
// 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 <key> if given <value> is nil.
// It does nothing if <key> 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 <key> 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 <key> does not exist in the cache.
// It deletes the <key> if <duration> < 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 <data>, which is expired after <duration>.
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> 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 {

View File

@ -10,7 +10,7 @@ import (
"github.com/gogf/gf/os/gtime"
)
// IsExpired checks whether <item> 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.

View File

@ -43,7 +43,7 @@ func (lru *adapterMemoryLru) Close() {
lru.closed.Set(true)
}
// Remove deletes the <key> FROM <lru>.
// 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 <lru>.
// Size returns the size of `lru`.
func (lru *adapterMemoryLru) Size() int {
return lru.data.Size()
}
// Push pushes <key> to the tail of <lru>.
// 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 <lru>.
// 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 <rawList> to <list> and <data>
// SyncAndClear synchronizes the keys from `rawList` to `list` and `data`
// using Least Recently Used algorithm.
func (lru *adapterMemoryLru) SyncAndClear() {
if lru.closed.Val() {

View File

@ -56,13 +56,13 @@ func (c *Cache) SetAdapter(adapter Adapter) {
c.adapter = adapter
}
// GetVar retrieves and returns the value of <key> 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 <keys> in the cache.
// Removes deletes `keys` in the cache.
// Deprecated, use Remove instead.
func (c *Cache) Removes(keys []interface{}) error {
_, err := c.Remove(keys...)

View File

@ -10,85 +10,85 @@ import (
"time"
)
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 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 <data>, which is expired after <duration>.
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> 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 <key>-<value> pair which is expired after <duration>
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
// cache and it sets <value> 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 <value> can be type of <func() interface{}>, 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 <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> 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 <key>.
// 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 <key>, or sets <key>-<value> pair and
// returns <value> if <key> does not exist in the cache. The key-value pair expires
// after <duration>.
// 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 <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.
// 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 <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>.
// 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 <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.
// 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 <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>.
// 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 <duration> == 0.
// It does nothing if function <f> returns nil.
// It does not expire if `duration` == 0.
// It does nothing if function `f` returns nil.
//
// Note that the function <f> 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 <key> 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 <key> in the cache.
// GetExpire retrieves and returns the expiration of `key` in the cache.
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> 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 <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.
// 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 <key> if given <value> is nil.
// It does nothing if <key> 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 <key> 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 <key> does not exist in the cache.
// It deletes the <key> if <duration> < 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())

View File

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

View File

@ -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 {

View File

@ -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 <path> from cache.
// GetBytesWithCache returns []byte content of given file by <path> from cache.
// If there's no content in the cache, it will read it from disk file specified by <path>.
// The parameter <expire> specifies the caching time for this file content in seconds.
func GetBytesWithCache(path string, duration ...time.Duration) []byte {

View File

@ -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 {

View File

@ -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

View File

@ -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.

View File

@ -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 = ""

View File

@ -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 {

View File

@ -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 <format> 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 <str>.
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])
}

View File

@ -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())

View File

@ -28,95 +28,22 @@ const (
NotFoundIndex = -1
)
// 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)
}
const (
defaultSuffixForStrLimit = "..."
)
// Replace 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
}
// Count counts the number of <substr> appears in <s>.
// It returns 0 if no <substr> found in <s>.
// 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 <substr> appears in <s>, case-insensitively.
// It returns 0 if no <substr> found in <s>.
// 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 <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
}
// 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 <str> specified by the <start> and <length> 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 <str> specified by the <start> and <length> parameters.
// SubStrRune considers parameter <str> 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 <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
}
addStr := "..."
if len(suffix) > 0 {
addStr = suffix[0]
}
return str[0:length] + addStr
}
// 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 {
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 <str>.
// 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.
// <decimals>: Sets the number of decimal points.
// <decPoint>: Sets the separator for the decimal point.
// <thousandsSep>: 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 <body> and <end> 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 <a> and <b>, 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 <str> 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 <str> 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 <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]
}
// Shuffle randomly shuffles a string.
// It considers parameter <str> 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 <str> by a string <delimiter>, 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 <str> by a string <delimiter> 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 <str> by a string <delimiter> 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 <array> to create a single string. The separator string
// <sep> 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 <array> to create a single string. The separator string
// <sep> 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 <array> 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 <str> by a string <delimiter>, 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 <pieces> with a string <glue>.
// 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 <str> to <hide> by <percentage> from the <middle>.
// It considers parameter <str> 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(<br>|<br />) before all newlines in a string:
// \n\r, \r\n, \r, \n.
// It considers parameter <str> 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 <s> in string slice <a> case-sensitively,
// returns its index in <a>.
// If <s> is not found in <a>, 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 <s> in slice <a>.
// InArray checks whether string `s` in slice `a`.
func InArray(a []string, s string) bool {
return SearchArray(a, s) != NotFoundIndex
}

View File

@ -8,8 +8,8 @@ package gstr
import "strings"
// Pos returns the position of the first occurrence of <needle>
// in <haystack> from <startOffset>, case-sensitively.
// Pos returns the position of the first occurrence of `needle`
// in `haystack` from <startOffset>, case-sensitively.
// It returns -1, if not found.
func Pos(haystack, needle string, startOffset ...int) int {
length := len(haystack)

89
text/gstr/gstr_replace.go Normal file
View File

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

52
text/gstr/gstr_str.go Normal file
View File

@ -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]
}

89
text/gstr/gstr_substr.go Normal file
View File

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

View File

@ -17,8 +17,8 @@ func Trim(str string, characterMask ...string) string {
return utils.Trim(str, characterMask...)
}
// TrimStr strips all of the given <cut> 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 <cut> 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 <cut> string from the beginning of a string.
// Note that it does not strips the whitespaces of its beginning.
// TrimLeftStr strips all the given <cut> 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 <cut> string from the end of a string.
// Note that it does not strips the whitespaces of its end.
// TrimRightStr strips all the given <cut> 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)

View File

@ -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{}

View File

@ -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) {

View File

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

View File

@ -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.

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.17.0"
const VERSION = "v1.16.6"
const AUTHORS = "john<john@goframe.org>"