add zip and parity stuff

This commit is contained in:
2023-07-18 18:21:12 -06:00
parent 8abb1408f0
commit f260ee1c9f
11 changed files with 403 additions and 21 deletions

23
fileUtilities/hash.go Normal file
View File

@ -0,0 +1,23 @@
package fileUtilities
import (
"crypto/sha256"
"fmt"
"io"
"os"
)
func HashFile(filePath string) ([]byte, error) {
file, err := os.Open(filePath)
if err != nil {
return []byte{}, fmt.Errorf("error opening file for hashing: %w", err)
}
defer file.Close()
h := sha256.New()
if _, err := io.Copy(h, file); err != nil {
return []byte{}, fmt.Errorf("error hashing file: %w", err)
}
return h.Sum(nil), nil
}

55
fileUtilities/parity.go Normal file
View File

@ -0,0 +1,55 @@
package fileUtilities
import (
"fmt"
"io"
)
func parityStream(in1, in2 io.Reader, out io.Writer) error {
var err error
byteSize := 1024
done1 := false
done2 := false
for !done1 && !done2 {
// get bytes from in1 and in2 and write the parity to buf
// if either in1 or in2 is done, write the remaining bytes from the other to buf
in1Bytes := make([]byte, byteSize)
in2Bytes := make([]byte, byteSize)
read1 := 0
read2 := 0
if !done1 {
read1, err = in1.Read(in1Bytes)
if err != nil {
if err == io.EOF {
done1 = true
} else {
return err
}
}
}
if !done2 {
read2, err = in2.Read(in2Bytes)
if err != nil {
if err == io.EOF {
done2 = true
} else {
return err
}
}
}
maxRead := read1
if read2 > maxRead {
maxRead = read2
}
parityBytes := make([]byte, maxRead)
for i := 0; i < maxRead; i++ {
parityBytes[i] = in1Bytes[i] ^ in2Bytes[i]
}
_, err := out.Write(parityBytes)
if err != nil {
return fmt.Errorf("error writing to buffer: %w", err)
}
}
return nil
}

View File

@ -0,0 +1,70 @@
package fileUtilities
import (
"bytes"
"io"
"testing"
)
func Test_createParityFile(t *testing.T) {
type args struct {
in1 io.Reader
in2 io.Reader
}
tests := []struct {
name string
args args
wantOut string
wantErr bool
}{
{
name: "create parity file",
args: args{
in1: bytes.NewBuffer([]byte{0, 1, 2, 3, 4, 5, 6, 7}),
in2: bytes.NewBuffer([]byte{7, 6, 5, 4, 3, 2, 1, 0}),
},
wantOut: string([]byte{7, 7, 7, 7, 7, 7, 7, 7}),
wantErr: false,
},
{
name: "in1 is longer than in2",
args: args{
in1: bytes.NewBuffer([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8}),
in2: bytes.NewBuffer([]byte{7, 6, 5, 4, 3, 2, 1, 0}),
},
wantOut: string([]byte{7, 7, 7, 7, 7, 7, 7, 7, 8}),
wantErr: false,
},
{
name: "in2 is longer than in1",
args: args{
in1: bytes.NewBuffer([]byte{0, 1, 2, 3, 4, 5, 6, 7}),
in2: bytes.NewBuffer([]byte{7, 6, 5, 4, 3, 2, 1, 0, 54}),
},
wantOut: string([]byte{7, 7, 7, 7, 7, 7, 7, 7, 54}),
wantErr: false,
},
{
name: "parity recreates original file",
args: args{
in1: bytes.NewBuffer([]byte{0, 1, 2, 3, 4, 5, 6, 7}),
in2: bytes.NewBuffer([]byte{7, 7, 7, 7, 7, 7, 7, 7, 54}),
},
wantOut: string([]byte{7, 6, 5, 4, 3, 2, 1, 0, 54}),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out := &bytes.Buffer{}
err := parityStream(tt.args.in1, tt.args.in2, out)
if (err != nil) != tt.wantErr {
t.Errorf("parityStream() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotOut := out.String(); gotOut != tt.wantOut {
t.Errorf("parityStream() gotOut = %v, want %v", gotOut, tt.wantOut)
}
})
}
}

86
fileUtilities/zip.go Normal file
View File

@ -0,0 +1,86 @@
package fileUtilities
import (
"archive/zip"
"fmt"
"forever-files/types"
"io"
"os"
"path"
"strings"
)
func CreateZip(fileName, outDir, baseDir string, partition []types.FileMetadata) error {
// Create a buffer to write our archive to.
outFile := path.Join(outDir, fileName+".zip")
zipFile, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("error opening/creating zip file: %w", err)
}
// Create a new zip archive.
w := zip.NewWriter(zipFile)
files := prepFiles(baseDir, partition)
for _, file := range files {
zf, err := w.Create(file.Name)
if err != nil {
return fmt.Errorf("error creating zip file: %w", err)
}
f, err := os.Open(file.Path)
if err != nil {
fmt.Println(fmt.Sprintf("error opening file: %v", err))
fmt.Println(fmt.Sprintf("skipping file: %v", file.Path))
continue
}
if _, err := io.Copy(zf, f); err != nil {
return fmt.Errorf("error copying file to zip file: %w", err)
}
err = f.Close()
if err != nil {
return fmt.Errorf("error closing file: %w", err)
}
}
// Make sure to check the error on Close.
err = w.Close()
if err != nil {
return fmt.Errorf("error closing zip file: %w", err)
}
err = zipFile.Close()
if err != nil {
return fmt.Errorf("error closing zip file: %w", err)
}
return nil
}
func prepFiles(baseDir string, partition []types.FileMetadata) []struct {
Name, Path string
} {
var files []struct {
Name, Path string
}
for _, file := range partition {
filePath := path.Join(file.Path, file.Name)
// from zip.Create documentation:
// ...The name must be a relative path: it must not start with a
// drive letter (e.g. C:) or leading slash, and only forward slashes
// are allowed...
fileName := strings.Replace(replaceBackslashes(strings.Replace(filePath, baseDir, "", 1)), "/", "", 1)
files = append(files, struct {
Name, Path string
}{
Name: fileName,
Path: filePath,
})
}
return files
}
func replaceBackslashes(input string) string {
return strings.ReplaceAll(input, "\\", "/")
}

36
fileUtilities/zip_test.go Normal file
View File

@ -0,0 +1,36 @@
package fileUtilities
import "testing"
func Test_replaceBackslashes(t *testing.T) {
type args struct {
input string
}
tests := []struct {
name string
args args
want string
}{
{
name: "replace backslashes",
args: args{
input: "C:\\Users\\james\\Documents\\test",
},
want: "C:/Users/james/Documents/test",
},
{
name: "no backslashes",
args: args{
input: "C:/Users/james/Documents/test",
},
want: "C:/Users/james/Documents/test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := replaceBackslashes(tt.args.input); got != tt.want {
t.Errorf("replaceBackslashes() = %v, want %v", got, tt.want)
}
})
}
}