From 83e82e03ab4dc5314d7a14291189ec75e16c688a Mon Sep 17 00:00:00 2001 From: Petrutiu Mihai Date: Mon, 18 Jul 2016 15:45:28 +0300 Subject: [PATCH] Add compressed employee example --- .../ByteArrayCompressionUtility.cs | 64 +++++++++++++ .../CaretakerListCompressed.cs | 92 +++++++++++++++++++ .../CaretakerObjectCompressed.cs | 31 +++++++ .../CompressedEmployee/Employee.cs | 46 ++++++++++ .../EmployeeCompressedExample.cs | 85 +++++++++++++++++ .../CompressedEmployee/ICaretaker.cs | 14 +++ src/MememntoPattern/MementoPatternExamples.cs | 12 ++- src/MememntoPattern/project.json | 1 + 8 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 src/MememntoPattern/CompressedEmployee/ByteArrayCompressionUtility.cs create mode 100644 src/MememntoPattern/CompressedEmployee/CaretakerListCompressed.cs create mode 100644 src/MememntoPattern/CompressedEmployee/CaretakerObjectCompressed.cs create mode 100644 src/MememntoPattern/CompressedEmployee/Employee.cs create mode 100644 src/MememntoPattern/CompressedEmployee/EmployeeCompressedExample.cs create mode 100644 src/MememntoPattern/CompressedEmployee/ICaretaker.cs diff --git a/src/MememntoPattern/CompressedEmployee/ByteArrayCompressionUtility.cs b/src/MememntoPattern/CompressedEmployee/ByteArrayCompressionUtility.cs new file mode 100644 index 0000000..007119c --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/ByteArrayCompressionUtility.cs @@ -0,0 +1,64 @@ +using ProtoBuf; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading.Tasks; + +namespace MememntoPattern.CompressedEmployee +{ + public static class ByteArrayCompressionUtility + { + public static byte[] Compress(byte[] inputData) + { + if (inputData == null) + throw new ArgumentNullException("inputData must be non-null"); + + using (var compressIntoMs = new MemoryStream()) + { + using (var gzs = new BufferedStream(new GZipStream(compressIntoMs, CompressionMode.Compress))) + { + gzs.Write(inputData, 0, inputData.Length); + } + return compressIntoMs.ToArray(); + } + } + + public static byte[] Decompress(byte[] inputData) + { + if (inputData == null) + throw new ArgumentNullException("inputData must be non-null"); + + using (var compressedMs = new MemoryStream(inputData)) + { + using (var decompressedMs = new MemoryStream()) + { + using (var gzs = new BufferedStream(new GZipStream(compressedMs, CompressionMode.Decompress))) + { + gzs.CopyTo(decompressedMs); + } + return decompressedMs.ToArray(); + } + } + } + + public static void SerializeAndCompress(this object obj, string fileWhereToSave) + { + using (FileStream ms = new FileStream(fileWhereToSave, FileMode.Truncate)) + using (GZipStream zs = new GZipStream(ms, CompressionMode.Compress, true)) + { + Serializer.Serialize(zs, obj); + } + } + + public static T DecompressAndDeserialize(this string fileToRead) + { + using (FileStream ms = new FileStream(fileToRead, FileMode.Open)) + using (GZipStream zs = new GZipStream(ms, CompressionMode.Decompress, true)) + { + return Serializer.Deserialize(zs); + } + } + } +} diff --git a/src/MememntoPattern/CompressedEmployee/CaretakerListCompressed.cs b/src/MememntoPattern/CompressedEmployee/CaretakerListCompressed.cs new file mode 100644 index 0000000..5e6fe9c --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/CaretakerListCompressed.cs @@ -0,0 +1,92 @@ +using ProtoBuf; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace MememntoPattern.CompressedEmployee +{ + /// + /// Caretaker + /// + public class CaretakerListCompressed : ICaretaker + { + private static string fileWhereToSave = Path.GetTempFileName(); + + + public void Revert(Employee obj) + { + List historyDeserialized = ReadFile(); + + if (!historyDeserialized.Any()) + return; + + var lastItem = historyDeserialized.Pop(); + + historyDeserialized.SerializeAndCompress(fileWhereToSave); + + obj.Revert(lastItem); + } + + public void Save(Employee obj) + { + List mementosHistory = ReadFile(); + mementosHistory.Add(obj.Save()); + + mementosHistory.SerializeAndCompress(fileWhereToSave); + } + + private static List ReadFile() + { + List historyDeserialized; + + if (new FileInfo(fileWhereToSave).Length != 0) + historyDeserialized = fileWhereToSave.DecompressAndDeserialize>(); + else + historyDeserialized = new List(); + + return historyDeserialized; + } + + public void PrintSizeOfFile() + { + Console.WriteLine("Size of compressed file: " + SizeSuffix(new FileInfo(fileWhereToSave).Length)); + } + + public void PrintSizeOfDecompressedHistory() + { + long historySize = 0; + var historyDeserialized = fileWhereToSave.DecompressAndDeserialize>(); + historySize = historyDeserialized.Sum(h => h.Length); + + Console.WriteLine("Size of all the objects saved in caretaker: " + SizeSuffix(historySize)); + } + + static readonly string[] SizeSuffixes = + { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + static string SizeSuffix(Int64 value) + { + if (value < 0) { return "-" + SizeSuffix(-value); } + if (value == 0) { return "0.0 bytes"; } + + int mag = (int)Math.Log(value, 1024); + decimal adjustedSize = (decimal)value / (1L << (mag * 10)); + + return string.Format("{0:n1} {1}", adjustedSize, SizeSuffixes[mag]); + } + } + + static class ListExtension + { + public static T Pop(this List list) + { + int index = list.Count - 1; + T r = list[index]; + list.RemoveAt(index); + return r; + } + } + + +} diff --git a/src/MememntoPattern/CompressedEmployee/CaretakerObjectCompressed.cs b/src/MememntoPattern/CompressedEmployee/CaretakerObjectCompressed.cs new file mode 100644 index 0000000..3a7ed61 --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/CaretakerObjectCompressed.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; + +namespace MememntoPattern.CompressedEmployee +{ + /// + /// Caretaker + /// + public class CaretakerObjectCompressed : ICaretaker + { + private Stack employeeHistory; + + public CaretakerObjectCompressed() + { + employeeHistory = new Stack(); + } + + public void Save(Employee emp) + { + employeeHistory.Push(ByteArrayCompressionUtility.Compress(emp.Save())); + } + + public void Revert(Employee emp) + { + if (employeeHistory.Count > 0) + emp.Revert(ByteArrayCompressionUtility.Decompress(employeeHistory.Pop())); + } + } +} diff --git a/src/MememntoPattern/CompressedEmployee/Employee.cs b/src/MememntoPattern/CompressedEmployee/Employee.cs new file mode 100644 index 0000000..cdfbcbb --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/Employee.cs @@ -0,0 +1,46 @@ +using ProtoBuf; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading.Tasks; + +namespace MememntoPattern.CompressedEmployee +{ + /// + /// Originator + /// + [ProtoContract] + public class Employee + { + [ProtoMember(1)] + public string Name { get; set; } + + [ProtoMember(2)] + public string Address { get; set; } + + public List Phones { get; set; } + + public byte[] Save() + { + using (MemoryStream ms = new MemoryStream()) + { + Serializer.Serialize(ms, this); + return ms.ToArray(); + } + } + + public void Revert(byte[] memento) + { + Employee employee; + using (MemoryStream ms = new MemoryStream(memento)) + { + employee = Serializer.Deserialize(ms); + } + + Name = employee.Name; + Address = employee.Address; + } + } +} diff --git a/src/MememntoPattern/CompressedEmployee/EmployeeCompressedExample.cs b/src/MememntoPattern/CompressedEmployee/EmployeeCompressedExample.cs new file mode 100644 index 0000000..419b30e --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/EmployeeCompressedExample.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +namespace MememntoPattern.CompressedEmployee +{ + public class EmployeeCompressedExample + { + public void Run() + { + CaretakerObjectCompressed caretaker = new CaretakerObjectCompressed(); + CaretakerListCompressed caretaker2 = new CaretakerListCompressed(); + Employee e = new Employee(); + e.Name = "Ghiuri"; + e.Address = "Stairway to heaven"; + + Console.WriteLine("First saved address: {0}", e.Address); + caretaker.Save(e); + caretaker2.Save(e); + + e.Address = "Highway to hell"; + + Console.WriteLine("Second saved address: {0}", e.Address); + + caretaker.Save(e); + caretaker2.Save(e); + + e.Address = "Home of the brave"; + + Console.WriteLine("Third saved address: {0}", e.Address); + + caretaker.Save(e); + caretaker2.Save(e); + + e.Address = "Somesing"; + + Console.WriteLine("Address before revert: {0}", e.Address); + + caretaker.Revert(e); + caretaker2.Revert(e); + + Console.WriteLine("First reverted Address: {0}", e.Address); + + caretaker.Revert(e); + caretaker2.Revert(e); + + Console.WriteLine("Second reverted Address: {0}", e.Address); + + caretaker.Revert(e); + caretaker2.Revert(e); + + Console.WriteLine("Third reverted Address: {0}", e.Address); + + caretaker.Revert(e); + caretaker2.Revert(e); + + Console.WriteLine("Forth reverted Address: {0}", e.Address); + + Stopwatch sw = Stopwatch.StartNew(); + for (int i = 0; i < 500; i++) + { + e.Address = e.Address + " Le ciupi"; + + caretaker2.Save(e); + } + sw.Stop(); + Console.WriteLine("It took {0} to save 500 versions of the employee object", sw.Elapsed.ToString()); + + caretaker2.PrintSizeOfFile(); + caretaker2.PrintSizeOfDecompressedHistory(); + + sw = Stopwatch.StartNew(); + for (int i = 0; i < 500; i++) + { + caretaker2.Revert(e); + } + sw.Stop(); + + Console.WriteLine("It took {0} to revert 500 versions of the employee object", sw.Elapsed.ToString()); + } + + } +} diff --git a/src/MememntoPattern/CompressedEmployee/ICaretaker.cs b/src/MememntoPattern/CompressedEmployee/ICaretaker.cs new file mode 100644 index 0000000..4cec49f --- /dev/null +++ b/src/MememntoPattern/CompressedEmployee/ICaretaker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MememntoPattern.CompressedEmployee +{ + public interface ICaretaker + { + void Save(T obj); + + void Revert(T obj); + } +} diff --git a/src/MememntoPattern/MementoPatternExamples.cs b/src/MememntoPattern/MementoPatternExamples.cs index 93f7b39..da004be 100644 --- a/src/MememntoPattern/MementoPatternExamples.cs +++ b/src/MememntoPattern/MementoPatternExamples.cs @@ -1,4 +1,5 @@ -using MememntoPattern.Employee; +using MememntoPattern.CompressedEmployee; +using MememntoPattern.Employee; using MememntoPattern.EmployeeSerialized; using MememntoPattern.IterativeEmployee; using System; @@ -19,20 +20,27 @@ namespace MememntoPattern GoToNextStep(); + //Basic example EmployeeExample empExample = new EmployeeExample(); empExample.Run(); GoToNextStep(); + //Limited stack with serialization EmployeeSerializedExample empSerExample = new EmployeeSerializedExample(); empSerExample.Run(); GoToNextStep(); - + + //Iterative memento EmployeeIterativeExample empIterEx = new EmployeeIterativeExample(); empIterEx.Run(); GoToNextStep(); + //Basic memento with compression + EmployeeCompressedExample empComp = new EmployeeCompressedExample(); + empComp.Run(); + Console.WriteLine(GetPitfalls()); } diff --git a/src/MememntoPattern/project.json b/src/MememntoPattern/project.json index 5cf4523..e731e7c 100644 --- a/src/MememntoPattern/project.json +++ b/src/MememntoPattern/project.json @@ -4,6 +4,7 @@ "dependencies": { "NETStandard.Library": "1.6.0", "Newtonsoft.Json": "9.0.1", + "protobuf-net": "2.1.0", "System.Runtime.Serialization.Primitives": "4.1.1" },