| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | package structr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"unicode" | 
					
						
							|  |  |  | 	"unicode/utf8" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | 	"github.com/zeebo/xxh3" | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // findField will search for a struct field with given set of names, where names is a len > 0 slice of names account for nesting. | 
					
						
							|  |  |  | func findField(t reflect.Type, names []string, allowZero bool) (sfield structfield, ok bool) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		// isExported returns whether name is exported | 
					
						
							|  |  |  | 		// from a package; can be func or struct field. | 
					
						
							|  |  |  | 		isExported = func(name string) bool { | 
					
						
							|  |  |  | 			r, _ := utf8.DecodeRuneInString(name) | 
					
						
							|  |  |  | 			return unicode.IsUpper(r) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// popName pops the next name from | 
					
						
							|  |  |  | 		// the provided slice of field names. | 
					
						
							|  |  |  | 		popName = func() string { | 
					
						
							|  |  |  | 			// Pop next name. | 
					
						
							|  |  |  | 			name := names[0] | 
					
						
							|  |  |  | 			names = names[1:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Ensure valid name. | 
					
						
							|  |  |  | 			if !isExported(name) { | 
					
						
							|  |  |  | 				panicf("field is not exported: %s", name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return name | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// field is the iteratively searched-for | 
					
						
							|  |  |  | 		// struct field value in below loop. | 
					
						
							|  |  |  | 		field reflect.StructField | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for len(names) > 0 { | 
					
						
							|  |  |  | 		// Pop next name. | 
					
						
							|  |  |  | 		name := popName() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Follow any ptrs leading to field. | 
					
						
							|  |  |  | 		for t.Kind() == reflect.Pointer { | 
					
						
							|  |  |  | 			t = t.Elem() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if t.Kind() != reflect.Struct { | 
					
						
							|  |  |  | 			// The end type after following ptrs must be struct. | 
					
						
							|  |  |  | 			panicf("field %s is not struct (ptr): %s", t, name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Look for next field by name. | 
					
						
							|  |  |  | 		field, ok = t.FieldByName(name) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Append next set of indices required to reach field. | 
					
						
							|  |  |  | 		sfield.index = append(sfield.index, field.Index...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Set the next type. | 
					
						
							|  |  |  | 		t = field.Type | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | 	// Get final type hash func. | 
					
						
							|  |  |  | 	sfield.hasher = hasher(t) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // panicf provides a panic with string formatting. | 
					
						
							|  |  |  | func panicf(format string, args ...any) { | 
					
						
							|  |  |  | 	panic(fmt.Sprintf(format, args...)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | // hashPool provides a memory pool of xxh3 | 
					
						
							|  |  |  | // hasher objects used indexing field vals. | 
					
						
							|  |  |  | var hashPool sync.Pool | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | // gethashbuf fetches hasher from memory pool. | 
					
						
							|  |  |  | func getHasher() *xxh3.Hasher { | 
					
						
							|  |  |  | 	v := hashPool.Get() | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	if v == nil { | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | 		v = new(xxh3.Hasher) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | 	return v.(*xxh3.Hasher) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 12:14:10 +00:00
										 |  |  | // putHasher replaces hasher in memory pool. | 
					
						
							|  |  |  | func putHasher(h *xxh3.Hasher) { | 
					
						
							|  |  |  | 	h.Reset() | 
					
						
							|  |  |  | 	hashPool.Put(h) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } |