/// <summary> /// Deserializes a compressed data object from the given file stream. /// </summary> /// <param name="inputStream">input file stream</param> /// <returns>object with a compressed byte[] and corresponding index</returns> public static IndexedDataObject Deserialize(FileStream inputStream) { IFormatter formatter = new BinaryFormatter(); IndexedDataObject compressedDataObject = (IndexedDataObject)formatter.Deserialize(inputStream); return(compressedDataObject); }
/// <summary> /// Compresses data inside the input indexedDataObject. /// </summary> /// <param name="dataObjectForCompression">input data object with byte[] and corresponding index</param> /// <returns>data object with compressed byte[] and corresponding index</returns> protected override IndexedDataObject ProcessData(IndexedDataObject dataObjectForCompression) { byte[] processedBlock = Compression.Compress(dataObjectForCompression.DataBlock); dataObjectForCompression.DataBlock = processedBlock; IndexedDataObject compressedDataObject = dataObjectForCompression; return(compressedDataObject); }
/// <summary> /// Reads (deserializes) indexed data objects wth compressed byte arrays from an input file and inserts them to the queue. /// </summary> /// <param name="inputFileStream">input file</param> protected override void ReadFromFileAndEnqueue(FileStream inputFileStream) { while (inputFileStream.Position != inputFileStream.Length) { IndexedDataObject compressedDataObject = Deserialization.Deserialize(inputFileStream); EnqueueObject(compressedDataObject); // put the data block into the queue } }
/// <summary> /// puts the indexed data object into a queue /// </summary> /// <param name="data"></param> protected void EnqueueObject(IndexedDataObject data) { lock (lockQueue) { while (queue.Count >= Settings.maxQueueSize) { Monitor.Wait(lockQueue); } queue.Enqueue(data); if (queue.Count == 1) { // wake up any blocked dequeue, i.e. the consumer threads Monitor.PulseAll(lockQueue); } } }
/// <summary> /// Reads data blocks (as byte[]) from an input file, assigns a unique number to each block (so that compressed data can later be saved to a file in the correct order) /// and inserts a new indexed data object (containing the input data block and the block number) to the queue. /// </summary> /// <param name="inputFileStream">input file</param> protected override void ReadFromFileAndEnqueue(FileStream inputFileStream) { byte[] block = new byte[Settings.blockSize]; int bytesRead; uint i = 1; while ((bytesRead = (inputFileStream).Read(block, 0, Settings.blockSize)) > 0) // read "blockSize" of bytes to a "block" (byte[]) { if (bytesRead < block.Length) { byte[] block2 = new byte[bytesRead]; Array.Copy(block, block2, bytesRead); block = block2; } IndexedDataObject indexedDataObject = new IndexedDataObject(block, i); EnqueueObject(indexedDataObject); // put the data block into the queue block = new byte[Settings.blockSize]; i++; } }
/// <summary> /// Writes compressed data from the given data object to a file (using object serialization). /// </summary> /// <param name="outputDataObject">data object to be written (serialized) into the output file</param> /// <param name="outputFileStream">output file</param> protected override void WriteToFile(IndexedDataObject outputDataObject, FileStream outputFileStream) { Serialization.Serialize(outputDataObject, outputFileStream); }
/// <summary> /// Writes decompressed data from the given indexed data object to the output file (in correct order based on the data objects's index number) as byte[]. /// </summary> /// <param name="outputDataObject">data object with byte[] to be written into the output file</param> /// <param name="outputFileStream">output file</param> protected override void WriteToFile(IndexedDataObject outputDataObject, FileStream outputFileStream) { outputFileStream.Write(outputDataObject.DataBlock, 0, outputDataObject.DataBlock.Length); }
/// <summary> /// Serializes a compressed data object to the given file stream. /// </summary> /// <param name="compressedDataObject">object with a compressed byte[] and corresponding index</param> /// <param name="outputStream">output file stream</param> public static void Serialize(IndexedDataObject compressedDataObject, FileStream outputStream) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(outputStream, compressedDataObject); }
/// <summary> /// Takes data objects from a queue, processes them (compression/decompression) and writes them to the output file. /// </summary> /// <param name="outputFileStreamObject">output file stream</param> public void ConsumeData(object outputFileStreamObject) //takes data from a queue { try { FileStream outputFileStream = (FileStream)outputFileStreamObject; while (true) // keeps the consumer thread running { IndexedDataObject dataElementFromQueue = null; lock (lockQueue) { if (queue.Count > 0) // queue not empty { dataElementFromQueue = queue.Dequeue(); Monitor.PulseAll(lockQueue); // data removed from a queue => wake up the producer to put more data into the queue if (dataElementFromQueue == null) // null object in queue means end of file reached, nothing more to read { ProducerConsumer.lastBlockRead = true; // last block in the queue (there are no more bytes to read from the input file) return; // Producer ended, all objects from the queue have been already processed => this thread ends } } else { if (ProducerConsumer.lastBlockRead) { return; // Producer ended, all objects from the queue have been already processed (or are processed by other threads) => this thread ends } Monitor.Wait(lockQueue); } } // data object was read from the queue => we can release the lock and enable access to the queue again to the producer or other consumer threads if (dataElementFromQueue != null) { IndexedDataObject processedData = ProcessData(dataElementFromQueue); lock (lockOutputFile) { // thread waits for previous data blocks to be written to the file before it can write its own data block to the file while ((processedData.Index - 1) != ProducerConsumer.GetProcessedBlocksCount()) { Monitor.Wait(lockOutputFile); } WriteToFile(processedData, outputFileStream); ProducerConsumer.IncrementProcessedBlocksCount(); Monitor.PulseAll(lockOutputFile); } } } } catch (DirectoryNotFoundException) { ErrorsChecker.SetError(new DirectoryNotFoundException("Error message: The output directory you're looking for doesn't exist.")); } catch (DriveNotFoundException) { ErrorsChecker.SetError(new DriveNotFoundException("Error message: The output drive you're looking for doesn't exist.")); } catch (PathTooLongException) { ErrorsChecker.SetError(new PathTooLongException("Error message: The output path is longer or fully qualified file name is longer than the system-defined maximum length.")); } catch (UnauthorizedAccessException) { ErrorsChecker.SetError(new UnauthorizedAccessException("Error message: Access to the output directory was denied.")); } catch (OutOfMemoryException) { ErrorsChecker.SetError(new OutOfMemoryException("Error message: There is not enough memory to continue the execution of the program.")); } catch (Exception e) { Console.WriteLine(e.Message); ErrorsChecker.SetError(new Exception("Error message: An error occured during the execution of the program.")); } }
/// <summary> /// Writes the processed data into the output file. /// </summary> /// <param name="outputDataObject">data object to be written in the output file</param> /// <param name="outputFileStream">output file</param> protected abstract void WriteToFile(IndexedDataObject outputDataObject, FileStream outputFileStream);
/// <summary> /// Retrieves an indexed data object from the queue nad processes it (compresses or decompresses the data). /// </summary> /// <param name="indexedDataObject"></param> /// <returns>data object with compressed or decompressed byte[] and corresponding index</returns> protected abstract IndexedDataObject ProcessData(IndexedDataObject indexedDataObject);