Private/Get-Crc32.ps1

# CRC32 lookup table (generated from polynomial 0xEDB88320)
$script:Crc32Table = $null

function Initialize-Crc32Table {
    if ($null -ne $script:Crc32Table) { return }

    $script:Crc32Table = New-Object uint32[] 256
    # 0xEDB88320 = 3988292384 in decimal (standard CRC32 polynomial)
    $polynomial = 3988292384

    for ($i = 0; $i -lt 256; $i++) {
        $crc = [uint32]$i
        for ($j = 0; $j -lt 8; $j++) {
            if (($crc -band 1) -eq 1) {
                $crc = [uint32](($crc -shr 1) -bxor $polynomial)
            }
            else {
                $crc = [uint32]($crc -shr 1)
            }
        }
        $script:Crc32Table[$i] = $crc
    }
}

function Get-Crc32 {
    <#
    .SYNOPSIS
    Calculate CRC32 hash of a file or portion of a file.

    .DESCRIPTION
    Native PowerShell CRC32 implementation using the standard IEEE polynomial.
    Supports offset and length for validating chunks of multi-volume archives.

    .PARAMETER FilePath
    Path to the file to hash

    .PARAMETER Offset
    Optional: Start reading from this byte offset

    .PARAMETER Length
    Optional: Only hash N bytes from the offset

    .OUTPUTS
    [uint32] CRC32 value
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$FilePath,

        [Parameter()]
        [long]$Offset = 0,

        [Parameter()]
        [long]$Length = -1
    )

    Initialize-Crc32Table

    $fs = [System.IO.File]::OpenRead($FilePath)
    try {
        if ($Offset -gt 0) {
            $fs.Seek($Offset, [System.IO.SeekOrigin]::Begin) | Out-Null
        }

        # 0xFFFFFFFF = 4294967295 in decimal
        $crc = [uint32]4294967295
        $buffer = New-Object byte[] 65536
        $totalRead = [long]0

        while (($bytesRead = $fs.Read($buffer, 0, $buffer.Length)) -gt 0) {
            $processCount = $bytesRead

            if ($Length -gt 0) {
                $remaining = $Length - $totalRead
                if ($remaining -le 0) { break }
                if ($processCount -gt $remaining) {
                    $processCount = [int]$remaining
                }
            }

            for ($i = 0; $i -lt $processCount; $i++) {
                $tableIndex = [int](($crc -bxor $buffer[$i]) -band 255)
                $crc = [uint32](($crc -shr 8) -bxor $script:Crc32Table[$tableIndex])
            }

            $totalRead += $processCount

            if ($Length -gt 0 -and $totalRead -ge $Length) { break }
        }

        return [uint32]($crc -bxor 4294967295)
    }
    finally {
        $fs.Close()
    }
}