Documentation ¶
Overview ¶
Package path provides more granular information about paths or pathways from a top-level root struct through its descendents to leaf fields.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Path ¶
type Path struct { // Name is the field's name. Name string // Index is the field's index into its owning struct. Index int // Offset is the field's uintptr offset into its owning struct. Offset uintptr // Type is the field's reflect.Type. Type reflect.Type // PathwayIndex represents the pathway when traversed by field index. // // The inner slice represents field indexes when calling reflect.Value.Field(n). // The outer slice represents breaks in the indexes where a pointer occurs. // // len(PathwayIndex)=1 means there are no pointers in this pathway and // reflect.Value.FieldByIndex(PathwayIndex[0]) can reach the field directly. PathwayIndex [][]int // PathwayOffsets represents the pathway when traversed by pointer arithmetic. // // Each PathOffsetSegment describes the memory offset from the struct's beginning // memory, followed by the number of pointer-indirections required to reach the // final type. // // len(PathwayOffsets)=1 means there are pointers and a single pointer addition // from the beginning of the struct's memory will yield the field. PathwayOffsets []PathOffsetSegment // The full pathway name with nested structs or fields joined by a DOT or PERIOD. PathwayName string // ParentPathwayName is the pathway name of the parent. ParentPathwayName string }
A Path represents a traversal from an originating struct to an accessible public field within the struct or any of its descendents, where the descendents are limited to being structs, pointer-to-struct, or pointer chains that end in struct.
Paths are either contiguous or segmented. A contiguous path is one where the origin and destination occupy a single unbroken region of memory. A segmented path is one where the origin and destination may be in separate regions of memory; segmented paths are caused when intermediary fields along the path are pointers.
A struct B struct C struct M, N
The Paths A ↣ M (start at A and traverse to M) or A ↣ N are contiguous because no intermediary field between the origin and destination is a pointer. In terms of pointer arithmetic M or N can be reached by adding their total offset to A's memory address.
A struct B struct *C struct M, N
The Paths A ↣ B and A ↣ C are still contiguous because no section of the path is segmented by a pointer. Note that even though C itself is a pointer it can be reached by adding its total offset to A's memory address.
However the paths A ↣ M and A ↣ N are no longer contiguous. In order to start at A and reach either M or N we must first traverse to C, perform pointer dereferencing (and possibly allocate a C), and then from &(*C) we can reach M or N by adding their offsets to C's address.
func (Path) ReflectPath ¶
func (p Path) ReflectPath() ReflectPath
ReflectPath returns a condensed representation of Path purpose-built to navigate the Path via reflect.
Path contains a moderately excessive amount of information. Discarding it in favor of ReflectPath can lower the memory requirements of packages needing this information and functionality.
type PathIndeces ¶
type PathIndeces [][]int
PathIndeces is a [][]int, where the inner []int would be appropriate for passing to reflect.Value.FieldByIndex([]int) to select a nested field.
A caveat of reflect.Value.FieldByIndex() is that it doesn't automatically instantiate intermediate fields that may be pointers. Any field that is a pointer effectively breaks or segments the original []int into a pair of []int with one on each side of the pointer. As a result this package stores the PathIndeces as [][]int.
If len(PathIndeces)==1 then the pathway is contiguous as described in the documentation for PathOffsetSegment and a single call of reflect.Value.FieldByIndex(Indeces[0]) is enough to obtain the field.
func (PathIndeces) String ¶
func (i PathIndeces) String() string
String returns the string description.
type PathOffsetSegment ¶
type PathOffsetSegment struct { // Offset is the total offset from the memory address of the // struct at the top of the struct hierarchy for this specific // segment. // // Address // A struct 0xabab # Top // B struct 0xabab + Offset(B) # A is top. // C struct 0xabab + Offset(C) # A is top. // // A and its members are contiguous regions of memory. All Offsets // are calculated from A's memory address. // // Address // X struct 0xeded # Top // *Y struct 0xeded + Offset(Y) -> 0xeeee # A is top; pointer begins new hierarchy at Y. // M 0xeeee + Offset(M) # Y is top. // N 0xeeee + Offset(N) # Y is top. // // X's hierarchy is segmented by the pointer at *Y. Therefore Y's Offset // is calculated from X's memory address **but** the Offsets for // M and N are added to Y's address to obtain their locations. Offset uintptr // IndirectionLevel determines if the field at Offset is a pointer or not. // // IndirectionLevel=0 means field is not a pointer. // IndirectionLevel=1 means field is a pointer with a single level of indirection. // IndirectionLevel>2 means field is a pointer with multiple levels of indirection. // // IndirectionLevel // A struct 0 *(&A + Offset) -> is not a pointer // B struct 0 *(&A + Offset) -> is not a pointer // C struct 0 *(&A + Offset) -> is not a pointer. // // X struct 0 *(&A + Offset) -> is not a pointer. // *Y struct 1 *(&A + Offset) -> is *Y, pointer with single level of indirection. // **Z struct 2 *(&A + Offset) -> is **Z, pointer with 2 levels of indirection. IndirectionLevel int // Type and EndType describe the type(s) located at Offset. // // IndirectionLevel=0 means Type and EndType are the same type. // IndirectionLevel>0 means Type is the pointer's type and EndType // is the type at the end of the pointer chain. // // Type EndType // A struct A A // B struct B B // C struct C C // // X struct X X // *Y struct *Y Y // **Z struct **Z Z Type reflect.Type EndType reflect.Type }
PathOffsetSegment describes a segment of a Path when traversed via pointer arithmetic.
func (PathOffsetSegment) String ¶
func (s PathOffsetSegment) String() string
String returns the string description.
type PathOffsets ¶
type PathOffsets []PathOffsetSegment
PathOffsets is a slice of PathOffsetSegment.
func (PathOffsets) String ¶
func (o PathOffsets) String() string
String returns the string description.
type Paths ¶
type Paths []Path
Paths is a slice of Path.
type ReflectPath ¶
ReflectPath contains the bare minimum information to traverse a path from an origin to the value described by Index+Last.
If a full index is []int{1,2,3,4} then it is stored in this type as
Index = []int{1,2,3} Last = 4 // See the source code for Value for the reasoning behind this decision.
Example ¶
package main import ( "fmt" "reflect" "github.com/nofeaturesonlybugs/set/path" ) func main() { type Other struct { Message string } type Foo struct { Num int Str string M Other } f := Foo{} var rp path.ReflectPath tree := path.Stat(Foo{}) v := reflect.Indirect(reflect.ValueOf(&f)) rp = tree.Leaves["Str"].ReflectPath() rp.Value(v).SetString("Blue") rp = tree.Leaves["Num"].ReflectPath() rp.Value(v).SetInt(42) rp = tree.Leaves["M.Message"].ReflectPath() rp.Value(v).SetString("hut hut") fmt.Println(f.Str, f.Num, f.M.Message) }
Output: Blue 42 hut hut
Example (Pointer) ¶
package main import ( "fmt" "reflect" "github.com/nofeaturesonlybugs/set/path" ) func main() { type Foo struct { Num int Str string } type Ptr struct { P *Foo } f := Ptr{} var rp path.ReflectPath tree := path.Stat(f) v := reflect.Indirect(reflect.ValueOf(&f)) rp = tree.Leaves["P.Str"].ReflectPath() rp.Value(v).SetString("Blue") rp = tree.Leaves["P.Num"].ReflectPath() rp.Value(v).SetInt(42) fmt.Println(f.P.Str, f.P.Num) }
Output: Blue 42
func (ReflectPath) Value ¶
func (p ReflectPath) Value(v reflect.Value) reflect.Value
Value accepts an originating struct value and traverses Index+Last to reach a leaf field.
v should be a struct whose type is equal to the type used when creating the original Path from which this ReflectPath was derived.
For performance critical code consider manually inlining this implementation.
type Tree ¶
type Tree struct { // Leaves are struct fields that have no children. Leaves map[string]Path // Branches are struct fields that have children. Branches map[string]Path }
Tree is the full mapping of a struct.
Example ¶
package main import ( "fmt" "time" "github.com/nofeaturesonlybugs/set/path" ) func main() { // The following fields become leaves: // - T // - Str // - Int // - X // - Y // // There is only one branch: // - A (embedded in Foo) type A struct { T time.Time Str string Int int } type Foo struct { X float64 Y float64 A } tree := path.Stat(Foo{}) fmt.Println(tree.StringIndent(" ")) }
Output: Branches A = A 2 16 Type=path_test.A Pathway[A][2] Parent[] Offsets= +16 ∴ path_test.A Leaves X = X 0 0 Type=float64 Pathway[X][0] Parent[] Offsets= +0 ∴ float64 Y = Y 1 8 Type=float64 Pathway[Y][1] Parent[] Offsets= +8 ∴ float64 A.T = T 0 0 Type=time.Time Pathway[A.T][2 0] Parent[A] Offsets= +16 ∴ time.Time A.Str = Str 1 24 Type=string Pathway[A.Str][2 1] Parent[A] Offsets= +40 ∴ string A.Int = Int 2 40 Type=int Pathway[A.Int][2 2] Parent[A] Offsets= +56 ∴ int
func Stat ¶
func Stat(v interface{}) Tree
Stat inspects the incoming value to build a Tree which consists of two sets of Paths -- branches and leaves.
The incoming value must be a struct, ptr-to-struct, or ptr-chain-to-struct.
Leaves are final fields in the struct that can be traversed no further. If a field is a struct with no exported fields then it is a leaf.
Branches are fields that are structs or embedded structs that can be traversed deeper.
func (Tree) StringIndent ¶
String returns the Tree represented as a string.