mirror of
				https://github.com/packwiz/packwiz.git
				synced 2025-11-04 12:04:31 +01:00 
			
		
		
		
	Fix O(n^2) path lookup using a binary search
This commit is contained in:
		@@ -3,6 +3,7 @@ package core
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"os"
 | 
			
		||||
@@ -79,33 +80,45 @@ func (in *Index) resortIndex() {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (in *Index) markFound(i int, format, hash string) {
 | 
			
		||||
	// Update hash
 | 
			
		||||
	in.Files[i].Hash = hash
 | 
			
		||||
	if in.HashFormat == format {
 | 
			
		||||
		in.Files[i].HashFormat = ""
 | 
			
		||||
	} else {
 | 
			
		||||
		in.Files[i].HashFormat = format
 | 
			
		||||
	}
 | 
			
		||||
	// Mark this file as found
 | 
			
		||||
	in.Files[i].fileExistsTemp = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (in *Index) updateFileHashGiven(path, format, hash string, mod bool) error {
 | 
			
		||||
	// Find in index
 | 
			
		||||
	found := false
 | 
			
		||||
	relPath, err := filepath.Rel(filepath.Dir(in.indexFile), path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	slashPath := filepath.ToSlash(relPath)
 | 
			
		||||
	// TODO: make this not a linear scan for every file update
 | 
			
		||||
	for k, v := range in.Files {
 | 
			
		||||
		if v.File == slashPath {
 | 
			
		||||
			found = true
 | 
			
		||||
			// Update hash
 | 
			
		||||
			in.Files[k].Hash = hash
 | 
			
		||||
			if in.HashFormat == format {
 | 
			
		||||
				in.Files[k].HashFormat = ""
 | 
			
		||||
			} else {
 | 
			
		||||
				in.Files[k].HashFormat = format
 | 
			
		||||
			}
 | 
			
		||||
			// Mark this file as found
 | 
			
		||||
			in.Files[k].fileExistsTemp = true
 | 
			
		||||
			in.Files[k].File = slashPath
 | 
			
		||||
			// Don't break out of loop, as there may be aliased versions that
 | 
			
		||||
			// also need to be updated
 | 
			
		||||
 | 
			
		||||
	// Binary search for slashPath in the files list
 | 
			
		||||
	i, found := slices.BinarySearchFunc(in.Files, IndexFile{File: slashPath}, func(a IndexFile, b IndexFile) int {
 | 
			
		||||
		return strings.Compare(a.File, b.File)
 | 
			
		||||
	})
 | 
			
		||||
	if found {
 | 
			
		||||
		in.markFound(i, format, hash)
 | 
			
		||||
		// There may be other entries with the same file path but different alias!
 | 
			
		||||
		// Search back and forth to find them:
 | 
			
		||||
		j := i
 | 
			
		||||
		for j > 0 && in.Files[j-1].File == slashPath {
 | 
			
		||||
			j = j - 1
 | 
			
		||||
			in.markFound(j, format, hash)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		j = i
 | 
			
		||||
		for j < len(in.Files)-1 && in.Files[j+1].File == slashPath {
 | 
			
		||||
			j = j + 1
 | 
			
		||||
			in.markFound(j, format, hash)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		newFile := IndexFile{
 | 
			
		||||
			File:           slashPath,
 | 
			
		||||
			Hash:           hash,
 | 
			
		||||
@@ -271,6 +284,8 @@ func (in *Index) Refresh() error {
 | 
			
		||||
	for i := range in.Files {
 | 
			
		||||
		in.Files[i].File = path.Clean(in.Files[i].File)
 | 
			
		||||
	}
 | 
			
		||||
	// Resort index (required by updateFile)
 | 
			
		||||
	in.resortIndex()
 | 
			
		||||
 | 
			
		||||
	for _, v := range fileList {
 | 
			
		||||
		start := time.Now()
 | 
			
		||||
@@ -303,6 +318,8 @@ func (in *Index) Refresh() error {
 | 
			
		||||
 | 
			
		||||
// RefreshFile calculates the hash for a given path and updates it in the index (also sorts the index)
 | 
			
		||||
func (in *Index) RefreshFile(path string) error {
 | 
			
		||||
	// Resort index first (required by updateFile)
 | 
			
		||||
	in.resortIndex()
 | 
			
		||||
	err := in.updateFile(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -335,6 +352,8 @@ func (in *Index) RefreshFileWithHash(path, format, hash string, mod bool) error
 | 
			
		||||
	if viper.GetBool("no-internal-hashes") {
 | 
			
		||||
		hash = ""
 | 
			
		||||
	}
 | 
			
		||||
	// Resort index first (required by updateFile)
 | 
			
		||||
	in.resortIndex()
 | 
			
		||||
	err := in.updateFileHashGiven(path, format, hash, mod)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user