static int Main(string[] args) { Console.CancelKeyPress += new ConsoleCancelEventHandler(closeAppHandler); ConsoleMesseges.Header(); #region Debugging //args = new string[3]; //Console.WriteLine("Start?"); //Console.ReadLine(); //args[0] = @"compress"; //args[1] = @"Panda.mp4"; //args[2] = @"Minipanda.gzps"; //MultiGzip.Run(args[1], args[2], MultiGzip.Mode.Compress); //args[0] = @"Decompress"; //args[1] = @"MiniPanda.gzps"; //args[2] = @"AgainBigPanda.mp4"; //return MultiGzip.Run(args[1], args[2], MultiGzip.Mode.Decompress); #endregion #region Input try { ConsoleMesseges.Exception(args); } catch (IndexOutOfRangeException) { ConsoleMesseges.Exception(); Environment.Exit(0); } catch (Exception error) { Console.WriteLine("Возникла ошибка!\n Метод: {0}\n Ошибка: {1}", error.TargetSite, error.Message); Environment.Exit(0); } #endregion #region Processin if (args[0].ToLower() == "compress") { return(MultiGzip.Run(args[1], args[2], MultiGzip.Mode.Compress)); } if (args[0].ToLower() == "decompress") { return(MultiGzip.Run(args[1], args[2], MultiGzip.Mode.Decompress)); } #endregion return(1); }
static double parts; //размер разбиения - 1 части public static int Run(string inputFile, string outputfile, Mode compressMode) { bool fileEnd = false; // флаг того что мы полностью считали исходный файл int maxThreads = Environment.ProcessorCount - 2; //Делаем количество "компрессных" потоков равными количиству ядер и -2 на чтение и запись if (maxThreads < 1) { maxThreads = 1; // на 2ух ядерном в любом случае будет меделенно как не крути, просто подстраховка } #region ВЫЧИСЛЯЕМ РАЗМЕР БЛОКОВ double byteLeft = new FileInfo(inputFile).Length; //размер несчитанного файла if (byteLeft > 3.2e10 || byteLeft == 0) { ConsoleMesseges.FileSizeException(); return(1); } // double freeRam = new Microsoft.VisualBasic.Devices.ComputerInfo().AvailablePhysicalMemory; // доступная оперативка if (byteLeft <= freeRam / (maxThreads * 2)) { parts = byteLeft / (maxThreads * 2); //2 - это доп мемори стрим под каждый поток сжатия } else { parts = freeRam / (maxThreads * 2); } if (parts > 2147483646) { parts = 2147483646; // размер блоков: не больше чем позволяент инт 2147483647 байт ~ 2Гб } #region Прогрессбар double totalParts = byteLeft / parts; double preparedParts = 1; var progress = new ProgressBar(); progress.Report(0); Console.WriteLine($"Max Threads: {maxThreads}"); Console.WriteLine($"Total Parts: {(int)totalParts}"); #endregion /*---------------DEBUG---------------------*/ //Console.WriteLine($"Operation = Compress"); //Console.WriteLine($"InputFile = {inputFile}"); //Console.WriteLine($"Outputfile = {outputfile}"); //Console.WriteLine($"Work Threads: {maxThreads}"); //Console.WriteLine($"File size: {byteLeft / 1024 / 1024} Mb"); //Console.WriteLine($"Free Ram: {freeRam / 1024 / 1024} Mb"); //Console.WriteLine($"Block size: {parts} Byte"); //Console.WriteLine($"Block size: {parts / 1024} Kb"); //Console.WriteLine($"Block size: {parts / 1024 / 1024} Mb"); /*---------------DEBUG---------------------*/ #endregion #region ReadThread - поток для чтения исходного файла Thread readThread = new Thread(() => { using (FileStream readStream = new FileStream(inputFile, FileMode.Open)) { if (compressMode == Mode.Compress) { while (byteLeft != 0) { if (threadsBreak) { break; } if (notProcessedParts.Count < maxThreads + 1) //не закидываем в оперативу больше чем нужно тредам, с запасным { byte[] buf = parts < byteLeft ? new byte[(int)parts] : new byte[(int)byteLeft]; readStream.Read(buf, 0, buf.Length); FilePart f = new FilePart(buf); //Console.WriteLine($"Чтение.. {f.partId} size: {f.data.Length} Byte"); notProcessedParts.Enqueue(f); byteLeft -= buf.Length; } } } if (compressMode == Mode.Decompress) { byte[] sizeIdBytes = new byte[4]; long curentSeek = 0; readStream.Seek(-sizeIdBytes.Length, SeekOrigin.End); readStream.Read(sizeIdBytes, 0, sizeIdBytes.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(sizeIdBytes); } parts = BitConverter.ToInt32(sizeIdBytes, 0); // размер разжатой 1ой пачки //Console.WriteLine($"part: {parts}\n"); readStream.Seek(-sizeIdBytes.Length - 4, SeekOrigin.End); readStream.Read(sizeIdBytes, 0, sizeIdBytes.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(sizeIdBytes); } int partsCount = BitConverter.ToInt32(sizeIdBytes, 0); // количество сжатых блоков в файле for (int i = partsCount; i > 0; i--) { if (threadsBreak) { break; } while (notProcessedParts.Count > maxThreads + 1) { Thread.Sleep(5); } readStream.Seek((-8 * i) - 8, SeekOrigin.End); //чтение карты readStream.Read(sizeIdBytes, 0, sizeIdBytes.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(sizeIdBytes); } int id = BitConverter.ToInt32(sizeIdBytes, 0); readStream.Read(sizeIdBytes, 0, sizeIdBytes.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(sizeIdBytes); } int dataLenght = BitConverter.ToInt32(sizeIdBytes, 0); //Console.WriteLine($"part {i} : id = {id}; dat = {dataLenght}"); byte[] buf = new byte[dataLenght]; readStream.Seek(curentSeek, SeekOrigin.Begin); //чтение частей readStream.Read(buf, 0, dataLenght); curentSeek += dataLenght; FilePart f = new FilePart(id, buf); //Console.WriteLine($"Чтение.. {f.partId} size: {f.data.Length} Byte\n"); notProcessedParts.Enqueue(f); } } } fileEnd = true; //файл дочитан }); #endregion #region WriteThread - поток для записи сжатого файла Thread writeThread = new Thread(() => { using (FileStream writeStream = new FileStream(outputfile, FileMode.Create)) { if (compressMode == Mode.Compress) { byte[] partOfMap = new byte[4]; Queue <byte[]> sizeAndId = new Queue <byte[]>(); //сохраняем в нее размер сжатых пачек и их айди для дальнейшей записи в "карту" файла. while (true) { if (threadsBreak) { break; } if (fileEnd && notProcessedParts.Count == 0 && processedParts.Count == 0 && workerThreads == 0) { break; } if (processedParts.Count != 0) { if (processedParts.TryDequeue(out FilePart f)) { partOfMap = BitConverter.GetBytes(f.partId); if (BitConverter.IsLittleEndian) { Array.Reverse(partOfMap); // это id } sizeAndId.Enqueue(partOfMap); partOfMap = BitConverter.GetBytes(f.data.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(partOfMap); // потом сжатый размер } sizeAndId.Enqueue(partOfMap); writeStream.Write(f.data, 0, f.data.Length); //Console.WriteLine($"Запись.. {f.partId}"); //DEBUG f.Dispose(); progress.Report(preparedParts / totalParts); //прогрессбар preparedParts++; } } } partOfMap = BitConverter.GetBytes(sizeAndId.Count / 2); // предпоследние 4 байта будет количество всех сжатых 4х байтных пачек содержащих id if (BitConverter.IsLittleEndian) { Array.Reverse(partOfMap); } //Console.WriteLine($"Vsego id and size: {sizeAndId.Count}"); //DEBUG sizeAndId.Enqueue(partOfMap); partOfMap = BitConverter.GetBytes((int)parts); // последние 4 байта размер 1ой распакованной части if (BitConverter.IsLittleEndian) { Array.Reverse(partOfMap); } sizeAndId.Enqueue(partOfMap); while (sizeAndId.Count != 0) { byte[] a = sizeAndId.Peek(); writeStream.Write(sizeAndId.Dequeue(), 0, 4); } } if (compressMode == Mode.Decompress) { while (true) { if ((fileEnd && notProcessedParts.Count == 0 && processedParts.Count == 0 && workerThreads == 0) || threadsBreak) { break; } if (processedParts.Count != 0) { if (processedParts.TryDequeue(out FilePart f)) { writeStream.Seek((int)parts * (f.partId - 1), SeekOrigin.Begin); writeStream.Write(f.data, 0, f.data.Length); //Console.WriteLine($"Запись.. {f.partId}"); f.Dispose(); progress.Report(preparedParts / totalParts); //прогрессбар preparedParts++; } } } } threadsDone.Set(); } }); #endregion readThread.Start(); writeThread.Start(); #region Workthreads for (int i = 0; i < maxThreads; i++) { Thread workThread = new Thread(() => { lock (workerThreadsLocker) workerThreads++; while (true) { if (threadsBreak) { break; } if (fileEnd && notProcessedParts.Count == 0) { break; } if (notProcessedParts.Count != 0) { if (notProcessedParts.TryDequeue(out FilePart f)) { //Console.WriteLine($"Сжатие - {f.partId}"); //DEBUG if (compressMode == Mode.Compress) { Compressing <FilePart>(f); } else if (compressMode == Mode.Decompress) { Decompressing <FilePart>(f); } } } } lock (workerThreadsLocker) workerThreads--; }); workThread.Start(); } #endregion if (threadsBreak) { progress.Dispose(); return(1); } else { threadsDone.WaitOne(); progress.Dispose(); if (compressMode == Mode.Compress) { Console.WriteLine("Compressing Done!"); } if (compressMode == Mode.Decompress) { Console.WriteLine("Decompressing Done!"); } } return(0); }