path

package
v0.5.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2022 License: MIT Imports: 4 Imported by: 1

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.

func (Path) String

func (p Path) String() string

String returns Path represented as a string.

func (Path) Value

func (p Path) Value(v reflect.Value) reflect.Value

Value returns the reflect.Value for the path from the origin value.

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.

func (Paths) Len

func (p Paths) Len() int

Len returns the length of Paths.

func (Paths) Less

func (p Paths) Less(a, b int) bool

Less returns true if the value at a is less than b.

func (Paths) Swap

func (p Paths) Swap(a, b int)

Swap swaps element a with element b.

type ReflectPath

type ReflectPath struct {
	HasPointer bool
	Index      []int
	Last       int
}

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

func (t Tree) Slice() []Path

Slice returns all members of Leaves and Branches combined into a sorted slice.

func (Tree) String

func (t Tree) String() string

String returns the Tree represented as a string.

func (Tree) StringIndent

func (t Tree) StringIndent(indent string) string

String returns the Tree represented as a string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL