🔷 (File Transferring Utility) Transfer files through the Net 🔷
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

106 lines
2.7 KiB

package checksum
import (
"crypto/sha256"
"fmt"
"io"
"os"
)
const CHECKSUMLEN uint = 32
type CheckSum [CHECKSUMLEN]byte
// returns a checksum of given file. NOTE, that it creates checksum
// not of a full file (from all file bytes), but from separate byte blocks.
// This is done as an optimisation because the file can be very large in size.
// The general idea:
// BOF... CHUNK -> STEP -> CHUNK... EOF
// checksum := sha256.Sum256(ALLCHUNKS)
// GetPartialCheckSum is default method used to get a file checksum by sender and receiver
func GetPartialCheckSum(file *os.File) (CheckSum, error) {
// "capturing" CHUNKSIZE bytes and then skipping STEP bytes before the next chunk until the last one
const CHUNKS uint = 100
const CHUNKSIZE uint = 100
const STEP uint = 250
fileStats, err := file.Stat()
if err != nil {
return [CHECKSUMLEN]byte{}, fmt.Errorf("could not get the stats: %s", err)
}
fileSize := fileStats.Size()
if fileSize < int64(CHUNKS*CHUNKSIZE+STEP*(CHUNKS-1)) {
// file is too small to chop it in chunks, so just doing full checksum
checksum, err := getFullCheckSum(file)
if err != nil {
return [CHECKSUMLEN]byte{}, err
}
return checksum, nil
}
var capturedChunks string
var read uint64 = 0
for i := 0; uint(i) < CHUNKS; i++ {
buffer := make([]byte, CHUNKSIZE)
r, _ := file.ReadAt(buffer, int64(read))
capturedChunks += string(buffer)
read += uint64(r)
read += uint64(STEP)
}
checksum := sha256.Sum256([]byte(capturedChunks))
return checksum, nil
}
// Returns a sha256 checksum of given file
func getFullCheckSum(file *os.File) (CheckSum, error) {
filebytes, err := io.ReadAll(file)
if err != nil {
return [CHECKSUMLEN]byte{}, fmt.Errorf("could not read the file: %s", err)
}
checksum := sha256.Sum256(filebytes)
return checksum, nil
}
// Simply compares 2 given checksums. If they are equal - returns true
func AreEqual(checksum1, checksum2 CheckSum) bool {
var i int = 0
for _, checksum1Byte := range checksum1 {
checksum2Byte := checksum2[i]
if checksum1Byte != checksum2Byte {
return false
}
i++
}
return true
}
// Tries to convert given bytes into CheckSum type
func BytesToChecksum(bytes []byte) (CheckSum, error) {
if uint(len(bytes)) > CHECKSUMLEN {
return CheckSum{}, fmt.Errorf("provided bytes` length is bigger than the checksum`s")
} else if uint(len(bytes)) < CHECKSUMLEN {
return CheckSum{}, fmt.Errorf("provided bytes` length is smaller than needed")
}
var checksum [CHECKSUMLEN]byte
for index, b := range bytes {
checksum[index] = b
}
return CheckSum(checksum), nil
}
// Converts given checksum into []byte
func ChecksumToBytes(checksum CheckSum) []byte {
var checksumBytes []byte
for _, b := range checksum {
checksumBytes = append(checksumBytes, b)
}
return checksumBytes
}