add left pack algo
This commit is contained in:
@ -28,6 +28,18 @@ func plan() *cli.Command {
|
|||||||
Aliases: []string{"v"},
|
Aliases: []string{"v"},
|
||||||
Value: false,
|
Value: false,
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "left-pack",
|
||||||
|
Usage: "Use the left-pack algorithm to calculate partitions",
|
||||||
|
Aliases: []string{"l"},
|
||||||
|
Value: false,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "reset",
|
||||||
|
Usage: "Reset the database before planning partitions",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Value: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
|
|
||||||
@ -42,6 +54,13 @@ func plan() *cli.Command {
|
|||||||
panic(fmt.Errorf("error migrating db: %w", err))
|
panic(fmt.Errorf("error migrating db: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Bool("reset") {
|
||||||
|
err = store.RemovePartitionAssignment()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error resetting partitions in the db: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size := int64(4600000000) // size of a single layer DVD
|
size := int64(4600000000) // size of a single layer DVD
|
||||||
targetSize := c.String("targetSize")
|
targetSize := c.String("targetSize")
|
||||||
switch targetSize {
|
switch targetSize {
|
||||||
@ -76,9 +95,20 @@ func plan() *cli.Command {
|
|||||||
fmt.Printf("Target Size: %v\n", humanize.Bytes(uint64(size)))
|
fmt.Printf("Target Size: %v\n", humanize.Bytes(uint64(size)))
|
||||||
fmt.Println("Calculating partitions...")
|
fmt.Println("Calculating partitions...")
|
||||||
|
|
||||||
partitions, err := partitioner.CalculatePartitions(store, size)
|
var partitions [][]types.FileMetadata
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error calculating partitions: %w", err)
|
if !c.Bool("left-pack") {
|
||||||
|
partitions, err = partitioner.CalculatePartitions(store, size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error calculating partitions: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool("left-pack") {
|
||||||
|
partitions, err = partitioner.CalculatePartitionsLeftPack(store, size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error calculating partitions: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, partition := range partitions {
|
for i, partition := range partitions {
|
||||||
|
10
db/db.go
10
db/db.go
@ -19,6 +19,7 @@ type DB interface {
|
|||||||
RemoveFile(fileMetadata types.FileMetadata) error
|
RemoveFile(fileMetadata types.FileMetadata) error
|
||||||
StoreFilePartition(fileMetadata types.FileMetadata) error
|
StoreFilePartition(fileMetadata types.FileMetadata) error
|
||||||
GetTotalSize() (int64, error)
|
GetTotalSize() (int64, error)
|
||||||
|
RemovePartitionAssignment() error
|
||||||
GetFileCount() (int64, error)
|
GetFileCount() (int64, error)
|
||||||
GetFiles() ([]types.FileMetadata, error)
|
GetFiles() ([]types.FileMetadata, error)
|
||||||
}
|
}
|
||||||
@ -120,6 +121,15 @@ func (d *store) StoreFilePartition(fileMetadata types.FileMetadata) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *store) RemovePartitionAssignment() error {
|
||||||
|
query := `UPDATE files SET partitionId = ''`
|
||||||
|
_, err := d.db.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error removing partition assignment | %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *store) GetTotalSize() (int64, error) {
|
func (d *store) GetTotalSize() (int64, error) {
|
||||||
var size int64
|
var size int64
|
||||||
query := `SELECT SUM(size) FROM files`
|
query := `SELECT SUM(size) FROM files`
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"forever-files/db"
|
"forever-files/db"
|
||||||
"forever-files/types"
|
"forever-files/types"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CalculatePartitions(store db.DB, targetSize int64) (partitions [][]types.FileMetadata, err error) {
|
func CalculatePartitions(store db.DB, targetSize int64) (partitions [][]types.FileMetadata, err error) {
|
||||||
@ -57,6 +58,81 @@ func CalculatePartitions(store db.DB, targetSize int64) (partitions [][]types.Fi
|
|||||||
|
|
||||||
// CalculatePartitionsLeftPack calculates the partitions efficiently by searching for files that fit the remaining space in each partition
|
// CalculatePartitionsLeftPack calculates the partitions efficiently by searching for files that fit the remaining space in each partition
|
||||||
func CalculatePartitionsLeftPack(store db.DB, targetSize int64) (partitions [][]types.FileMetadata, err error) {
|
func CalculatePartitionsLeftPack(store db.DB, targetSize int64) (partitions [][]types.FileMetadata, err error) {
|
||||||
// TODO: implement this function
|
totalSize, err := store.GetTotalSize()
|
||||||
return nil, nil
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting total size: %w", err)
|
||||||
|
}
|
||||||
|
if targetSize <= 0 {
|
||||||
|
targetSize = totalSize / 2
|
||||||
|
}
|
||||||
|
fmt.Printf("Total Size: %v\n", totalSize)
|
||||||
|
fmt.Printf("Target Size: %v\n", targetSize)
|
||||||
|
|
||||||
|
files, err := store.GetFiles()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting files: %w", err)
|
||||||
|
}
|
||||||
|
partitions = make([][]types.FileMetadata, 0)
|
||||||
|
partitionSize := int64(0)
|
||||||
|
partitionFiles := make([]types.FileMetadata, 0)
|
||||||
|
overSizedFiles := make([]types.FileMetadata, 0)
|
||||||
|
overSizedSize := int64(0)
|
||||||
|
|
||||||
|
// sort files by size
|
||||||
|
sort.SliceStable(files, func(i, j int) bool {
|
||||||
|
return files[i].Size > files[j].Size
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Size > targetSize {
|
||||||
|
overSizedFiles = append(overSizedFiles, file)
|
||||||
|
overSizedSize += file.Size
|
||||||
|
file.PartitionId = "-1"
|
||||||
|
} else {
|
||||||
|
// you've hit files that are smaller than the target size
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partitionIndex := int64(0)
|
||||||
|
// pick the largest file that fits in the partition's remaining space using sort.Search()
|
||||||
|
// for loop counting down for the size of the files slice
|
||||||
|
for i := len(files) - 1; i >= 0; i-- {
|
||||||
|
index := indexOfLargestFittingFile(files, targetSize-partitionSize)
|
||||||
|
if index == -1 {
|
||||||
|
// no files fit in the partition so move on to the next partition
|
||||||
|
partitions = append(partitions, partitionFiles)
|
||||||
|
partitionFiles = make([]types.FileMetadata, 0)
|
||||||
|
partitionSize = 0
|
||||||
|
partitionIndex++
|
||||||
|
index = indexOfLargestFittingFile(files, targetSize-partitionSize)
|
||||||
|
if index == -1 {
|
||||||
|
// no files fit in the new partition so break out of the loop
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partitionFiles = append(partitionFiles, files[index])
|
||||||
|
partitionSize += files[index].Size
|
||||||
|
files[index].PartitionId = fmt.Sprintf("%d", partitionIndex)
|
||||||
|
// remove the file from the slice
|
||||||
|
files = append(files[:index], files[index+1:]...)
|
||||||
|
}
|
||||||
|
if len(partitionFiles) > 0 {
|
||||||
|
partitions = append(partitions, partitionFiles)
|
||||||
|
}
|
||||||
|
fmt.Printf("Over Sized File Count: %v\n", len(overSizedFiles))
|
||||||
|
fmt.Printf("Total Over Sized Size: %v\n", humanize.Bytes(uint64(overSizedSize)))
|
||||||
|
return partitions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexOfLargestFittingFile(files []types.FileMetadata, remainingSize int64) int {
|
||||||
|
// find the index of the largest file that fits in the remaining space
|
||||||
|
index := sort.Search(len(files), func(i int) bool {
|
||||||
|
return files[i].Size < remainingSize
|
||||||
|
})
|
||||||
|
// if index is == len(files) then there are no files that fit
|
||||||
|
if index == len(files) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return index
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user