Added a stack overflow safe recursive variation of FindLastChildNode

Old method deprecated.

Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
Dave Shanley
2023-04-30 07:58:46 -04:00
parent dcfb480095
commit 8549b1547a
3 changed files with 937 additions and 893 deletions

View File

@@ -321,12 +321,10 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
break break
case reflect.Float64: case reflect.Float64:
precision := -1 precision := -1
if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") { if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") {
precision = len(strings.Split(fmt.Sprint(entry.StringValue), ".")[1]) precision = len(strings.Split(fmt.Sprint(entry.StringValue), ".")[1])
} }
val := strconv.FormatFloat(value.(float64), 'f', precision, 64) val := strconv.FormatFloat(value.(float64), 'f', precision, 64)
valueNode = utils.CreateFloatNode(val) valueNode = utils.CreateFloatNode(val)
valueNode.Line = line valueNode.Line = line

View File

@@ -49,6 +49,9 @@ func FindNodes(yamlData []byte, jsonPath string) ([]*yaml.Node, error) {
return results, nil return results, nil
} }
// FindLastChildNode will find the last node in a tree, based on a starting node.
// Deprecated: This function is deprecated, use FindLastChildNodeWithLevel instead.
// this has the potential to cause a stack overflow, so use with caution. It will be removed later.
func FindLastChildNode(node *yaml.Node) *yaml.Node { func FindLastChildNode(node *yaml.Node) *yaml.Node {
s := len(node.Content) - 1 s := len(node.Content) - 1
if s < 0 { if s < 0 {
@@ -64,6 +67,27 @@ func FindLastChildNode(node *yaml.Node) *yaml.Node {
} }
} }
// FindLastChildNodeWithLevel will find the last node in a tree, based on a starting node.
// Will stop searching after 100 levels, because that's just silly, we probably have a loop.
func FindLastChildNodeWithLevel(node *yaml.Node, level int) *yaml.Node {
if level > 100 {
return node // we've gone too far, give up.
}
s := len(node.Content) - 1
if s < 0 {
s = 0
}
if len(node.Content) > 0 && len(node.Content[s].Content) > 0 {
level++
return FindLastChildNodeWithLevel(node.Content[s], level)
} else {
if len(node.Content) > 0 {
return node.Content[s]
}
return node
}
}
// BuildPath will construct a JSONPath from a base and an array of strings. // BuildPath will construct a JSONPath from a base and an array of strings.
func BuildPath(basePath string, segs []string) string { func BuildPath(basePath string, segs []string) string {
@@ -608,6 +632,3 @@ func CheckEnumForDuplicates(seq []*yaml.Node) []*yaml.Node {
} }
return res return res
} }

View File

@@ -58,11 +58,15 @@ func TestFindLastChildNode(t *testing.T) {
nodes, _ := FindNodes(getPetstore(), "$.info") nodes, _ := FindNodes(getPetstore(), "$.info")
lastNode := FindLastChildNode(nodes[0]) lastNode := FindLastChildNode(nodes[0])
assert.Equal(t, "1.0.11", lastNode.Value) // should be the version. assert.Equal(t, "1.0.11", lastNode.Value) // should be the version.
lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0)
assert.Equal(t, lastNode, lastNodeDouble)
} }
func TestFindLastChildNode_WithKids(t *testing.T) { func TestFindLastChildNode_WithKids(t *testing.T) {
nodes, _ := FindNodes(getPetstore(), "$.paths./pet") nodes, _ := FindNodes(getPetstore(), "$.paths./pet")
lastNode := FindLastChildNode(nodes[0]) lastNode := FindLastChildNode(nodes[0])
lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0)
assert.Equal(t, lastNode, lastNodeDouble)
assert.Equal(t, "read:pets", lastNode.Value) assert.Equal(t, "read:pets", lastNode.Value)
} }
@@ -72,6 +76,27 @@ func TestFindLastChildNode_NotFound(t *testing.T) {
} }
lastNode := FindLastChildNode(node) lastNode := FindLastChildNode(node)
assert.Equal(t, "same", lastNode.Value) // should be the same node assert.Equal(t, "same", lastNode.Value) // should be the same node
lastNodeDouble := FindLastChildNodeWithLevel(node, 0)
assert.Equal(t, lastNode, lastNodeDouble)
}
func genLoop(count int) *yaml.Node {
if count > 200 {
return nil
}
count++
return &yaml.Node{
Value: "same",
Content: []*yaml.Node{
genLoop(count),
},
}
}
func TestFindLastChildNode_TooDeep(t *testing.T) {
node := genLoop(0)
lastNodeDouble := FindLastChildNodeWithLevel(node, 0)
assert.NotNil(t, lastNodeDouble)
} }
func TestBuildPath(t *testing.T) { func TestBuildPath(t *testing.T) {