#c_sharp #gzip
Пытаюсь разобраться как написать процесс сжатия и разжатия при помощи GZip в многопоточной среде. При разжатии файла в многопоточной среде выдает постоянно следующие ошибки: StackOverFlow, System.OutOfMemory. В режиме отладки на строках: blockLength = BitConverter.ToInt32(lengthBuffer, 4); и _dataSize = BitConverter.ToInt32(compressedData[i], blockLength - 4) во втором потоке присваиваются космические переменные, которые и ведут к фатальным ошибкам. Пробовал ставить 4 байта, всячески "игрался", пытаясь реализовать по-разному. Итог один. Прошу помощи!. Код ниже. public abstract class GZip { protected static bool _cancelled = false; protected static bool _success = false; protected string sourceFile, destinationFile; protected static int _threads = Environment.ProcessorCount; protected const int buffer_size = 1024 * 1024; protected static int blockSize = 10000000; protected static byte[][] lastBuffer = new byte[_threads][]; protected static byte[][] compressedData = new byte[_threads][]; public GZip(string input, string output) { this.sourceFile = input; this.destinationFile = output; } public int CallBackResult() { if (!_cancelled && _success) return 0; return 1; } public void Cancel() { _cancelled = true; } public abstract void Launch(); } class Decompressor : GZip { public Decompressor(string input, string output) : base(input, output) { } public override void Launch() { Console.Write("Decompressing"); try { using (FileStream _compressedFile = new FileStream(sourceFile, FileMode.Open)) { using (FileStream _decompressedFile = new FileStream(sourceFile.Remove(sourceFile.Length - 3), FileMode.Append)) { int blockLength; int _dataSize; byte[] lengthBuffer = new byte[8]; Thread[] tPool = new Thread[_threads]; while (_compressedFile.Position < _compressedFile.Length) { for (int i = 0; (i < _threads) && (_compressedFile.Position < _compressedFile.Length); i++) { Console.Write("."); _compressedFile.Read(lengthBuffer, 0, lengthBuffer.Length); blockLength = BitConverter.ToInt32(lengthBuffer, 4); compressedData[i] = new byte[blockLength]; lengthBuffer.CopyTo(compressedData[i], 0); _compressedFile.Read(compressedData[i], 8, blockLength - 8); _dataSize = BitConverter.ToInt32(compressedData[i], blockLength - 4); lastBuffer[i] = new byte[_dataSize]; tPool[i] = new Thread(Decompress); tPool[i].Start(i); } for (int i = 0; (i < _threads) && (tPool[i] != null);) { if (tPool[i].ThreadState == ThreadState.Stopped) { _decompressedFile.Write(lastBuffer[i], 0, lastBuffer[i].Length); i++; } } } } } } catch (Exception ex) { Console.WriteLine("Error is occured!\n Method: {0}\n Error description {1}", ex.TargetSite, ex.Message); _cancelled = true; } } public static void Decompress(object i) { using (MemoryStream _memoryStream = new MemoryStream(compressedData[(int)i])) { using (GZipStream cs = new GZipStream(_memoryStream, CompressionMode.Decompress)) { cs.Read(lastBuffer[(int)i], 0, lastBuffer[(int)i].Length); } } } } }
Ответы
Ответ 1
Ваш код неявно предполагает, что GZipStream'у можно скормить случайный фрагмент сжатого файла, и он его сможет разжать. Это не так. В сжатом файле есть служебная информация, которую GZipStream не может найти, если ему достаётся лишь кусок файла. Судя по всему, случайные данные воспринимаются кодом как управляющая информация, что и приводит к проблемам. Формат gzip состоит из нескольких, кладущихся впритык кусков. Какие бы ни были размеры каждого куска, архиватор распаковывает куски один за одним. В начале каждого куска есть десятибайтный заголовок, содержащий информацию о куске. Но в этом заголовке нет информации о том, какого размера упакованные данные. За заголовком следует информация, упакованная алгоритмом Deflate, после которой — контрольная сумма и размер распакованных данных. Поэтому склеивать файл из кусков легко, просто можно укладывать их один за другим. А вот для того, чтобы разбить файл на правильные куски, требуется узнать границы куска. Я покамест не вижу, как сделать это, не осуществляя по сути распаковку Deflate. Ваш код по сути берёт случайные границы кусков, поэтому интерпретировать данные на границе куска как длину нельзя.Ответ 2
После долгих мучений я все же разобрался как это реализовать по-человечески. Посмотреть код можно в моем профиле на Git: https://github.com/Mikezar/GZip
Комментариев нет:
Отправить комментарий