Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shallow clone trees to save on memory usage #268

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions pkg/filetree/filetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ func New() *FileTree {
}
}

// Copy returns a Copy of the current FileTree.
// Clone returns a shallow Copy of the current FileTree.
func (t *FileTree) Clone() (ReadWriter, error) {
ct := New()
ct.tree = t.tree.Clone()
return ct, nil
}

// Copy returns a deep Copy of the current FileTree.
func (t *FileTree) Copy() (ReadWriter, error) {
ct := New()
ct.tree = t.tree.Copy()
Expand Down Expand Up @@ -781,7 +788,8 @@ func (t *FileTree) Walk(fn func(path file.Path, f filenode.FileNode) error, cond

// Merge takes the given Tree and combines it with the current Tree, preferring files in the other Tree if there
// are path conflicts. This is the basis function for squashing (where the current Tree is the bottom Tree and the
// given Tree is the top Tree).
// given Tree is the top Tree). Note: existing nodes are not mutated during this operation, they are replaced with
// a node from the other tree.
//
//nolint:gocognit,funlen
func (t *FileTree) Merge(upper Reader) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/filetree/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type PathReader interface {

type Copier interface {
Copy() (ReadWriter, error)
Clone() (ReadWriter, error)
}

type Walker interface {
Expand Down
8 changes: 6 additions & 2 deletions pkg/filetree/union_filetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ func (u *UnionFileTree) Squash() (ReadWriter, error) {
case 0:
return New(), nil
case 1:
return u.trees[0].Copy()
// important: use clone over copy to reduce memory footprint. If callers need a distinct tree they can call
// copy after the fact.
return u.trees[0].Clone()
}

var squashedTree ReadWriter
var err error
for layerIdx, refTree := range u.trees {
if layerIdx == 0 {
squashedTree, err = refTree.Copy()
// important: use clone over copy to reduce memory footprint. If callers need a distinct tree they can call
// copy after the fact.
squashedTree, err = refTree.Clone()
if err != nil {
return nil, err
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/tree/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tree

import (
"fmt"
"maps"

"github.com/anchore/stereoscope/pkg/tree/node"
)
Expand All @@ -22,6 +23,16 @@ func NewTree() *Tree {
}
}

// Clone returns a shallow copy of the Tree.
func (t *Tree) Clone() *Tree {
ct := NewTree()
ct.nodes = maps.Clone(t.nodes)
ct.parent = maps.Clone(t.parent)
ct.children = maps.Clone(t.children)
return ct
}

// Copy returns a deep copy of the Tree.
func (t *Tree) Copy() *Tree {
ct := NewTree()
for k, v := range t.nodes {
Expand Down
Loading