public ConsumerThreadParams(
     string threadName,
     MemoryBlocksManager <byte[]> releasedMemoryBlocks,
     MemoryBlocksManager <ReadyToGetMemoryBlock> readyToGetMemoryBlocks,
     string hashAlgorithmName,
     IBufferedResultWriter resultWriter,
     ManualResetEventSlim exitByFileEndingEvent,
     ManualResetEventSlim exitByErrorEvent,
     Action <Exception> errorNotifierMethod)
 {
     ThreadName               = threadName;
     ReleasedMemoryBlocks     = releasedMemoryBlocks;
     ReadyToGetMemoryBlocks   = readyToGetMemoryBlocks;
     HashAlgorithmName        = hashAlgorithmName;
     ResultWriter             = resultWriter;
     ExitByFileEndingEvent    = exitByFileEndingEvent;
     ExitByErrorEvent         = exitByErrorEvent;
     NotifyProducerAboutError = errorNotifierMethod;
 }
        /// <inheritdoc/>
        public void SplitFileAndCalculateHashes(Stream fileStream, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter)
        {
            var bytesLeft = fileStream.Length;

            byte[] buffer        = new byte[blockSize];
            var    chunkIndex    = 1;
            var    numberOfBytes = 0;

            using var hashAlgorithm = HashAlgorithm.Create(hashAlgorithmName);

            while (true)
            {
                // Это позволяет нам не создавать слишком большой массив буффера, если файл сам по себе меньше размера блока
                // А так же, если мы находимся в последнем блоке, часть массива будет занята нулями,
                // чтобы не расчитывать хэш для нулевой части, укажем явно границы массива
                if (bytesLeft < blockSize)
                {
                    blockSize = (int)bytesLeft;
                }

                numberOfBytes = fileStream.Read(buffer, 0, blockSize);
                if (numberOfBytes == 0)
                {
                    break;
                }

                var hashBytes = hashAlgorithm.ComputeHash(buffer, 0, blockSize);
                resultWriter.Write(chunkIndex, hashBytes);

                bytesLeft = fileStream.Length - fileStream.Position;
                chunkIndex++;
            }
        }
 /// <inheritdoc/>
 public void SplitFileAndCalculateHashes(string path, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter)
 {
     using var fileStream = File.OpenRead(path);
     SplitFileAndCalculateHashes(fileStream, blockSize, hashAlgorithmName, resultWriter);
 }
Beispiel #4
0
        public void SplitFileAndCalculateHashes(Stream fileStream, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter)
        {
            // Это позволяет нам не создавать слишком большой массив буффера, если файл сам по себе меньше размера блока
            blockSize = CalculateBlockSize(fileStream.Length, blockSize);

            // Расчитываем, сколько мы можем создать блоков в памяти
            var amountOfBlocks = CalculateAmountOfBlocks(blockSize);

            // Счетчик потоков поможет нам правильно завершить работу
            var allThreadsAreCompletedEvent = new AutoResetEvent(false);

            ThreadCounter.Initialize(allThreadsAreCompletedEvent);

            // Эти события будут доставлять информацию в другие потоки
            var closeThreadsByFileEndingEvent = new ManualResetEventSlim(false);
            var closeThreadsByErrorEvent      = new ManualResetEventSlim(false);

            // Алгоритм работает на основе двух очередей: readyToGetMemoryBlocks (доступные для хэширования) и releasedMemoryBlocks (доступные для перезаписи)
            // Прочитанный блок файла записывается в уже созданный буфер, затем отправляется в очередь readyToGetMemoryBlocks.
            // Оттуда его подхватывает первый попавшийся consumer-поток, делает свои дела и затем ссылку на буфер отправляет в releasedMemoryBlocks, таким образом сообщая, что буфер доступен для следующего блока
            // Из этой очереди producer-поток достает свободный буфер, записывает в него блок файла и опять отправляет в очередь readyToGetMemoryBlocks
            var memoryBlockIsReleasedEvent = new ManualResetEventSlim(true);
            var releasedMemoryBlocks       = new MemoryBlocksManager <byte[]>(amountOfBlocks, memoryBlockIsReleasedEvent, closeThreadsByFileEndingEvent, closeThreadsByErrorEvent);

            var memoryBlockIsReadyToGetEvent = new ManualResetEventSlim(true);
            var readyToGetMemoryBlocks       = new MemoryBlocksManager <ReadyToGetMemoryBlock>(amountOfBlocks, memoryBlockIsReadyToGetEvent, closeThreadsByFileEndingEvent, closeThreadsByErrorEvent);

            var chunkIndex    = 1;
            var numberOfBytes = 1;

            byte[] currentBuffer;

            try
            {
                // Первичное чтение файла, ограниченное количеством блоков amountOfBlocks
                // Здесь мы создаем буферы, создаем потоки и уже начинаем хэшировать файл
                for (; chunkIndex <= amountOfBlocks && !closeThreadsByErrorEvent.IsSet; chunkIndex++)
                {
                    currentBuffer = new byte[blockSize];

                    numberOfBytes = fileStream.Read(currentBuffer, 0, blockSize);
                    if (numberOfBytes == 0)
                    {
                        SuccessfullExit();
                        return;
                    }

                    if (blockSize > numberOfBytes)
                    {
                        currentBuffer = currentBuffer[0..numberOfBytes];