@@ -195,11 +195,6 @@ type GenericParameter struct {
195195 // it will cache the final street value to avoid parsing the path again.
196196 cache map [string ]reflect.Value
197197
198- // pathSegments caches the split path segments to avoid repeated strings.Split operations.
199- // For example, "user.address.street" will be cached as ["user", "address", "street"].
200- // This optimization reduces CPU overhead when the same parameter paths are accessed repeatedly.
201- pathSegments map [string ][]string
202-
203198 // structFieldIndex caches the field indexes for struct types at each path level.
204199 // The first key is the position in the path (e.g., for "user.address.street": 0 for user, 1 for address).
205200 // The second key is the concrete type of the struct, which ensures correct field lookup for different struct types.
@@ -215,67 +210,68 @@ func (g *GenericParameter) get(name string) (reflect.Value, bool) {
215210 value = g .Value
216211 )
217212
218- // get or split the path segments
219- // to avoid repeated strings.Split operations
220- // cache the split segments
221- segments , exists := g .pathSegments [name ]
222- if ! exists {
223- segments = strings .Split (name , "." )
224- if g .pathSegments == nil {
225- g .pathSegments = make (map [string ][]string )
226- }
227- g .pathSegments [name ] = segments
228- }
229-
230- for i , item := range segments {
231-
232- // only unwrap when the value need to call Get method
233- value = reflectlite .Unwrap (value )
234-
235- // match the value type
236- // only map, struct, slice and array can be wrapped as parameter
237- switch value .Kind () {
238- case reflect .Map :
239- // if the map key is not a string type, then return false
240- if value .Type ().Key ().Kind () != reflect .String {
241- return reflect.Value {}, false
242- }
243- param = mapParameter {Value : value }
244- case reflect .Struct :
245- // Initialize the three-level cache if not exists:
246- // Level 1: path position -> to handle different levels in the path (e.g., user.address.street)
247- // Level 2: concrete type -> to handle different struct types at the same position
248- // Level 3: field name -> to cache the actual field indexes
249- if g .structFieldIndex == nil {
250- g .structFieldIndex = make (map [int ]map [reflect.Type ]map [string ][]int )
251- }
252-
253- // Cache the type to avoid multiple calls to Type()
254- valueType := value .Type ()
213+ // Iterate through the name character by character to avoid strings.Split allocation
214+ start := 0
215+ i := 0 // path segment index for structFieldIndex cache
216+ for j := 0 ; j <= len (name ); j ++ {
217+ if j == len (name ) || name [j ] == '.' {
218+ // Extract the segment between start and j
219+ if j > start { // avoid empty segments
220+ item := name [start :j ]
221+
222+ // only unwrap when the value need to call Get method
223+ value = reflectlite .Unwrap (value )
224+
225+ // match the value type
226+ // only map, struct, slice and array can be wrapped as parameter
227+ switch value .Kind () {
228+ case reflect .Map :
229+ // if the map key is not a string type, then return false
230+ if value .Type ().Key ().Kind () != reflect .String {
231+ return reflect.Value {}, false
232+ }
233+ param = mapParameter {Value : value }
234+ case reflect .Struct :
235+ // Initialize the three-level cache if not exists:
236+ // Level 1: path position -> to handle different levels in the path (e.g., user.address.street)
237+ // Level 2: concrete type -> to handle different struct types at the same position
238+ // Level 3: field name -> to cache the actual field indexes
239+ if g .structFieldIndex == nil {
240+ g .structFieldIndex = make (map [int ]map [reflect.Type ]map [string ][]int )
241+ }
242+
243+ // Cache the type to avoid multiple calls to Type()
244+ valueType := value .Type ()
245+
246+ // Get or create the type-level cache for current path position
247+ structFieldIndex , in := g .structFieldIndex [i ]
248+ if ! in {
249+ // Initialize with the current type to avoid another map lookup
250+ structFieldIndex = map [reflect.Type ]map [string ][]int {
251+ valueType : {},
252+ }
253+ g .structFieldIndex [i ] = structFieldIndex
254+ }
255+
256+ // Create a new structParameter with its field cache pointing to
257+ // the cached indexes for its specific type, ensuring different
258+ // struct types don't share the same field index cache
259+ param = & structParameter {Value : value , fieldIndexes : structFieldIndex [valueType ]}
260+ case reflect .Slice , reflect .Array :
261+ param = sliceParameter {Value : value }
262+ default :
263+ // otherwise, return false
264+ return reflect.Value {}, false
265+ }
255266
256- // Get or create the type-level cache for current path position
257- structFieldIndex , in := g .structFieldIndex [i ]
258- if ! in {
259- // Initialize with the current type to avoid another map lookup
260- structFieldIndex = map [reflect.Type ]map [string ][]int {
261- valueType : {},
267+ var exists bool
268+ value , exists = param .Get (item )
269+ if ! exists {
270+ return reflect.Value {}, false
262271 }
263- g . structFieldIndex [ i ] = structFieldIndex
272+ i ++
264273 }
265-
266- // Create a new structParameter with its field cache pointing to
267- // the cached indexes for its specific type, ensuring different
268- // struct types don't share the same field index cache
269- param = & structParameter {Value : value , fieldIndexes : structFieldIndex [valueType ]}
270- case reflect .Slice , reflect .Array :
271- param = sliceParameter {Value : value }
272- default :
273- // otherwise, return false
274- return reflect.Value {}, false
275- }
276- value , exists = param .Get (item )
277- if ! exists {
278- return reflect.Value {}, false
274+ start = j + 1
279275 }
280276 }
281277 return value , true
@@ -351,21 +347,31 @@ type prefixPatternParameter struct {
351347}
352348
353349func (p prefixPatternParameter ) Get (name string ) (value reflect.Value , exists bool ) {
354- items := strings .Split (name , "." )
355- prefix := items [0 ]
350+ // Find the first dot to extract the prefix
351+ dotIdx := strings .IndexByte (name , '.' )
352+
353+ var prefix string
354+ if dotIdx == - 1 {
355+ // No dot found, the entire name is the prefix
356+ prefix = name
357+ } else {
358+ prefix = name [:dotIdx ]
359+ }
356360
357361 if p .prefix != prefix {
358362 return reflect.Value {}, false
359363 }
360364
361- if len (items ) == 1 {
365+ if dotIdx == - 1 {
366+ // Only prefix, return the param itself
362367 return reflect .ValueOf (p .param ), true
363368 }
364369
365370 if p .parameter == nil {
366371 p .parameter = NewGenericParam (p .param , "" )
367372 }
368- return p .parameter .Get (strings .Join (items [1 :], "." ))
373+ // Pass the remaining part after the prefix (skip the dot)
374+ return p .parameter .Get (name [dotIdx + 1 :])
369375}
370376
371377// PrefixPatternParameter is a parameter that supports prefix pattern.
0 commit comments