github.com/gogf/gf/v2@v2.7.4/util/gconv/internal/structcache/structcache_cached.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package structcache
     8  
     9  import (
    10  	"reflect"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/gogf/gf/v2/internal/utils"
    15  	"github.com/gogf/gf/v2/os/gtime"
    16  	"github.com/gogf/gf/v2/util/gtag"
    17  )
    18  
    19  // CommonConverter holds some converting functions of common types for internal usage.
    20  type CommonConverter struct {
    21  	Int64   func(any interface{}) int64
    22  	Uint64  func(any interface{}) uint64
    23  	String  func(any interface{}) string
    24  	Float32 func(any interface{}) float32
    25  	Float64 func(any interface{}) float64
    26  	Time    func(any interface{}, format ...string) time.Time
    27  	GTime   func(any interface{}, format ...string) *gtime.Time
    28  	Bytes   func(any interface{}) []byte
    29  	Bool    func(any interface{}) bool
    30  }
    31  
    32  var (
    33  	// map[reflect.Type]*CachedStructInfo
    34  	cachedStructsInfoMap = sync.Map{}
    35  
    36  	// localCommonConverter holds some converting functions of common types for internal usage.
    37  	localCommonConverter CommonConverter
    38  )
    39  
    40  // RegisterCommonConverter registers the CommonConverter for local usage.
    41  func RegisterCommonConverter(commonConverter CommonConverter) {
    42  	localCommonConverter = commonConverter
    43  }
    44  
    45  // GetCachedStructInfo retrieves or parses and returns a cached info for certain struct type.
    46  // The given `structType` should be type of struct.
    47  func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo {
    48  	if structType.Kind() != reflect.Struct {
    49  		return nil
    50  	}
    51  	// check if it has been cached.
    52  	cachedStructInfo, ok := getCachedConvertStructInfo(structType)
    53  	if ok {
    54  		// directly returns the cached struct info if already exists.
    55  		return cachedStructInfo
    56  	}
    57  
    58  	// else create one.
    59  
    60  	// it parses and generates a cache info for given struct type.
    61  	cachedStructInfo = &CachedStructInfo{
    62  		tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo),
    63  	}
    64  	var (
    65  		priorityTagArray []string
    66  		parentIndex      = make([]int, 0)
    67  	)
    68  	if priorityTag != "" {
    69  		priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), gtag.StructTagPriority...)
    70  	} else {
    71  		priorityTagArray = gtag.StructTagPriority
    72  	}
    73  	parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray)
    74  	storeCachedStructInfo(structType, cachedStructInfo)
    75  	return cachedStructInfo
    76  }
    77  
    78  func storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) {
    79  	// Temporarily enabled as an experimental feature
    80  	cachedStructsInfoMap.Store(structType, cachedStructInfo)
    81  }
    82  
    83  func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) {
    84  	// Temporarily enabled as an experimental feature
    85  	v, ok := cachedStructsInfoMap.Load(structType)
    86  	if ok {
    87  		return v.(*CachedStructInfo), ok
    88  	}
    89  	return nil, false
    90  }
    91  
    92  // parseStructToCachedStructInfo parses given struct reflection type and stores its fields info into given CachedStructInfo.
    93  // It stores nothing into CachedStructInfo if given struct reflection type has no fields.
    94  func parseStructToCachedStructInfo(
    95  	structType reflect.Type,
    96  	fieldIndexes []int,
    97  	cachedStructInfo *CachedStructInfo,
    98  	priorityTagArray []string,
    99  ) {
   100  	var (
   101  		fieldName   string
   102  		structField reflect.StructField
   103  		fieldType   reflect.Type
   104  	)
   105  	// TODO:
   106  	//  Check if the structure has already been cached in the cache.
   107  	//  If it has been cached, some information can be reused,
   108  	//  but the [FieldIndex] needs to be reset.
   109  	//  We will not implement it temporarily because it is somewhat complex
   110  	for i := 0; i < structType.NumField(); i++ {
   111  		structField = structType.Field(i)
   112  		fieldType = structField.Type
   113  		fieldName = structField.Name
   114  		// Only do converting to public attributes.
   115  		if !utils.IsLetterUpper(fieldName[0]) {
   116  			continue
   117  		}
   118  
   119  		copyFieldIndexes := make([]int, len(fieldIndexes))
   120  		copy(copyFieldIndexes, fieldIndexes)
   121  
   122  		// normal basic attributes.
   123  		if structField.Anonymous {
   124  			// handle struct attributes, it might be struct/*struct embedded..
   125  			if fieldType.Kind() == reflect.Ptr {
   126  				fieldType = fieldType.Elem()
   127  			}
   128  			if fieldType.Kind() != reflect.Struct {
   129  				continue
   130  			}
   131  			// Skip the embedded structure of the 0 field,
   132  			if fieldType.NumField() == 0 {
   133  				continue
   134  			}
   135  			if structField.Tag != "" {
   136  				// Do not add anonymous structures without tags
   137  				cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray)
   138  			}
   139  			parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray)
   140  			continue
   141  		}
   142  		// Do not directly use append(fieldIndexes, i)
   143  		// When the structure is nested deeply, it may lead to bugs,
   144  		// which are caused by the slice expansion mechanism
   145  		// So it is necessary to allocate a separate index for each field
   146  		// See details https://github.com/gogf/gf/issues/3789
   147  		cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray)
   148  	}
   149  }