Функция хэш-функции Opensubtitles не работает для больших файлов

Я использую функцию ниже для вычисления хэша opensubtitles.org для файлов фильмов. Он работает в основном, но с большими файлами я получаю следующую ошибку.

Я действительно не понимаю, потому что всегда должны быть доступные данные.

Может ли кто-нибудь указать мне в правильном направлении?

PHP Предупреждение: unpack(): Тип v: недостаточно ввода, нужно 2, 0 file.php в строке 169

function OpenSubtitlesHash($file)
{
    $handle = fopen($file, "rb");
    $fsize = filesize($file);

    $hash = array(3 => 0, 
                  2 => 0, 
                  1 => ($fsize >> 16) & 0xFFFF, 
                  0 => $fsize & 0xFFFF);

    for ($i = 0; $i < 8192; $i++)
    {
        $tmp = ReadUINT64($handle);
        $hash = AddUINT64($hash, $tmp);
    }

    $offset = $fsize - 65536;
    fseek($handle, $offset > 0 ? $offset : 0, SEEK_SET);

    for ($i = 0; $i < 8192; $i++)
    {
        $tmp = ReadUINT64($handle);
        $hash = AddUINT64($hash, $tmp);         
    }

    fclose($handle);
        return UINT64FormatHex($hash);
}

function ReadUINT64($handle)
{
    $u = unpack("va/vb/vc/vd", fread($handle, 8));
    return array(0 => $u["a"], 1 => $u["b"], 2 => $u["c"], 3 => $u["d"]);
}

function AddUINT64($a, $b)
{
    $o = array(0 => 0, 1 => 0, 2 => 0, 3 => 0);

    $carry = 0;
    for ($i = 0; $i < 4; $i++) 
    {
        if (($a[$i] + $b[$i] + $carry) > 0xffff ) 
        {
            $o[$i] += ($a[$i] + $b[$i] + $carry) & 0xffff;
            $carry = 1;
        }
        else 
        {
            $o[$i] += ($a[$i] + $b[$i] + $carry);
            $carry = 0;
        }
    }

    return $o;   
}

function UINT64FormatHex($n)
{   
    return sprintf("%04x%04x%04x%04x", $n[3], $n[2], $n[1], $n[0]);
}

Ответ 1

Если вы предоставили некоторую дополнительную информацию: версию системы, версию php, размер больших файлов, тип файлов (простые файлы, URL-адреса и т.д.) - это даст больше информации для точного ответа.

Основное предположение, что вы находитесь на 32-битной системе и имеете проблемы с filsize с файлами более 2 ГБ. Из документов:

Примечание. Поскольку тип целочисленного типа PHP подписан и многие платформы используют 32-битные целые числа, некоторые функции файловой системы могут возвращать неожиданные результаты для файлов размером более 2 ГБ.

Вероятно, вы получите неправильное значение filesize и, следовательно, не можете точно прочитать байты с байтами. Этот комментарий объясняет, как получить размер больших файлов, а также отмечает, что fseek использует int внутренне, поэтому вы не можете поместить указатель после 2GB порог. Вам нужно будет fread в эту позицию.

Можно проверить другую гипотезу:

  • fread может читать больше данных, чем запрашивается при определенных циклах:

    если поток считывается с буферизацией и не представляет собой обычный файл, производится не более одного считывания до нескольких байтов, равных размеру блока (обычно 8192); в зависимости от ранее буферизованных данных размер возвращаемых данных может быть больше размера блока.

  • stat кеш не позволяет получить точное значение размера файла;

Ответ 2

Вы никогда не проверяете, имеет ли ваш дескриптор $какой-либо resoure, когда ваш дескриптор $имеет значение null или false, вы получите ту же ошибку

PHP Warning: unpack(): Type v: not enough input, need 2, have 0 in file.php on line 169

Итак, добавьте чек, прежде чем что-то сделать с помощью $handle

if(!is_null($handle)){
  // Do something..
}

Ответ 3

Вам не нужно и не следует вычислять общий размер файла. Если размер файла превышает PHP_INT_MAX, тогда результат будет неточным,

Лучшим решением является использование fseek() в конце файла:

fseek($handle, -65536, SEEK_END);