Private/Test-FileByteIntegrity.ps1

function Test-FileByteIntegrity {
    <#
    .SYNOPSIS
        Performs a partial byte comparison between two files (first + last 8 KB).
    .DESCRIPTION
        Compares file existence, size, and the first/last 8 KB of content.
        Returns a status string indicating the result.
    .PARAMETER SourcePath
        Full path to the source file.
    .PARAMETER DestinationPath
        Full path to the destination file.
    .OUTPUTS
        [string] One of: 'Match', 'MissingOnSource', 'MissingOnDestination', 'SizeMismatch', 'ByteMismatch'.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [string]$SourcePath,

        [Parameter(Mandatory)]
        [string]$DestinationPath
    )

    if (-not [System.IO.File]::Exists($SourcePath)) {
        return 'MissingOnSource'
    }
    if (-not [System.IO.File]::Exists($DestinationPath)) {
        return 'MissingOnDestination'
    }

    $srcInfo = [System.IO.FileInfo]::new($SourcePath)
    $dstInfo = [System.IO.FileInfo]::new($DestinationPath)

    if ($srcInfo.Length -ne $dstInfo.Length) {
        return 'SizeMismatch'
    }

    $chunkSize = 8192
    $fileLen   = $srcInfo.Length

    if ($fileLen -eq 0) {
        return 'Match'
    }

    $srcStream = [System.IO.File]::OpenRead($SourcePath)
    $dstStream = [System.IO.File]::OpenRead($DestinationPath)
    try {
        # Compare first chunk
        $readLen = [math]::Min($chunkSize, $fileLen)
        $srcBuf  = [byte[]]::new($readLen)
        $dstBuf  = [byte[]]::new($readLen)
        $srcStream.Read($srcBuf, 0, $readLen) | Out-Null
        $dstStream.Read($dstBuf, 0, $readLen) | Out-Null

        if (-not [System.Linq.Enumerable]::SequenceEqual($srcBuf, $dstBuf)) {
            return 'ByteMismatch'
        }

        # Compare last chunk (if file is larger than one chunk)
        if ($fileLen -gt $chunkSize) {
            $tailStart = [math]::Max(0, $fileLen - $chunkSize)
            $tailLen   = [int]($fileLen - $tailStart)
            $srcBuf2   = [byte[]]::new($tailLen)
            $dstBuf2   = [byte[]]::new($tailLen)
            $srcStream.Seek($tailStart, [System.IO.SeekOrigin]::Begin) | Out-Null
            $dstStream.Seek($tailStart, [System.IO.SeekOrigin]::Begin) | Out-Null
            $srcStream.Read($srcBuf2, 0, $tailLen) | Out-Null
            $dstStream.Read($dstBuf2, 0, $tailLen) | Out-Null

            if (-not [System.Linq.Enumerable]::SequenceEqual($srcBuf2, $dstBuf2)) {
                return 'ByteMismatch'
            }
        }

        return 'Match'
    }
    finally {
        $srcStream.Dispose()
        $dstStream.Dispose()
    }
}