├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Blazer.Benchmark ├── Blazer.Benchmark.csproj ├── MessagesDto │ └── LogMessage.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── QuickLZ │ └── QuickLZ.cs └── packages.config ├── Blazer.Core.sln ├── Blazer.Exe ├── Blazer.Exe.csproj ├── CommandLine │ ├── BlazerCommandLineOptions.cs │ ├── CommandLineDescriptionAttribute.cs │ ├── CommandLineOptionAttribute.cs │ └── CommandLineParser.cs ├── FileNameHelper.cs ├── NullStream.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── StatStream.cs ├── Blazer.Native.Build ├── Blazer.Native.x64.dll └── Blazer.Native.x86.dll ├── Blazer.Native ├── .gitignore ├── Blazer.Native.vcxproj ├── Blazer.Native.vcxproj.filters ├── Blazer.cpp ├── Blazer.rc ├── BlazerBlock.cpp ├── BlazerStream.cpp ├── build.cmd ├── crc32c.cpp ├── dllmain.cpp ├── resource.h ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── Blazer.Net.Tests ├── BigDataTests.cs ├── Blazer.Net.Tests.csproj ├── Blazer.Net.Tests.xproj ├── Crc32Tests.cs ├── DataArrayTests.cs ├── EncryptionTests.cs ├── IntegrityHelper.cs ├── IntegrityTests.cs ├── MultipleFilesTests.cs ├── NativeCheckTests.cs ├── OptionsTests.cs ├── PatternedCompressionTests.cs ├── Properties │ └── AssemblyInfo.cs ├── StreamEncoderTests.cs ├── StreamHandlingTests.cs ├── TcpClientExtensions.cs ├── TcpStreamTests.cs ├── packages.config ├── project.json └── project.lock.json ├── Blazer.Net ├── .gitignore ├── Algorithms │ ├── BlockDecoder.cs │ ├── BlockDecoderNative.cs │ ├── BlockEncoder.cs │ ├── BlockEncoderNative.cs │ ├── BufferInfo.cs │ ├── Crc32C │ │ ├── Crc32C.cs │ │ ├── Crc32CHardware.cs │ │ ├── Crc32CSoftware.cs │ │ └── ICrc32CCalculator.cs │ ├── EncoderDecoderFactory.cs │ ├── IDecoder.cs │ ├── IEncoder.cs │ ├── NoCompressionDecoder.cs │ ├── NoCompressionEncoder.cs │ ├── Patterned │ │ ├── BasePatternedCompressor.cs │ │ ├── BlockPatternedCompressor.cs │ │ ├── IPatternedCompressor.cs │ │ ├── StreamHighPatternedCompressor.cs │ │ └── StreamPatternedCompressor.cs │ ├── StreamDecoder.cs │ ├── StreamDecoderNative.cs │ ├── StreamEncoder.cs │ ├── StreamEncoderHigh.cs │ └── StreamEncoderNative.cs ├── Blazer.Net.csproj ├── Blazer.Net.nuspec ├── Blazer.Net.xproj ├── BlazerAlgorithm.cs ├── BlazerBlockType.cs ├── BlazerCompressionOptions.cs ├── BlazerDecompressionOptions.cs ├── BlazerFileInfo.cs ├── BlazerFlags.cs ├── BlazerFlushMode.cs ├── BlazerInputStream.cs ├── BlazerOutputStream.cs ├── BlazerPatternedHelper.cs ├── Encyption │ ├── DecryptHelper.cs │ ├── EncryptHelper.cs │ └── Iso10126TransformEmulator.cs ├── Helpers │ ├── BlazerFileHelper.cs │ ├── DataArrayCompressorHelper.cs │ └── FileHeaderHelper.cs ├── Native │ └── NativeHelper.cs ├── Properties │ └── AssemblyInfo.cs ├── pack.cmd ├── project.json └── project.lock.json ├── Blazer.sln ├── Doc ├── Images │ ├── chart_blocksize1.png │ ├── chart_comprrate1.png │ ├── chart_comprrate2.png │ └── chart_pattern1.png └── PatternedCompression.md ├── LICENSE ├── README.md ├── blazer-nuget-ico.png ├── global.json └── public.snk /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | *.user 4 | *.suo 5 | packages 6 | .tools 7 | private.snk 8 | sign.cmd 9 | _releases 10 | 11 | # todo: check this folders 12 | ipch 13 | Win32 14 | *.sdf 15 | *.opensdf 16 | 17 | .idea -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /Blazer.Benchmark/Blazer.Benchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C3B4452D-103F-4191-A3FC-7188D7F9D082} 8 | Exe 9 | Properties 10 | Force.Blazer.Benchmark 11 | Blazer.Benchmark 12 | v4.0 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | ..\packages\Crc32C.NET.1.0.5.0\lib\net20\Crc32C.NET.dll 41 | 42 | 43 | True 44 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll 45 | 46 | 47 | ..\packages\lz4net.1.0.10.93\lib\net4-client\LZ4.dll 48 | 49 | 50 | ..\packages\Snappy.NET.1.1.1.8\lib\net20\Snappy.NET.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {026EE2B9-3367-480A-8B46-118F4037C827} 69 | Blazer.Net 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 80 | 81 | 82 | 83 | 90 | -------------------------------------------------------------------------------- /Blazer.Benchmark/MessagesDto/LogMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Xml.Serialization; 6 | 7 | namespace Force.Blazer.Benchmark.MessagesDto 8 | { 9 | public class LogMessage 10 | { 11 | public DateTime EventDate { get; set; } 12 | 13 | public string Level { get; set; } 14 | 15 | public string UserName { get; set; } 16 | 17 | public int ProcessingTime { get; set; } 18 | 19 | public string Message { get; set; } 20 | 21 | public LogMessage() 22 | { 23 | } 24 | 25 | public LogMessage(Random r) 26 | { 27 | EventDate = new DateTime(2016, 1, 1).AddSeconds(r.Next(60 * 60 * 24 * 365)); 28 | Level = new[] { "DEBUG", "INFO", "WARN", "ERROR", "FATAL" }[r.Next(5)]; 29 | if (r.Next(2) == 0) UserName = "System"; 30 | else UserName = _words[r.Next(_words.Length)]; 31 | ProcessingTime = r.Next(1000); 32 | Message = string.Join(" ", Enumerable.Range(0, r.Next(10) + 3).Select(x => _words[r.Next(_words.Length)])); 33 | } 34 | 35 | private static readonly string[] _words; 36 | 37 | static LogMessage() 38 | { 39 | var r = new Random(124); 40 | _words = 41 | Enumerable.Range(0, 1000) 42 | .Select(_ => new string(Enumerable.Range(0, r.Next(6) + 1).Select(x => (char)(r.Next(26) + 'a')).ToArray())).ToArray(); 43 | } 44 | 45 | public static byte[][] Generate(int count) 46 | { 47 | // fixed seed 48 | var r = new Random(124); 49 | var l = new List(); 50 | for (var i = 0; i < count; i++) 51 | l.Add(new LogMessage(r)); 52 | 53 | var l2 = new List(); 54 | var s = new XmlSerializer(typeof(LogMessage)); 55 | for (var i = 0; i < count; i++) 56 | { 57 | var ms = new MemoryStream(); 58 | s.Serialize(ms, l[i]); 59 | l2.Add(ms.ToArray()); 60 | } 61 | 62 | return l2.ToArray(); 63 | } 64 | 65 | public static byte[] GenerateBestPattern() 66 | { 67 | var m = new LogMessage 68 | { 69 | EventDate = new DateTime(2016, 1, 1), 70 | Level = "DEBUGINFOWARNERRORFATAL", 71 | UserName = "System", 72 | Message = string.Join(string.Empty, _words) 73 | }; 74 | 75 | var s = new XmlSerializer(typeof(LogMessage)); 76 | var ms = new MemoryStream(); 77 | s.Serialize(ms, m); 78 | return ms.ToArray(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Blazer.Benchmark/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Blazer.Benchmark")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Blazer.Benchmark")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e9830f12-fe45-48a2-80e5-a09b7ddef86b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Blazer.Benchmark/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Blazer.Core.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Blazer.Net", "Blazer.Net\Blazer.Net.xproj", "{1697F214-4621-4366-8313-CF729755BBDF}" 5 | EndProject 6 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Blazer.Net.Tests", "Blazer.Net.Tests\Blazer.Net.Tests.xproj", "{C4BF2879-8EF2-49E5-B168-4C45BA895EEE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {1697F214-4621-4366-8313-CF729755BBDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {1697F214-4621-4366-8313-CF729755BBDF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {1697F214-4621-4366-8313-CF729755BBDF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {1697F214-4621-4366-8313-CF729755BBDF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {C4BF2879-8EF2-49E5-B168-4C45BA895EEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {C4BF2879-8EF2-49E5-B168-4C45BA895EEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {C4BF2879-8EF2-49E5-B168-4C45BA895EEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {C4BF2879-8EF2-49E5-B168-4C45BA895EEE}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | EndGlobal 24 | -------------------------------------------------------------------------------- /Blazer.Exe/Blazer.Exe.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F} 8 | Exe 9 | Properties 10 | Force.Blazer.Exe 11 | Blazer 12 | v4.0 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | true 38 | 39 | 40 | ..\public.snk 41 | 42 | 43 | true 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Resources\Blazer.Net.dll 68 | 69 | 70 | 71 | 72 | {026EE2B9-3367-480A-8B46-118F4037C827} 73 | Blazer.Net 74 | 75 | 76 | 77 | 78 | public.snk 79 | 80 | 81 | 82 | 83 | echo del /q $(TargetDir)Blazer.Net.* 84 | cmd /c if exist "$(ProjectDir)sign.cmd" "$(ProjectDir)sign.cmd" "$(TargetPath)" "$(SolutionDir)private.snk" 85 | 86 | 93 | -------------------------------------------------------------------------------- /Blazer.Exe/CommandLine/BlazerCommandLineOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Exe.CommandLine 2 | { 3 | [CommandLineDescription("Usage: Blazer.exe [options] [archiveName.blz] sourceFile|@fileList")] 4 | public class BlazerCommandLineOptions 5 | { 6 | [CommandLineOption('h', "help", "Display this help")] 7 | public bool Help { get; set; } 8 | 9 | [CommandLineOption('d', "decompress", "Decompress archive")] 10 | public bool Decompress { get; set; } 11 | 12 | [CommandLineOption('l', "list", "List content of archive")] 13 | public bool List { get; set; } 14 | 15 | [CommandLineOption('t', "test", "Test archive")] 16 | public bool Test { get; set; } 17 | 18 | [CommandLineOption('f', "force", "Overwrite target files without confirmation")] 19 | public bool Force { get; set; } 20 | 21 | [CommandLineOption("stdin", "Read data from stdin")] 22 | public bool Stdin { get; set; } 23 | 24 | [CommandLineOption("stdout", "Write data to stdout")] 25 | public bool Stdout { get; set; } 26 | 27 | [CommandLineOption('p', "password", "Archive password")] 28 | public string Password { get; set; } 29 | 30 | [CommandLineOption("encyptfull", "Encrypt archive fully (this key required on decompress)")] 31 | public bool EncryptFull { get; set; } 32 | 33 | [CommandLineOption("nofilename", "Do not (re)store file name")] 34 | public bool NoFileName { get; set; } 35 | 36 | [CommandLineOption("nopathname", "Do not (re)store information about paths")] 37 | public bool NoPathName { get; set; } 38 | 39 | [CommandLineOption("mode", "Compression mode: none, block (default), stream, streamhigh")] 40 | public string Mode { get; set; } 41 | 42 | [CommandLineOption("maxblocksize", "Specifies maximum size of data chunk")] 43 | public string MaxBlockSize { get; set; } 44 | 45 | [CommandLineOption("dataarray", "Compress to solid array with 4-bytes length prefix")] 46 | public bool DataArray { get; set; } 47 | 48 | [CommandLineOption("comment", "Add comment to archive")] 49 | public string Comment { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Blazer.Exe/CommandLine/CommandLineDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Exe.CommandLine 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class CommandLineDescriptionAttribute : Attribute 7 | { 8 | public string Description { get; set; } 9 | 10 | public CommandLineDescriptionAttribute(string description) 11 | { 12 | Description = description; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Blazer.Exe/CommandLine/CommandLineOptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Exe.CommandLine 4 | { 5 | [AttributeUsage(AttributeTargets.Property)] 6 | public class CommandLineOptionAttribute : Attribute 7 | { 8 | public char ShortKey { get; set; } 9 | 10 | public string LongKey { get; set; } 11 | 12 | public string Description { get; set; } 13 | 14 | public CommandLineOptionAttribute(char shortKey, string longKey, string description) 15 | { 16 | ShortKey = shortKey; 17 | LongKey = longKey; 18 | Description = description; 19 | } 20 | 21 | public CommandLineOptionAttribute(string longKey, string description) 22 | { 23 | LongKey = longKey; 24 | Description = description; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Blazer.Exe/CommandLine/CommandLineParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | 9 | namespace Force.Blazer.Exe.CommandLine 10 | { 11 | public class CommandLineParser where T : new() 12 | { 13 | private T _options; 14 | 15 | private List _nonKeyOptions; 16 | 17 | private bool _hasAnyOptions; 18 | 19 | public CommandLineParser(string[] args) 20 | { 21 | ParseArgumentsInternal(args); 22 | } 23 | 24 | private void ParseArgumentsInternal(string[] args) 25 | { 26 | var t = new T(); 27 | 28 | _hasAnyOptions = args.Length > 0; 29 | 30 | var knownOptions = 31 | typeof(T).GetProperties() 32 | .Select(x => new Tuple(x, (CommandLineOptionAttribute)x.GetCustomAttributes(typeof(CommandLineOptionAttribute), true).FirstOrDefault())) 33 | .Where(x => x.Item2 != null) 34 | .ToArray(); 35 | 36 | var boolOptions = 37 | knownOptions.Where(x => x.Item1.PropertyType == typeof(bool) || x.Item1.PropertyType == typeof(bool?)).ToArray(); 38 | 39 | Action throwError = e => { throw new InvalidOperationException("Invalid commandline argument " + e); }; 40 | var dict = new Dictionary(); 41 | var list = new List(); 42 | string key = null; 43 | foreach (var arg in args) 44 | { 45 | if (arg.Length > 0) 46 | { 47 | if (arg[0] == '-') 48 | { 49 | if (arg.Length == 1) throwError(arg); 50 | if (arg[1] == '-') 51 | { 52 | if (arg[1] == '-' && arg.Length <= 3) throwError(arg); 53 | if (key != null) dict[key] = string.Empty; 54 | key = arg.Remove(0, 2); 55 | } 56 | else 57 | { 58 | key = arg.Remove(0, 1); 59 | } 60 | 61 | dict[key] = string.Empty; 62 | } 63 | else 64 | { 65 | if (boolOptions.Any(x => x.Item2.ShortKey.ToString(CultureInfo.InvariantCulture) == key || x.Item2.LongKey == key)) key = null; 66 | if (key == null) 67 | { 68 | list.Add(arg); 69 | } 70 | else 71 | { 72 | dict[key] = arg; 73 | key = null; 74 | } 75 | } 76 | } 77 | } 78 | 79 | _nonKeyOptions = list; 80 | 81 | foreach (var v in dict) 82 | { 83 | var option = knownOptions.FirstOrDefault( 84 | x => x.Item2.ShortKey.ToString(CultureInfo.InvariantCulture) == v.Key || x.Item2.LongKey == v.Key); 85 | if (option != null) 86 | { 87 | if (boolOptions.Contains(option)) 88 | { 89 | option.Item1.SetValue(t, true, null); 90 | } 91 | else 92 | { 93 | var converted = new TypeConverter().ConvertTo(v.Value, option.Item1.PropertyType); 94 | option.Item1.SetValue(t, converted, null); 95 | } 96 | } 97 | } 98 | 99 | _options = t; 100 | } 101 | 102 | public T Get() 103 | { 104 | return _options; 105 | } 106 | 107 | public string[] GetNonParamOptions() 108 | { 109 | return _nonKeyOptions.ToArray(); 110 | } 111 | 112 | public string GetNonParamOptions(int idx) 113 | { 114 | if (_nonKeyOptions.Count <= idx) return null; 115 | return _nonKeyOptions[idx]; 116 | } 117 | 118 | public string GenerateHelp() 119 | { 120 | var headerAttribute = (CommandLineDescriptionAttribute)typeof(T).GetCustomAttributes(typeof(CommandLineDescriptionAttribute), true).FirstOrDefault(); 121 | 122 | var options = typeof(T).GetProperties() 123 | .Select(x => (CommandLineOptionAttribute)x.GetCustomAttributes(typeof(CommandLineOptionAttribute), true).FirstOrDefault()) 124 | .Where(x => x != null) 125 | .ToArray(); 126 | 127 | var maxParamLen = 0; 128 | 129 | foreach (var option in options) 130 | { 131 | var cnt = 0; 132 | if (option.ShortKey != default(char) && option.LongKey != null) cnt = 6 + option.LongKey.Length; 133 | else if (option.ShortKey != default(char)) cnt = 2; 134 | else cnt = 2 + option.LongKey.Length; 135 | maxParamLen = Math.Max(maxParamLen, cnt); 136 | } 137 | 138 | var b = new StringBuilder(); 139 | 140 | if (headerAttribute != null) b.Append(headerAttribute.Description).AppendLine(); 141 | 142 | foreach (var option in options) 143 | { 144 | b.Append("\t"); 145 | string str; 146 | if (option.ShortKey != default(char) && option.LongKey != null) 147 | { 148 | str = string.Format("-{0}, --{1}", option.ShortKey, option.LongKey); 149 | } 150 | else if (option.ShortKey != default(char)) 151 | { 152 | str = string.Format("-{0}", option.ShortKey); 153 | } 154 | else 155 | { 156 | str = string.Format("--{0}", option.LongKey); 157 | } 158 | 159 | b.Append(str); 160 | b.Append(new string(' ', maxParamLen - str.Length)); 161 | b.Append("\t"); 162 | b.Append(option.Description); 163 | b.AppendLine(); 164 | } 165 | 166 | return b.ToString(); 167 | } 168 | 169 | public string GenerateHeader(string additionalVersion) 170 | { 171 | var title = (AssemblyTitleAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), true).First(); 172 | var copy = (AssemblyCopyrightAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true).First(); 173 | var version = (AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true).First(); 174 | 175 | return string.Format("{0} {2} ({3}) {1}", title.Title, copy.Copyright, version.Version, additionalVersion); 176 | } 177 | 178 | public bool HasAnyOptions() 179 | { 180 | return _hasAnyOptions; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Blazer.Exe/FileNameHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | using Force.Blazer.Exe.CommandLine; 7 | 8 | namespace Force.Blazer.Exe 9 | { 10 | public class FileNameHelper 11 | { 12 | public class FileOptions 13 | { 14 | public string ArchiveName { get; set; } 15 | 16 | public string[] SourceFiles { get; set; } 17 | } 18 | 19 | public static FileOptions ParseCompressOptions(CommandLineParser options) 20 | { 21 | var opt = options.Get(); 22 | var nonParamOptions = options.GetNonParamOptions(); 23 | if (nonParamOptions.Length == 0 && !opt.Stdout) 24 | { 25 | Console.WriteLine(options.GenerateHelp()); 26 | return null; 27 | } 28 | 29 | var listFile = options.GetNonParamOptions().FirstOrDefault(x => x[0] == '@'); 30 | if (listFile != null && nonParamOptions.Length != 2 - (opt.Stdout ? 1 : 0)) 31 | { 32 | Console.Error.WriteLine("When list file is provided, only archive name is allowed"); 33 | return null; 34 | } 35 | 36 | if (listFile != null && opt.Stdin) 37 | { 38 | Console.Error.WriteLine("Stdin is not compatible with list file"); 39 | return null; 40 | } 41 | 42 | if (opt.Stdin && nonParamOptions.Length > 1) 43 | { 44 | Console.Error.WriteLine("Stdin is not compatible with multiple files"); 45 | return null; 46 | } 47 | 48 | var archiveName = opt.Stdout ? null : nonParamOptions[0]; 49 | string[] filesToCompress; 50 | 51 | if (listFile != null) 52 | { 53 | listFile = listFile.Remove(0, 1); 54 | if (!File.Exists(listFile)) 55 | { 56 | Console.Error.WriteLine("Invalid list file"); 57 | return null; 58 | } 59 | 60 | filesToCompress = 61 | File.ReadAllLines(listFile).Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray(); 62 | } 63 | else 64 | { 65 | filesToCompress = opt.Stdin ? new string[0] : nonParamOptions.Skip(1).ToArray(); 66 | 67 | if (filesToCompress.Length == 0 && archiveName != null && !opt.Stdin) filesToCompress = new[] { archiveName }; 68 | if (!opt.Stdin) 69 | { 70 | /*if (filesToCompress.Length == 1 && Directory.Exists(filesToCompress[0])) 71 | filesToCompress = Directory.GetFiles(filesToCompress[0], "*", SearchOption.AllDirectories); 72 | else*/ 73 | filesToCompress = filesToCompress.SelectMany(x => Directory.Exists(x) ? Directory.GetFiles(x, "*", SearchOption.AllDirectories) : new[] { x }).ToArray(); 74 | } 75 | } 76 | 77 | bool hasMissingFiles; 78 | filesToCompress = ExpandFilesInList(filesToCompress, out hasMissingFiles); 79 | 80 | if (hasMissingFiles) 81 | { 82 | Console.Error.WriteLine("One or more of files to compress does not exist"); 83 | return null; 84 | } 85 | 86 | if (archiveName != null && !archiveName.EndsWith(".blz")) archiveName += ".blz"; 87 | 88 | return new FileOptions 89 | { 90 | ArchiveName = archiveName, 91 | SourceFiles = filesToCompress 92 | }; 93 | } 94 | 95 | public static FileOptions ParseDecompressOptions(CommandLineParser options) 96 | { 97 | var opt = options.Get(); 98 | 99 | var nonParamOptions = options.GetNonParamOptions(); 100 | 101 | if (!opt.Stdin && nonParamOptions.Length == 0) 102 | { 103 | Console.Error.WriteLine("Archive name was not specified"); 104 | return null; 105 | } 106 | 107 | var archiveName = opt.Stdin ? null : nonParamOptions[0]; 108 | 109 | if (archiveName != null && !File.Exists(archiveName)) 110 | { 111 | Console.Error.WriteLine("Archive file " + archiveName + " does not exist"); 112 | return null; 113 | } 114 | 115 | var listFile = options.GetNonParamOptions().FirstOrDefault(x => x[0] == '@'); 116 | if (listFile != null && nonParamOptions.Length != 2 - (opt.Stdin ? 1 : 0)) 117 | { 118 | Console.Error.WriteLine("When list file is provided, only archive name is allowed"); 119 | return null; 120 | } 121 | 122 | string[] customOutFileNames = null; 123 | 124 | if (listFile != null) 125 | { 126 | listFile = listFile.Remove(0, 1); 127 | if (!File.Exists(listFile)) 128 | { 129 | Console.Error.WriteLine("Invalid list file"); 130 | return null; 131 | } 132 | 133 | customOutFileNames = File.ReadAllLines(listFile).Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray(); 134 | } 135 | else 136 | { 137 | customOutFileNames = nonParamOptions.Skip(opt.Stdin ? 0 : 1).ToArray(); 138 | } 139 | 140 | return new FileOptions 141 | { 142 | ArchiveName = archiveName, 143 | SourceFiles = customOutFileNames 144 | }; 145 | } 146 | 147 | private static string[] ExpandFilesInList(string[] initialFiles, out bool hasMissingFiles) 148 | { 149 | hasMissingFiles = false; 150 | var l = new List(); 151 | // todo: better search + unit tests 152 | foreach (var s in initialFiles) 153 | { 154 | if (File.Exists(s)) 155 | l.Add(s); 156 | else if (Directory.Exists(s)) l.Add(s); 157 | else 158 | { 159 | var asteriskIdx = s.IndexOf("*", StringComparison.InvariantCulture); 160 | if (asteriskIdx < 0) hasMissingFiles = true; 161 | else 162 | { 163 | var slashIdx = asteriskIdx > 0 ? s.LastIndexOfAny( 164 | new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, asteriskIdx - 1, asteriskIdx - 1) : -1; 165 | var dirToSearch = "."; 166 | if (slashIdx >= 0) dirToSearch = s.Substring(0, slashIdx); 167 | l.AddRange(Directory.GetFiles(dirToSearch, s.Remove(0, slashIdx + 1), SearchOption.AllDirectories)); 168 | } 169 | } 170 | } 171 | 172 | return l.ToArray(); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Blazer.Exe/NullStream.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Force.Blazer.Exe 4 | { 5 | public class NullStream : Stream 6 | { 7 | public override void Flush() 8 | { 9 | } 10 | 11 | public override long Seek(long offset, SeekOrigin origin) 12 | { 13 | return -1; 14 | } 15 | 16 | public override void SetLength(long value) 17 | { 18 | } 19 | 20 | public override int Read(byte[] buffer, int offset, int count) 21 | { 22 | return 0; 23 | } 24 | 25 | public override void Write(byte[] buffer, int offset, int count) 26 | { 27 | } 28 | 29 | public override bool CanRead 30 | { 31 | get 32 | { 33 | return true; 34 | } 35 | } 36 | 37 | public override bool CanSeek 38 | { 39 | get 40 | { 41 | return true; 42 | } 43 | } 44 | 45 | public override bool CanWrite 46 | { 47 | get 48 | { 49 | return true; 50 | } 51 | } 52 | 53 | public override long Length 54 | { 55 | get 56 | { 57 | return -1; 58 | } 59 | } 60 | 61 | public override long Position 62 | { 63 | get 64 | { 65 | return -1; 66 | } 67 | 68 | set 69 | { 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Blazer.Exe/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Blazer")] 8 | [assembly: AssemblyDescription("Blazer archiver")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Force")] 11 | [assembly: AssemblyProduct("Blazer Archiver")] 12 | [assembly: AssemblyCopyright("Copyright by Force 2016-2017")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("a1bb1e2e-ec84-48dd-88b0-ff82b75417cd")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("0.10.0.0")] 35 | [assembly: AssemblyFileVersion("0.10.0.13")] 36 | -------------------------------------------------------------------------------- /Blazer.Exe/StatStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | namespace Force.Blazer.Exe 6 | { 7 | public class StatStream : Stream 8 | { 9 | private readonly Stream _baseStream; 10 | 11 | private long _totalHandled; 12 | 13 | private readonly bool _doStats; 14 | 15 | private readonly long _totalLength; 16 | 17 | private int prevPcnt = -1; 18 | 19 | private readonly Stopwatch _sw = new Stopwatch(); 20 | 21 | public string Prefix { get; set; } 22 | 23 | public StatStream(Stream baseStream, bool doStats = false) 24 | { 25 | _baseStream = baseStream; 26 | _doStats = doStats; 27 | if (doStats) 28 | { 29 | _totalLength = baseStream.CanSeek ? baseStream.Length : -1; 30 | _sw.Start(); 31 | } 32 | } 33 | 34 | public override void Close() 35 | { 36 | _baseStream.Close(); 37 | if (_doStats) 38 | { 39 | DoStats(); 40 | if (_sw.ElapsedMilliseconds > 0) 41 | Console.WriteLine(); 42 | } 43 | } 44 | 45 | public override void Flush() 46 | { 47 | _baseStream.Flush(); 48 | } 49 | 50 | public override long Seek(long offset, SeekOrigin origin) 51 | { 52 | return _baseStream.Seek(offset, origin); 53 | } 54 | 55 | public override void SetLength(long value) 56 | { 57 | _baseStream.SetLength(value); 58 | } 59 | 60 | public override int Read(byte[] buffer, int offset, int count) 61 | { 62 | var readed = _baseStream.Read(buffer, offset, count); 63 | _totalHandled += readed; 64 | if (_doStats) 65 | { 66 | DoStats(); 67 | } 68 | 69 | return readed; 70 | } 71 | 72 | private void DoStats() 73 | { 74 | var elapsed = _sw.ElapsedMilliseconds; 75 | if (elapsed == 0) 76 | return; 77 | if (_totalLength > 0) 78 | { 79 | var pcnt = 10000 * _totalHandled / _totalLength; 80 | var pcntAdj = (int)(pcnt / 100); 81 | if (pcntAdj != prevPcnt && pcnt > 0) 82 | { 83 | var eta = (10000 * elapsed / pcnt) - elapsed; 84 | Console.Write( 85 | "\r{3}{0,3}% ETA: {1} {2}MB/s ", 86 | pcntAdj, 87 | TimeSpan.FromMilliseconds(eta).ToString(@"hh\:mm\:ss"), 88 | _totalHandled / 1048 / elapsed, 89 | Prefix); 90 | prevPcnt = pcntAdj; 91 | } 92 | } 93 | else 94 | { 95 | Console.Write("\r{2}Processed {0,3}MB {1}MB/s ", _totalHandled / 1048576, _totalHandled / 1048 / elapsed, Prefix); 96 | } 97 | } 98 | 99 | public override void Write(byte[] buffer, int offset, int count) 100 | { 101 | _baseStream.Write(buffer, offset, count); 102 | _totalHandled += count; 103 | if (_doStats) 104 | { 105 | DoStats(); 106 | } 107 | } 108 | 109 | public override bool CanRead 110 | { 111 | get 112 | { 113 | return _baseStream.CanRead; 114 | } 115 | } 116 | 117 | public override bool CanSeek 118 | { 119 | get 120 | { 121 | return _baseStream.CanSeek; 122 | } 123 | } 124 | 125 | public override bool CanWrite 126 | { 127 | get 128 | { 129 | return _baseStream.CanWrite; 130 | } 131 | } 132 | 133 | public override long Length 134 | { 135 | get 136 | { 137 | return _baseStream.Length; 138 | } 139 | } 140 | 141 | public override long Position 142 | { 143 | get 144 | { 145 | return _baseStream.Position; 146 | } 147 | 148 | set 149 | { 150 | _baseStream.Position = value; 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Blazer.Native.Build/Blazer.Native.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Blazer.Native.Build/Blazer.Native.x64.dll -------------------------------------------------------------------------------- /Blazer.Native.Build/Blazer.Native.x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Blazer.Native.Build/Blazer.Native.x86.dll -------------------------------------------------------------------------------- /Blazer.Native/.gitignore: -------------------------------------------------------------------------------- 1 | # todo remove this folders from config 2 | Debug 3 | Release 4 | Win32 5 | Win32Debug 6 | x64 -------------------------------------------------------------------------------- /Blazer.Native/Blazer.Native.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | 49 | 50 | Resource Files 51 | 52 | 53 | -------------------------------------------------------------------------------- /Blazer.Native/Blazer.cpp: -------------------------------------------------------------------------------- 1 | // Blazer.cpp : Defines the exported functions for the DLL application. 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | 7 | -------------------------------------------------------------------------------- /Blazer.Native/Blazer.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Blazer.Native/Blazer.rc -------------------------------------------------------------------------------- /Blazer.Native/build.cmd: -------------------------------------------------------------------------------- 1 | C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /t:Rebuild /p:Configuration=Release /p:Platform=x64 "/p:VCTargetsPath=C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\" 2 | C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /t:Rebuild /p:Configuration=Release /p:Platform=Win32 "/p:VCTargetsPath=C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\" 3 | xcopy /y Release\Blazer.Native.x86.dll ..\Blazer.Native.Build\ 4 | xcopy /y Release\Blazer.Native.x64.dll ..\Blazer.Native.Build\ 5 | 6 | del /s /q %temp%\Blazer.Net.0.8.2.8 -------------------------------------------------------------------------------- /Blazer.Native/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | 4 | static char* _dummy = "Blazer archiver by Force"; 5 | 6 | void _crc32c_init(); 7 | 8 | BOOL APIENTRY DllMain(HMODULE hModule, 9 | DWORD ul_reason_for_call, 10 | LPVOID lpReserved 11 | ) 12 | { 13 | _crc32c_init(); 14 | if (_dummy == 0) 15 | return FALSE; 16 | /*switch (ul_reason_for_call) 17 | { 18 | case DLL_PROCESS_ATTACH: 19 | case DLL_THREAD_ATTACH: 20 | case DLL_THREAD_DETACH: 21 | case DLL_PROCESS_DETACH: 22 | break; 23 | }*/ 24 | return TRUE; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Blazer.Native/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Blazer.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /Blazer.Native/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // Blazer.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /Blazer.Native/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | 14 | 15 | 16 | // TODO: reference additional headers your program requires here 17 | -------------------------------------------------------------------------------- /Blazer.Native/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/BigDataTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | using Force.Blazer; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Blazer.Net.Tests 10 | { 11 | [TestFixture] 12 | public class BigDataTests 13 | { 14 | [Test] 15 | [Ignore("Slow for usual run")] 16 | public void Large_Data_Should_Be_Compressed() 17 | { 18 | var outs = new MemoryStream(); 19 | // stream algorithm is affected to big data (requires backref shift) 20 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 21 | blazerCompressionOptions.LeaveStreamOpen = true; 22 | var comp = new BlazerInputStream(outs, blazerCompressionOptions); 23 | const int BufSize = 200000; 24 | var buf = new byte[BufSize]; 25 | long totalSize = 1L << 32; // 4Gb 26 | var iterations = totalSize / buf.Length; 27 | totalSize = iterations * buf.Length; 28 | for (var i = 0; i < iterations; i++) 29 | { 30 | var v = i % 256; 31 | for (var k = 0; k < buf.Length; k++) buf[k] = (byte)v; 32 | comp.Write(buf, 0, buf.Length); 33 | } 34 | 35 | comp.Close(); 36 | 37 | Debug.WriteLine("Compressed. " + outs.Length); 38 | 39 | outs.Seek(0, SeekOrigin.Begin); 40 | var decomp = new BlazerOutputStream(outs); 41 | long totalPos = 0; 42 | while (true) 43 | { 44 | var readed = decomp.Read(buf, 0, buf.Length); 45 | if (readed == 0) break; 46 | for (var i = 0; i < readed; i++) 47 | { 48 | long v = (totalPos / BufSize) % 256; 49 | if (buf[i] != v) 50 | throw new ArgumentException("Invalid data at " + totalPos + ". Expected: " + v + ", but was: " + buf[i]); 51 | totalPos++; 52 | } 53 | } 54 | 55 | Assert.That(totalPos, Is.EqualTo(totalSize)); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/Blazer.Net.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF} 8 | Library 9 | Properties 10 | Blazer.Net.Tests 11 | Blazer.Net.Tests 12 | v4.0 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Crc32.NET.1.1.0\lib\net20\Crc32.NET.dll 37 | True 38 | 39 | 40 | ..\packages\Crc32C.NET.1.0.5.0\lib\net20\Crc32C.NET.dll 41 | 42 | 43 | ..\packages\NUnit.3.4.0\lib\net40\nunit.framework.dll 44 | True 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {026EE2B9-3367-480A-8B46-118F4037C827} 75 | Blazer.Net 76 | 77 | 78 | 79 | 80 | 81 | 82 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 83 | 84 | 85 | 86 | 93 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/Blazer.Net.Tests.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0 6 | 7 | 8 | 9 | 10 | {C4BF2879-8EF2-49E5-B168-4C45BA895EEE} 11 | Blazer.Net.Tests 12 | .\obj 13 | .\bin\ 14 | v4.6.1 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/Crc32Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Force.Crc32; 6 | using NUnit.Framework; 7 | 8 | using E = Force.Blazer.Algorithms.Crc32C.Crc32C; 9 | 10 | namespace Blazer.Net.Tests 11 | { 12 | [TestFixture] 13 | public class Crc32Tests 14 | { 15 | [TestCase("Hello", 3)] 16 | [TestCase("Nazdar", 0)] 17 | [TestCase("Ahoj", 1)] 18 | [TestCase("Very long text.Very long text.Very long text.Very long text.Very long text.Very long text.Very long text", 0)] 19 | [TestCase("Very long text.Very long text.Very long text.Very long text.Very long text.Very long text.Very long text", 3)] 20 | public void ResultConsistency(string text, int offset) 21 | { 22 | var bytes = Encoding.ASCII.GetBytes(text); 23 | 24 | var crc1 = E.Calculate(bytes.Skip(offset).ToArray()); 25 | var crc2 = Crc32CAlgorithm.Append(0, bytes, offset, bytes.Length - offset); 26 | Assert.That(crc2, Is.EqualTo(crc1)); 27 | } 28 | 29 | [Test] 30 | public void ResultConsistencyLong() 31 | { 32 | var bytes = new byte[30000]; 33 | new Random().NextBytes(bytes); 34 | var crc1 = E.Calculate(bytes, 0, bytes.Length); 35 | var crc2 = Crc32CAlgorithm.Append(0, bytes, 0, bytes.Length); 36 | Assert.That(crc2, Is.EqualTo(crc1)); 37 | } 38 | 39 | [Test] 40 | public void ResultConsistency2() 41 | { 42 | Assert.That(E.Calculate(new byte[] { 1 }), Is.EqualTo(Crc32CAlgorithm.Compute(new byte[] { 1 }))); 43 | Assert.That(E.Calculate(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }), Is.EqualTo(Crc32CAlgorithm.Compute(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }))); 44 | } 45 | 46 | [Test] 47 | public void PartIsWhole() 48 | { 49 | var bytes = new byte[30000]; 50 | new Random().NextBytes(bytes); 51 | var r1 = E.Calculate(0, bytes, 0, 15000); 52 | var r2 = E.Calculate(r1, bytes, 15000, 15000); 53 | var r3 = E.Calculate(0, bytes, 0, 30000); 54 | Assert.That(r2, Is.EqualTo(r3)); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/DataArrayTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Force.Blazer; 5 | using Force.Blazer.Algorithms; 6 | using Force.Blazer.Helpers; 7 | using Force.Blazer.Native; 8 | 9 | using NUnit.Framework; 10 | 11 | namespace Blazer.Net.Tests 12 | { 13 | [TestFixture] 14 | public class DataArrayTests 15 | { 16 | [Test] 17 | public void Stream_Encode_Decode_Should_Not_Resize_Array() 18 | { 19 | var bufferIn = new byte[] { 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; 20 | var compr = StreamEncoder.CompressData(bufferIn); 21 | var bufferOut = new byte[bufferIn.Length]; 22 | var cnt = StreamDecoder.DecompressBlockExternal(compr, 0, compr.Length, ref bufferOut, 0, bufferOut.Length, false); 23 | Assert.That(cnt, Is.EqualTo(bufferIn.Length)); 24 | CollectionAssert.AreEqual(bufferIn, bufferOut); 25 | } 26 | 27 | [Test] 28 | [TestCase(typeof(BlockEncoder), typeof(BlockDecoder))] 29 | [TestCase(typeof(BlockEncoderNative), typeof(BlockDecoderNative))] 30 | public void Block_Encode_Decode_Should_Not_Resize_Array(Type encoderType, Type decoderType) 31 | { 32 | // ensuring native is inited 33 | NativeHelper.SetNativeImplementation(true); 34 | var bufferIn = new byte[] { 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; 35 | var encoder = (BlockEncoder)Activator.CreateInstance(encoderType); 36 | encoder.Init(bufferIn.Length); 37 | var compr = new byte[bufferIn.Length]; 38 | var comprCnt = encoder.CompressBlock(bufferIn, 0, bufferIn.Length, compr, 0, true); 39 | Console.WriteLine(comprCnt); 40 | 41 | var bufferOut = new byte[bufferIn.Length]; 42 | var decoder = (BlockDecoder)Activator.CreateInstance(decoderType); 43 | decoder.Init(bufferIn.Length); 44 | var cnt = decoder.DecompressBlock(compr, 0, comprCnt, bufferOut, 0, bufferOut.Length, true); 45 | Assert.That(cnt, Is.EqualTo(bufferIn.Length)); 46 | CollectionAssert.AreEqual(bufferIn, bufferOut); 47 | } 48 | 49 | [Test] 50 | [TestCase(typeof(StreamEncoder), typeof(StreamDecoder))] 51 | [TestCase(typeof(StreamEncoderNative), typeof(StreamDecoderNative))] 52 | [TestCase(typeof(StreamEncoderHigh), typeof(StreamDecoder))] 53 | public void Stream_Encode_Decode_Should_Not_Resize_Array(Type encoderType, Type decoderType) 54 | { 55 | // ensuring native is inited 56 | NativeHelper.SetNativeImplementation(true); 57 | var bufferIn = new byte[] { 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; 58 | var encoder = (StreamEncoder)Activator.CreateInstance(encoderType); 59 | encoder.Init(bufferIn.Length); 60 | var compr = new byte[bufferIn.Length]; 61 | var comprCnt = encoder.CompressBlock(bufferIn, 0, bufferIn.Length, 0, compr, 0); 62 | Console.WriteLine(comprCnt); 63 | 64 | var bufferOut = new byte[bufferIn.Length]; 65 | var decoder = (StreamDecoder)Activator.CreateInstance(decoderType); 66 | decoder.Init(bufferIn.Length); 67 | var cnt = decoder.DecompressBlock(compr, 0, comprCnt, bufferOut, 0, bufferOut.Length); 68 | Assert.That(cnt, Is.EqualTo(bufferIn.Length)); 69 | CollectionAssert.AreEqual(bufferIn, bufferOut); 70 | } 71 | 72 | [Test] 73 | [TestCase(BlazerAlgorithm.NoCompress)] 74 | [TestCase(BlazerAlgorithm.Block)] 75 | [TestCase(BlazerAlgorithm.Stream)] 76 | public void DataArrayCompressionHelper_Should_Encode_Decode(BlazerAlgorithm algorithm) 77 | { 78 | var bufferIn = new byte[] { 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; 79 | var arr = DataArrayCompressorHelper.CompressDataToArray(bufferIn, EncoderDecoderFactory.GetEncoder(algorithm)); 80 | var bufferOut = DataArrayCompressorHelper.DecompressDataArray(arr, EncoderDecoderFactory.GetDecoder(algorithm)); 81 | CollectionAssert.AreEqual(bufferIn, bufferOut); 82 | } 83 | 84 | [Test] 85 | [TestCase(BlazerAlgorithm.NoCompress)] 86 | [TestCase(BlazerAlgorithm.Block)] 87 | [TestCase(BlazerAlgorithm.Stream)] 88 | public void DataArrayCompressionHelper_Should_Encode_Decode_With_Offset(BlazerAlgorithm algorithm) 89 | { 90 | var bufferIn = new byte[] { 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; 91 | var arr = DataArrayCompressorHelper.CompressDataToArray(bufferIn, 1, bufferIn.Length - 2, EncoderDecoderFactory.GetEncoder(algorithm)); 92 | var dupArray = new byte[arr.Length + 10]; 93 | Buffer.BlockCopy(arr, 0, dupArray, 2, arr.Length); 94 | var bufferOut = DataArrayCompressorHelper.DecompressDataArray(dupArray, 2, arr.Length, EncoderDecoderFactory.GetDecoder(algorithm)); 95 | CollectionAssert.AreEqual(bufferIn.Skip(1).Take(bufferIn.Length - 2).ToArray(), bufferOut); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/IntegrityHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | using Force.Blazer; 5 | using Force.Blazer.Algorithms; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Blazer.Net.Tests 10 | { 11 | public static class IntegrityHelper 12 | { 13 | public static int CheckCompressDecompress(byte[] inData, BlazerCompressionOptions options, Func decoderCreator = null, int bufferSize = 81920) 14 | { 15 | var compressed = CompressData(inData, options, bufferSize); 16 | var decompressed = DecompressData(compressed, decoderCreator, bufferSize); 17 | 18 | CollectionAssert.AreEqual(inData, decompressed); 19 | return compressed.Length; 20 | } 21 | 22 | public static byte[] CompressData(byte[] inData, BlazerCompressionOptions options, int bufferSize = 81920) 23 | { 24 | var ms1 = new MemoryStream(); 25 | var input = new BlazerInputStream(ms1, options); 26 | 27 | new MemoryStream(inData).CopyTo(input, bufferSize); 28 | input.Close(); 29 | return ms1.ToArray(); 30 | } 31 | 32 | public static byte[] DecompressData(byte[] inData, Func decoderCreator = null, int bufferSize = 81920) 33 | { 34 | var ms3 = new MemoryStream(inData); 35 | var output = decoderCreator != null ? decoderCreator(ms3) : new BlazerOutputStream(ms3); 36 | var ms2 = new MemoryStream(); 37 | output.CopyTo(ms2, bufferSize); 38 | output.Close(); 39 | return ms2.ToArray(); 40 | } 41 | 42 | public static int StreamEncoderCheckCompressDecompress(byte[] inData) 43 | { 44 | var compressed = StreamEncoder.CompressData(inData); 45 | var decompressed = StreamDecoder.DecompressData(compressed); 46 | CollectionAssert.AreEqual(inData, decompressed); 47 | return compressed.Length; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/IntegrityTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | using Force.Blazer; 7 | 8 | using NUnit.Framework; 9 | 10 | namespace Blazer.Net.Tests 11 | { 12 | [TestFixture] 13 | public class IntegrityTests 14 | { 15 | [Test] 16 | [TestCase(BlazerAlgorithm.NoCompress)] 17 | [TestCase(BlazerAlgorithm.Stream)] 18 | [TestCase(BlazerAlgorithm.Block)] 19 | public void Simple_Data_Should_Be_Encoded_Decoded(BlazerAlgorithm algorithm) 20 | { 21 | var data = Encoding.UTF8.GetBytes("some compressible not very long string. some some some."); 22 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 23 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 24 | IntegrityHelper.CheckCompressDecompress(data, blazerCompressionOptions); 25 | } 26 | 27 | [Test] 28 | [TestCase(BlazerAlgorithm.NoCompress)] 29 | [TestCase(BlazerAlgorithm.Stream)] 30 | [TestCase(BlazerAlgorithm.Block)] 31 | public void HighCompressible_Data_Should_Be_Encoded_Decoded(BlazerAlgorithm algorithm) 32 | { 33 | var data = new byte[10 * 1048576]; 34 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 35 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 36 | IntegrityHelper.CheckCompressDecompress(data, blazerCompressionOptions); 37 | } 38 | 39 | [Test] 40 | [TestCase(BlazerAlgorithm.NoCompress)] 41 | [TestCase(BlazerAlgorithm.Stream)] 42 | [TestCase(BlazerAlgorithm.Block)] 43 | public void NonCompressible_Data_Should_Be_Encoded_Decoded(BlazerAlgorithm algorithm) 44 | { 45 | var data = new byte[1234]; 46 | RandomNumberGenerator.Create().GetBytes(data); 47 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 48 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 49 | IntegrityHelper.CheckCompressDecompress(data, blazerCompressionOptions); 50 | } 51 | 52 | [Test] 53 | public void Invalid_Header_Should_Throw_Errors() 54 | { 55 | var data1 = new byte[12]; 56 | var compressed = IntegrityHelper.CompressData(data1, BlazerCompressionOptions.CreateStream()); 57 | // not blazer archiver 58 | compressed[0]++; 59 | Assert.That(Assert.Throws(() => IntegrityHelper.DecompressData(compressed)).Message, Is.EqualTo("This is not Blazer archive")); 60 | compressed[0]--; 61 | 62 | // invalid version 63 | compressed[3]++; 64 | Assert.That(Assert.Throws(() => IntegrityHelper.DecompressData(compressed)).Message, Is.EqualTo("Stream was created in newer version of Blazer library")); 65 | compressed[3]--; 66 | 67 | // invalid flags 68 | compressed[6] = 0xff; 69 | Assert.That(Assert.Throws(() => IntegrityHelper.DecompressData(compressed)).Message, Is.EqualTo("Invalid flag combination. Try to use newer version of Blazer")); 70 | // compressed[6]--; 71 | } 72 | 73 | [Test] 74 | [TestCase(1, BlazerAlgorithm.NoCompress)] 75 | [TestCase(1, BlazerAlgorithm.Stream)] 76 | [TestCase(1, BlazerAlgorithm.Block)] 77 | [TestCase(2, BlazerAlgorithm.NoCompress)] 78 | [TestCase(2, BlazerAlgorithm.Stream)] 79 | [TestCase(2, BlazerAlgorithm.Block)] 80 | [TestCase(3, BlazerAlgorithm.NoCompress)] 81 | [TestCase(3, BlazerAlgorithm.Stream)] 82 | [TestCase(3, BlazerAlgorithm.Block)] 83 | [TestCase(4, BlazerAlgorithm.NoCompress)] 84 | [TestCase(4, BlazerAlgorithm.Stream)] 85 | [TestCase(4, BlazerAlgorithm.Block)] 86 | public void Small_Block_Sizes_ShouldBe_Handled_WithoutFlush(int blockSize, BlazerAlgorithm algorithm) 87 | { 88 | var data = new byte[blockSize]; 89 | data[0] = (byte)blockSize; 90 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 91 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 92 | 93 | var memoryStream = new MemoryStream(); 94 | const int Count = 10; 95 | using (var stream = new BlazerInputStream(memoryStream, blazerCompressionOptions)) 96 | { 97 | for (var i = 0; i < Count; i++) 98 | stream.Write(data, 0, data.Length); 99 | } 100 | 101 | var compressed = memoryStream.ToArray(); 102 | var decompressed = IntegrityHelper.DecompressData(compressed); 103 | Assert.That(decompressed.Length, Is.EqualTo(Count * data.Length)); 104 | } 105 | 106 | [Test] 107 | [TestCase(1, BlazerAlgorithm.NoCompress)] 108 | [TestCase(1, BlazerAlgorithm.Stream)] 109 | [TestCase(1, BlazerAlgorithm.Block)] 110 | [TestCase(2, BlazerAlgorithm.NoCompress)] 111 | [TestCase(2, BlazerAlgorithm.Stream)] 112 | [TestCase(2, BlazerAlgorithm.Block)] 113 | [TestCase(3, BlazerAlgorithm.NoCompress)] 114 | [TestCase(3, BlazerAlgorithm.Stream)] 115 | [TestCase(3, BlazerAlgorithm.Block)] 116 | [TestCase(4, BlazerAlgorithm.NoCompress)] 117 | [TestCase(4, BlazerAlgorithm.Stream)] 118 | [TestCase(4, BlazerAlgorithm.Block)] 119 | public void Small_Block_Sizes_ShouldBe_Handled_WithFlush(int blockSize, BlazerAlgorithm algorithm) 120 | { 121 | var data = new byte[blockSize]; 122 | data[0] = (byte)blockSize; 123 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 124 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 125 | blazerCompressionOptions.FlushMode = BlazerFlushMode.RespectFlush; 126 | 127 | var memoryStream = new MemoryStream(); 128 | const int Count = 10; 129 | using (var stream = new BlazerInputStream(memoryStream, blazerCompressionOptions)) 130 | { 131 | for (var i = 0; i < Count; i++) 132 | { 133 | stream.Write(data, 0, data.Length); 134 | stream.Flush(); 135 | } 136 | } 137 | 138 | var compressed = memoryStream.ToArray(); 139 | var decompressed = IntegrityHelper.DecompressData(compressed); 140 | Assert.That(decompressed.Length, Is.EqualTo(Count * data.Length)); 141 | Assert.That(decompressed[blockSize], Is.EqualTo(blockSize)); 142 | } 143 | 144 | [Test] 145 | [TestCase(BlazerAlgorithm.NoCompress)] 146 | [TestCase(BlazerAlgorithm.Stream)] 147 | [TestCase(BlazerAlgorithm.Block)] 148 | public void Zero_Block_Sizes_Should_Not_Cause_Error(BlazerAlgorithm algorithm) 149 | { 150 | var data = new byte[0]; 151 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 152 | blazerCompressionOptions.SetEncoderByAlgorithm(algorithm); 153 | 154 | var memoryStream = new MemoryStream(); 155 | const int Count = 10; 156 | using (var stream = new BlazerInputStream(memoryStream, blazerCompressionOptions)) 157 | { 158 | for (var i = 0; i < Count; i++) 159 | { 160 | stream.Write(data, 0, data.Length); 161 | stream.Flush(); 162 | } 163 | } 164 | 165 | var compressed = memoryStream.ToArray(); 166 | var decompressed = IntegrityHelper.DecompressData(compressed); 167 | Assert.That(decompressed.Length, Is.EqualTo(0)); 168 | } 169 | 170 | [Test] 171 | public void FailData_Should_Not_Cause_StackOverflow() 172 | { 173 | var ss = new MemoryStream(); 174 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 175 | blazerCompressionOptions.IncludeFooter = false; 176 | var os = new BlazerInputStream(ss, blazerCompressionOptions); 177 | os.WriteControlData(new byte[0], 0, 0); 178 | 179 | new BlazerOutputStream(new MemoryStream(ss.ToArray()), new BlazerDecompressionOptions { NoSeek = true }).Read(new byte[100], 0, 100); 180 | } 181 | 182 | [Test] 183 | public void MissingFooter_With_No_Seec_Should_Cause_Error() 184 | { 185 | var ss = new MemoryStream(); 186 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 187 | var os = new BlazerInputStream(ss, blazerCompressionOptions); 188 | os.WriteControlData(new byte[0], 0, 0); 189 | 190 | var ex = Assert.Throws(() => new BlazerOutputStream(new MemoryStream(ss.ToArray()), new BlazerDecompressionOptions { NoSeek = true }).Read(new byte[100], 0, 100)); 191 | Assert.That(ex.Message, Is.EqualTo("Stream was finished, but footer is missing. It seems, stream is incomplete")); 192 | } 193 | 194 | [Test] 195 | public void After_Footer_Read_Should_Be_Zero() 196 | { 197 | var ss = new MemoryStream(); 198 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 199 | blazerCompressionOptions.LeaveStreamOpen = true; 200 | var os = new BlazerInputStream(ss, blazerCompressionOptions); 201 | os.Write(new byte[100], 0, 100); 202 | os.Close(); 203 | ss.Write(new byte[] { 1, 2, 3, 4 }, 0, 4); 204 | ss.Close(); 205 | 206 | var oos = new BlazerOutputStream(new MemoryStream(ss.ToArray()), new BlazerDecompressionOptions { NoSeek = true }); 207 | var read = oos.Read(new byte[100], 0, 100); 208 | Assert.That(read, Is.EqualTo(100)); 209 | Assert.That(oos.Read(new byte[100], 0, 100), Is.EqualTo(0)); 210 | Assert.That(oos.Read(new byte[100], 0, 100), Is.EqualTo(0)); 211 | } 212 | 213 | // checking strange MemoryStream logic 214 | /*[Test] 215 | public void Test() 216 | { 217 | var stream = new MemoryStream(new byte[1000], 10, 100); 218 | 219 | Assert.That(stream.Position, Is.EqualTo(stream.Seek(0, SeekOrigin.Current))); 220 | }*/ 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/MultipleFilesTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | using Force.Blazer; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Blazer.Net.Tests 10 | { 11 | [TestFixture] 12 | public class MultipleFilesTests 13 | { 14 | [Test] 15 | public void Cannot_Set_One_File_And_Multiple_Files() 16 | { 17 | var options = new BlazerCompressionOptions(); 18 | options.FileInfo = new BlazerFileInfo(); 19 | Assert.Throws(() => options.MultipleFiles = true); 20 | 21 | options = new BlazerCompressionOptions(); 22 | options.MultipleFiles = true; 23 | Assert.Throws(() => options.FileInfo = new BlazerFileInfo()); 24 | } 25 | 26 | [Test] 27 | public void EmptyFileInfo_Should_Not_Cause_An_Error() 28 | { 29 | var data = new byte[123]; 30 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 31 | blazerCompressionOptions.FileInfo = new BlazerFileInfo(); 32 | var compressed = IntegrityHelper.CompressData(data, blazerCompressionOptions); 33 | var os = new BlazerOutputStream(new MemoryStream(compressed)); 34 | Assert.That(os.FileInfo, Is.Not.Null); 35 | Assert.That(os.FileInfo.FileName, Is.EqualTo(string.Empty)); 36 | } 37 | 38 | [Test] 39 | public void FirstChunk_For_MultipleFiles_Should_Be_FileInfo() 40 | { 41 | var data = new byte[123]; 42 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 43 | blazerCompressionOptions.MultipleFiles = true; 44 | var stream = new BlazerInputStream(new MemoryStream(), blazerCompressionOptions); 45 | Assert.Throws(() => stream.Write(data, 0, data.Length)); 46 | 47 | // does not throw 48 | stream = new BlazerInputStream(new MemoryStream(), blazerCompressionOptions); 49 | stream.WriteFileInfo(new BlazerFileInfo()); 50 | stream.Write(data, 0, data.Length); 51 | } 52 | 53 | [Test] 54 | public void Two_Files_Should_Be_Compressed() 55 | { 56 | var data = new byte[123]; 57 | var blazerCompressionOptions = BlazerCompressionOptions.CreateStream(); 58 | blazerCompressionOptions.MultipleFiles = true; 59 | var memoryStream = new MemoryStream(); 60 | var stream = new BlazerInputStream(memoryStream, blazerCompressionOptions); 61 | stream.WriteFileInfo(new BlazerFileInfo { FileName = "t1" }); 62 | stream.Write(data, 0, data.Length); 63 | stream.WriteFileInfo(new BlazerFileInfo { FileName = "t2" }); 64 | stream.Write(data, 0, data.Length); 65 | stream.Close(); 66 | var res = memoryStream.ToArray(); 67 | var decOptions = new BlazerDecompressionOptions(); 68 | var q = new Queue(new[] { "t1", "t2" }); 69 | decOptions.FileInfoCallback = f => Assert.That(f.FileName, Is.EqualTo(q.Dequeue())); 70 | new BlazerOutputStream(new MemoryStream(res), decOptions).CopyTo(new MemoryStream()); 71 | Assert.That(q.Count, Is.EqualTo(0)); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/NativeCheckTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using Force.Blazer.Native; 7 | using Force.Crc32; 8 | using NUnit.Framework; 9 | 10 | using E = Force.Blazer.Algorithms.Crc32C.Crc32C; 11 | 12 | namespace Blazer.Net.Tests 13 | { 14 | [TestFixture] 15 | public class NativeCheckTests 16 | { 17 | [Test] 18 | public void ResultConsistency() 19 | { 20 | // checking that native implementation is available. it works only on windows, but tests on windows now 21 | 22 | // removing old data 23 | var architectureSuffix = IntPtr.Size == 8 ? "x64" : "x86"; 24 | var dllPath = Path.Combine(Path.GetTempPath(), "Blazer.Net.0.8.3.9", architectureSuffix); 25 | var fileName = Path.Combine(dllPath, "Blazer.Native.dll"); 26 | if (File.Exists(fileName)) 27 | { 28 | try 29 | { 30 | File.Delete(fileName); 31 | } 32 | catch (Exception e) 33 | { 34 | File.Move(fileName, fileName + ".old"); 35 | throw; 36 | } 37 | } 38 | 39 | var result = (bool) typeof(NativeHelper).GetMethod("Init", BindingFlags.Static | BindingFlags.NonPublic) 40 | .Invoke(null, new object[0]); 41 | 42 | Assert.That(result, Is.True); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/PatternedCompressionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Force.Blazer.Algorithms; 5 | using Force.Blazer.Algorithms.Patterned; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Blazer.Net.Tests 10 | { 11 | [TestFixture(1)] 12 | [TestFixture(2)] 13 | [TestFixture(3)] 14 | public class PatternedCompressionTests 15 | { 16 | private readonly Type _compressorType; 17 | 18 | private BasePatternedCompressor GetCompressor() 19 | { 20 | return (BasePatternedCompressor)Activator.CreateInstance(_compressorType); 21 | } 22 | 23 | public PatternedCompressionTests(int type) 24 | { 25 | if (type == 1) _compressorType = typeof(StreamPatternedCompressor); 26 | else if (type == 2) _compressorType = typeof(StreamHighPatternedCompressor); 27 | else if (type == 3) _compressorType = typeof(BlockPatternedCompressor); 28 | else throw new NotImplementedException(); 29 | } 30 | 31 | [Test] 32 | public void DataWithPattern_Should_Be_Correctly_Encoded_Decoded() 33 | { 34 | var ssed = GetCompressor(); 35 | var pattern = new byte[100]; 36 | pattern[1] = 42; 37 | ssed.PreparePattern(pattern, 0, pattern.Length); 38 | 39 | var data1 = new byte[10]; 40 | data1[0] = 42; 41 | 42 | var data2 = new byte[10]; 43 | data2[0] = 12; 44 | 45 | var tmpOut = new byte[ssed.CalculateMaxCompressedBufferLength(10)]; 46 | var tmpOut2 = new byte[ssed.CalculateMaxCompressedBufferLength(10)]; 47 | 48 | var cntPatterned = ssed.EncodeWithPattern(data1, 0, data1.Length, tmpOut, 0); 49 | var cntUnpatterned = StreamEncoder.CompressData(data1); 50 | // TODO: uncomment 51 | Assert.That(cntPatterned, Is.LessThan(cntUnpatterned.Length)); 52 | var cntPatternUnpacked = ssed.DecodeWithPattern(tmpOut, 0, cntPatterned, tmpOut2, 0); 53 | Assert.That(cntPatternUnpacked, Is.EqualTo(10)); 54 | CollectionAssert.AreEqual(data1, tmpOut2.Take(cntPatternUnpacked)); 55 | 56 | // checking that we can repeat without failure 57 | cntPatterned = ssed.EncodeWithPattern(data2, 0, data2.Length, tmpOut, 0); 58 | cntPatternUnpacked = ssed.DecodeWithPattern(tmpOut, 0, cntPatterned, tmpOut2, 0); 59 | Assert.That(cntPatternUnpacked, Is.EqualTo(10)); 60 | CollectionAssert.AreEqual(data2, tmpOut2.Take(cntPatternUnpacked)); 61 | } 62 | 63 | [Test] 64 | public void DataWithPattern_Should_Be_Correctly_Encoded_Decoded_With_Offsets() 65 | { 66 | var ssed = GetCompressor(); 67 | var pattern = new byte[100]; 68 | pattern[1] = 42; 69 | ssed.PreparePattern(pattern, 1, pattern.Length - 1); 70 | 71 | var data1 = new byte[10]; 72 | data1[1] = 42; 73 | 74 | var data2 = new byte[9]; 75 | data1[0] = 42; 76 | 77 | var tmpOut = new byte[ssed.CalculateMaxCompressedBufferLength(10)]; 78 | var tmpOut2 = new byte[ssed.CalculateMaxCompressedBufferLength(10)]; 79 | 80 | var cntPatterned1 = ssed.EncodeWithPattern(data1, 1, data1.Length - 1, tmpOut, 1); 81 | var cntPatterned2 = ssed.EncodeWithPattern(data2, 0, data2.Length, tmpOut2, 0); 82 | Assert.That(cntPatterned1, Is.EqualTo(cntPatterned2)); 83 | var cntPatternedUnpacked = ssed.DecodeWithPattern(tmpOut, 1, cntPatterned1, tmpOut2, 1); 84 | Assert.That(cntPatternedUnpacked, Is.EqualTo(9)); 85 | CollectionAssert.AreEqual(data1.Skip(1), tmpOut2.Skip(1).Take(cntPatternedUnpacked)); 86 | } 87 | 88 | [Test] 89 | public void DataWithPattern_Should_Be_Correctly_Encoded_Decoded_With_Simple_Interface() 90 | { 91 | var ssed = GetCompressor(); 92 | var pattern = new byte[100]; 93 | pattern[1] = 42; 94 | ssed.PreparePattern(pattern, 1, pattern.Length - 1); 95 | 96 | var data1 = new byte[10]; 97 | data1[1] = 42; 98 | 99 | var patterned = ssed.EncodeWithPattern(data1); 100 | var patternedUnpacked = ssed.DecodeWithPattern(patterned); 101 | 102 | CollectionAssert.AreEqual(data1, patternedUnpacked); 103 | } 104 | 105 | [Test] 106 | public void Zero_Length_Should_not_Cause_Error() 107 | { 108 | var ssed = GetCompressor(); 109 | var pattern = new byte[100]; 110 | pattern[1] = 42; 111 | ssed.PreparePattern(pattern, 1, pattern.Length - 1); 112 | 113 | var data1 = new byte[0]; 114 | 115 | var patterned = ssed.EncodeWithPattern(data1); 116 | Assert.That(patterned.Length, Is.EqualTo(1)); 117 | var patternUnpacked = ssed.DecodeWithPattern(patterned); 118 | 119 | CollectionAssert.AreEqual(data1, patternUnpacked); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Blazer.Net.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Blazer.Net.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("23d2154d-816c-47aa-b67c-6690949c054e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/StreamEncoderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using NUnit.Framework; 6 | 7 | namespace Blazer.Net.Tests 8 | { 9 | [TestFixture] 10 | public class StreamEncoderTests 11 | { 12 | [OneTimeSetUp] 13 | public void OneTimeSetUp() 14 | { 15 | #if NETCORE 16 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 17 | #endif 18 | } 19 | 20 | [Test] 21 | public void Test_AAAAAAAA() 22 | { 23 | var buf = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1 }; 24 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 25 | // 1 times 1, then 7 times ref -1 26 | Assert.That(len, Is.EqualTo(3)); 27 | } 28 | 29 | [Test] 30 | public void Test_AAAAAAAABBBBBBBB() 31 | { 32 | var buf = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 }; 33 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 34 | Assert.That(len, Is.LessThanOrEqualTo(16)); 35 | } 36 | 37 | [Test] 38 | public void Test_1000A() 39 | { 40 | // byte 0: short backref, 1 lit, 15 repetitions 41 | // byte 1: backref 0 + 1 42 | // byte 2: 254 repetitions + 2 byte 43 | // byte 3-4: 1d7 repetitions. 471 + 256 + 253 + 15 + 4 = 999 44 | // 1F-00-FE-01-D7-01 (6) 45 | var buf = Enumerable.Range(0, 1000).Select(x => (byte)1).ToArray(); 46 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 47 | // 1 times 1, then 7 times ref -1 48 | Assert.That(len, Is.EqualTo(6)); 49 | } 50 | 51 | [Test] 52 | public void Test_ByteBorder_Repetitions() 53 | { 54 | for (var i = 250; i < 300; i++) 55 | { 56 | var buf = Enumerable.Range(0, i).Select(x => (byte)1).ToArray(); 57 | IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 58 | } 59 | 60 | for (var i = 65700; i < 65800; i++) 61 | { 62 | var buf = Enumerable.Range(0, i).Select(x => (byte)1).ToArray(); 63 | IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 64 | } 65 | } 66 | 67 | [Test] 68 | public void Test_HabaxHabax() 69 | { 70 | var buf = new byte[] { 2, 1, 3, 1, 4, 2, 1, 3, 1, 4 }; 71 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 72 | Assert.That(len, Is.LessThan(8)); 73 | } 74 | 75 | [Test] 76 | public void Test_Cycle_20000_SemiRandom() 77 | { 78 | var r = new Random(12346); 79 | var buf = Enumerable.Range(0, 20000).Select(x => (byte)r.Next(2)).ToArray(); 80 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 81 | // 1 times 1, then 7 times ref -1 82 | Assert.That(len, Is.LessThan(20000)); 83 | } 84 | 85 | [Test] 86 | public void Test_Cycle_120000_SemiRandom() 87 | { 88 | var r = new Random(12346); 89 | var buf = Enumerable.Range(0, 120000).Select(x => (byte)r.Next(2)).ToArray(); 90 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 91 | // 1 times 1, then 7 times ref -1 92 | Assert.That(len, Is.LessThan(120000)); 93 | } 94 | 95 | [Test] 96 | public void Test_Text() 97 | { 98 | var buf = Encoding.GetEncoding(1251).GetBytes(@" 99 | Когда то давно Московское метро замышлялось как гигантское бомбоубежище, способное спасти десятки тысяч жизней. Мир стоял на пороге гибели, но тогда ее удалось отсрочить. Дорога, по которой идет человечество, вьется, как спираль, и однажды оно снова окажется на краю пропасти. Когда мир будет рушиться, метро окажется последним пристанищем человека перед тем, как он канет в ничто. 100 | — Кто это там? Эй, Артем! Глянь ка! 101 | Артем нехотя поднялся со своего места у костра и, перетягивая со спины на грудь автомат, двинулся во тьму. Стоя на самом краю освещенного пространства, он демонстративно, как можно громче и внушительней, щелкнул затвором и хрипло крикнул: — Стоять! Пароль!"); 102 | IntegrityHelper.StreamEncoderCheckCompressDecompress(buf); 103 | } 104 | 105 | [Test] 106 | public void Test_Zero_Bytes() 107 | { 108 | var len = IntegrityHelper.StreamEncoderCheckCompressDecompress(new byte[0]); 109 | Assert.That(len, Is.EqualTo(0)); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/StreamHandlingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | using Force.Blazer; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace Blazer.Net.Tests 9 | { 10 | [TestFixture] 11 | public class StreamHandlingTests 12 | { 13 | [Test] 14 | public void Memory_Stream_With_Offset_Should_Be_Handled() 15 | { 16 | var memoryStream = new MemoryStream(); 17 | var blzStream = new BlazerInputStream(memoryStream, BlazerCompressionOptions.CreateStream()); 18 | var streamWriter = new StreamWriter(blzStream); 19 | streamWriter.Write("123"); 20 | streamWriter.Flush(); 21 | blzStream.Dispose(); 22 | 23 | var dstArr = new byte[1000]; 24 | var comprData = memoryStream.ToArray(); 25 | Buffer.BlockCopy(comprData, 0, dstArr, 1, comprData.Length); 26 | 27 | // such usage of MemoryStream causes Seek and Position return invalid values. As result, decompress can cause an error 28 | // we're checking that we work correctly 29 | var r = new StreamReader(new BlazerOutputStream(new MemoryStream(dstArr, 1, comprData.Length))).ReadToEnd(); 30 | Assert.That(r, Is.EqualTo("123")); 31 | 32 | Buffer.BlockCopy(comprData, 0, dstArr, 23, comprData.Length); 33 | r = new StreamReader(new BlazerOutputStream(new MemoryStream(dstArr, 23, comprData.Length))).ReadToEnd(); 34 | Assert.That(r, Is.EqualTo("123")); 35 | } 36 | 37 | [Test] 38 | public void Two_Joined_Streams_Should_Be_Handled() 39 | { 40 | var memoryStream = new MemoryStream(); 41 | var options = BlazerCompressionOptions.CreateStream(); 42 | options.LeaveStreamOpen = true; 43 | var blzStream = new BlazerInputStream(memoryStream, options); 44 | var streamWriter = new StreamWriter(blzStream); 45 | streamWriter.Write("111111111111111111111"); 46 | streamWriter.Flush(); 47 | blzStream.Dispose(); 48 | 49 | blzStream = new BlazerInputStream(memoryStream, options); 50 | streamWriter = new StreamWriter(blzStream); 51 | streamWriter.Write("22222222222222222"); 52 | streamWriter.Flush(); 53 | blzStream.Dispose(); 54 | 55 | 56 | var comprData = memoryStream.ToArray(); 57 | var readMs = new MemoryStream(comprData); 58 | 59 | // such usage of MemoryStream causes Seek and Position return invalid values. As result, decompress can cause an error 60 | // we're checking that we work correctly 61 | var r = new StreamReader(new BlazerOutputStream(readMs, new BlazerDecompressionOptions { LeaveStreamOpen = true })).ReadToEnd(); 62 | Assert.That(r, Is.EqualTo("111111111111111111111")); 63 | 64 | r = new StreamReader(new BlazerOutputStream(readMs, new BlazerDecompressionOptions { LeaveStreamOpen = true })).ReadToEnd(); 65 | Assert.That(r, Is.EqualTo("22222222222222222")); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/TcpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | 5 | namespace Blazer.Net.Tests 6 | { 7 | public static class TcpClientExtensions 8 | { 9 | #if NETCORE 10 | public static TcpClient AcceptTcpClient(this TcpListener listener) 11 | { 12 | return listener.AcceptTcpClientAsync().Result; 13 | } 14 | 15 | public static void Connect(this TcpClient client, IPEndPoint endpoint) 16 | { 17 | client.ConnectAsync(endpoint.Address, endpoint.Port).Wait(); 18 | } 19 | 20 | public static void Close(this TcpClient client) 21 | { 22 | client.Dispose(); 23 | } 24 | 25 | public static void Close(this Stream stream) 26 | { 27 | stream.Dispose(); 28 | } 29 | #endif 30 | } 31 | } -------------------------------------------------------------------------------- /Blazer.Net.Tests/TcpStreamTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | using System.Threading.Tasks; 4 | 5 | using Force.Blazer; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Blazer.Net.Tests 10 | { 11 | // in these test we ensure that all data is correctly processed with live network streams 12 | [TestFixture] 13 | public class TcpStreamTests 14 | { 15 | [Test] 16 | public void EnsureUsualStreamWriteFull() 17 | { 18 | var l = new TcpListener(new IPEndPoint(IPAddress.Loopback, 9990)); 19 | l.Start(); 20 | try 21 | { 22 | Task.Factory.StartNew( 23 | () => 24 | { 25 | var cl = l.AcceptTcpClient(); 26 | cl.GetStream().Write(new byte[] { 0x1, 0x2, 0x3 }, 0, 3); 27 | cl.Close(); 28 | }); 29 | 30 | var c = new TcpClient(); 31 | c.Connect(new IPEndPoint(IPAddress.Loopback, 9990)); 32 | var stream = c.GetStream(); 33 | var buf = new byte[100]; 34 | Assert.That(stream.Read(buf, 0, buf.Length), Is.EqualTo(3)); 35 | Assert.That(buf[1], Is.EqualTo(2)); 36 | } 37 | finally 38 | { 39 | l.Stop(); 40 | } 41 | } 42 | 43 | [Test] 44 | public void EnsureBlazerStreamWriteFull() 45 | { 46 | var l = new TcpListener(new IPEndPoint(IPAddress.Loopback, 9990)); 47 | l.Start(); 48 | try 49 | { 50 | Task.Factory.StartNew( 51 | () => 52 | { 53 | var cl = l.AcceptTcpClient(); 54 | var networkStream = new BlazerInputStream(cl.GetStream(), BlazerCompressionOptions.CreateStream()); 55 | networkStream.Write(new byte[] { 0x1, 0x2, 0x3 }, 0, 3); 56 | networkStream.Close(); 57 | cl.Close(); 58 | }); 59 | 60 | var c = new TcpClient(); 61 | c.Connect(new IPEndPoint(IPAddress.Loopback, 9990)); 62 | var stream = new BlazerOutputStream(c.GetStream()); 63 | var buf = new byte[100]; 64 | Assert.That(stream.Read(buf, 0, buf.Length), Is.EqualTo(3)); 65 | Assert.That(buf[1], Is.EqualTo(2)); 66 | } 67 | finally 68 | { 69 | l.Stop(); 70 | } 71 | } 72 | 73 | [Test] 74 | public void EnsureBlazerStreamWriteFull_ControlBlock() 75 | { 76 | var l = new TcpListener(new IPEndPoint(IPAddress.Loopback, 9990)); 77 | l.Start(); 78 | try 79 | { 80 | Task.Factory.StartNew( 81 | () => 82 | { 83 | var cl = l.AcceptTcpClient(); 84 | var str = cl.GetStream(); 85 | var networkStream = new BlazerInputStream(str, BlazerCompressionOptions.CreateStream()); 86 | var b = new byte[80]; 87 | b[1] = 0x2; 88 | networkStream.WriteControlData(b, 0, b.Length); 89 | networkStream.Flush(); 90 | networkStream.Close(); 91 | cl.Close(); 92 | }); 93 | 94 | var c = new TcpClient(); 95 | c.Connect(new IPEndPoint(IPAddress.Loopback, 9990)); 96 | var readedCnt = 0; 97 | var stream = new BlazerOutputStream(c.GetStream(), new BlazerDecompressionOptions() { ControlDataCallback = (bytes, i, arg3) => readedCnt = arg3 }); 98 | var buf = new byte[100]; 99 | Assert.That(stream.Read(buf, 0, buf.Length), Is.EqualTo(0)); 100 | Assert.That(readedCnt, Is.EqualTo(80)); 101 | } 102 | finally 103 | { 104 | l.Stop(); 105 | } 106 | } 107 | 108 | [Test] 109 | public void EnsureBlazerStreamWriteFull_EncryptFull() 110 | { 111 | var l = new TcpListener(new IPEndPoint(IPAddress.Loopback, 9990)); 112 | l.Start(); 113 | try 114 | { 115 | Task.Factory.StartNew( 116 | () => 117 | { 118 | var cl = l.AcceptTcpClient(); 119 | var opt = BlazerCompressionOptions.CreateStream(); 120 | opt.Comment = "test"; 121 | var networkStream = new BlazerInputStream(cl.GetStream(), opt); 122 | networkStream.WriteControlData(new byte[0], 0, 0); 123 | networkStream.Close(); 124 | cl.Close(); 125 | }); 126 | 127 | var c = new TcpClient(); 128 | c.Connect(new IPEndPoint(IPAddress.Loopback, 9990)); 129 | var stream = new BlazerOutputStream(c.GetStream(), new BlazerDecompressionOptions()); 130 | var buf = new byte[100]; 131 | Assert.That(stream.Read(buf, 0, buf.Length), Is.EqualTo(0)); 132 | Assert.That(stream.Comment, Is.EqualTo("test")); 133 | } 134 | finally 135 | { 136 | l.Stop(); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Blazer.Net.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "buildOptions": { 4 | "debugType": "portable", 5 | "allowUnsafe": true 6 | }, 7 | "dependencies": { 8 | "Blazer.Net": "0.10.0-*", 9 | "Crc32.NET": "1.1.0", 10 | "NUnit": "3.6.0" 11 | }, 12 | 13 | "testRunner": "nunit", 14 | 15 | "frameworks": { 16 | "net461": { 17 | "dependencies": { 18 | } 19 | }, 20 | "netcoreapp1.0": { 21 | "imports": "portable-net45+win8", 22 | "buildOptions": { 23 | "define": ["NETCORE"] 24 | }, 25 | "dependencies": { 26 | "System.Text.Encoding.CodePages": "4.3.0", 27 | "NETStandard.Library": "1.6.1", 28 | "dotnet-test-nunit": "3.4.0-beta-3", 29 | "Microsoft.NETCore.App": { 30 | "version": "1.1.0-*", 31 | "type": "platform" 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Blazer.Net/.gitignore: -------------------------------------------------------------------------------- 1 | _releases -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/BlockDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Force.Blazer.Algorithms 5 | { 6 | /// 7 | /// Decoder of block version of Blazer algorithm 8 | /// 9 | /// This version provides relative good and fast compression but decompression rate is same as compression 10 | public class BlockDecoder : IDecoder 11 | { 12 | // should be equal with BlockEncoder 13 | private const int HASH_TABLE_BITS = 16; 14 | 15 | /// 16 | /// Length of hashtable - 1 17 | /// 18 | protected const int HASH_TABLE_LEN = (1 << HASH_TABLE_BITS) - 1; 19 | private const int MIN_SEQ_LEN = 4; 20 | // carefully selected random number 21 | private const uint Mul = 1527631329; 22 | 23 | private byte[] _innerBuffer; 24 | 25 | private int _maxUncompressedBlockSize; 26 | 27 | /// 28 | /// Hash array to store dictionary between iterations 29 | /// 30 | [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1304:NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")] 31 | protected readonly int[] _hashArr = new int[HASH_TABLE_LEN + 1]; 32 | 33 | /// 34 | /// Returns internal hash array 35 | /// 36 | public int[] HashArr 37 | { 38 | get 39 | { 40 | return _hashArr; 41 | } 42 | } 43 | 44 | /// 45 | /// Decodes given buffer 46 | /// 47 | public BufferInfo Decode(byte[] buffer, int offset, int length, bool isCompressed) 48 | { 49 | if (!isCompressed) 50 | return new BufferInfo(buffer, offset, length); 51 | 52 | var outLen = DecompressBlock(buffer, offset, length, _innerBuffer, 0, _maxUncompressedBlockSize, true); 53 | return new BufferInfo(_innerBuffer, 0, outLen); 54 | } 55 | 56 | /// 57 | /// Initializes decoder with information about maximum uncompressed block size 58 | /// 59 | public virtual void Init(int maxUncompressedBlockSize) 60 | { 61 | _innerBuffer = new byte[maxUncompressedBlockSize]; 62 | _maxUncompressedBlockSize = maxUncompressedBlockSize; 63 | } 64 | 65 | /// 66 | /// Returns algorithm id 67 | /// 68 | public BlazerAlgorithm GetAlgorithmId() 69 | { 70 | return BlazerAlgorithm.Block; 71 | } 72 | 73 | /// 74 | /// Decompresses block of data 75 | /// 76 | public virtual int DecompressBlock( 77 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength, bool doCleanup) 78 | { 79 | var cnt = DecompressBlockExternal(bufferIn, bufferInOffset, bufferInLength, bufferOut, bufferOutOffset, bufferOutLength, _hashArr); 80 | if (doCleanup) 81 | Array.Clear(_hashArr, 0, HASH_TABLE_LEN + 1); 82 | 83 | return cnt; 84 | } 85 | 86 | /// 87 | /// Decompresses block of data, can be used independently for byte arrays 88 | /// 89 | /// In buffer 90 | /// In buffer offset 91 | /// In buffer right offset (offset + count) 92 | /// Out buffer, should be enough size 93 | /// Out buffer offset 94 | /// Out buffer maximum right offset (offset + count) 95 | /// Hash array. Can be null. 96 | /// Bytes count of decompressed data 97 | public static int DecompressBlockExternal(byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength, int[] hashArr) 98 | { 99 | hashArr = hashArr ?? new int[HASH_TABLE_LEN + 1]; 100 | var idxIn = bufferInOffset; 101 | var idxOut = bufferOutOffset; 102 | uint mulEl = 0; 103 | 104 | while (idxIn < bufferInLength) 105 | { 106 | var elem = bufferIn[idxIn++]; 107 | 108 | var seqCntFirst = elem & 0xf; 109 | var litCntFirst = (elem >> 4) & 7; 110 | 111 | var litCnt = litCntFirst; 112 | int seqCnt; 113 | var backRef = 0; 114 | var hashIdx = -1; 115 | 116 | if (elem >= 128) 117 | { 118 | hashIdx = bufferIn[idxIn++] | (bufferIn[idxIn++] << 8); 119 | seqCnt = seqCntFirst + MIN_SEQ_LEN/* + 1*/; 120 | if (hashIdx == 0xffff) 121 | { 122 | seqCnt = 0; 123 | seqCntFirst = 0; 124 | litCnt = elem - 128; 125 | litCntFirst = litCnt == 127 ? 7 : 0; 126 | } 127 | } 128 | else 129 | { 130 | backRef = bufferIn[idxIn++] + 1; 131 | seqCnt = seqCntFirst + MIN_SEQ_LEN; 132 | } 133 | 134 | if (litCntFirst == 7) 135 | { 136 | var litCntR = bufferIn[idxIn++]; 137 | if (litCntR < 253) litCnt += litCntR; 138 | else if (litCntR == 253) 139 | litCnt += 253 + bufferIn[idxIn++]; 140 | else if (litCntR == 254) 141 | litCnt += 253 + 256 + bufferIn[idxIn++] + (bufferIn[idxIn++] << 8); 142 | else 143 | litCnt += 253 + (256 * 256) + bufferIn[idxIn++] + (bufferIn[idxIn++] << 8) + (bufferIn[idxIn++] << 16) + (bufferIn[idxIn++] << 24); 144 | } 145 | 146 | if (seqCntFirst == 15) 147 | { 148 | var seqCntR = bufferIn[idxIn++]; 149 | if (seqCntR < 253) seqCnt += seqCntR; 150 | else if (seqCntR == 253) 151 | seqCnt += 253 + bufferIn[idxIn++]; 152 | else if (seqCntR == 254) 153 | seqCnt += 253 + 256 + bufferIn[idxIn++] + (bufferIn[idxIn++] << 8); 154 | else 155 | seqCnt += 253 + (256 * 256) + bufferIn[idxIn++] + (bufferIn[idxIn++] << 8) + (bufferIn[idxIn++] << 16) + (bufferIn[idxIn++] << 24); 156 | } 157 | 158 | var maxOutLength = idxOut + litCnt + seqCnt; 159 | if (maxOutLength > bufferOutLength) 160 | { 161 | throw new InvalidOperationException("Very small inner buffer. Invalid configuration or stream."); 162 | } 163 | 164 | while (--litCnt >= 0) 165 | { 166 | var v = bufferIn[idxIn++]; 167 | mulEl = (mulEl << 8) | v; 168 | var hashKey = (mulEl * Mul) >> (32 - HASH_TABLE_BITS); 169 | hashArr[hashKey] = idxOut; 170 | bufferOut[idxOut++] = v; 171 | } 172 | 173 | var inRepIdx = hashIdx >= 0 ? hashArr[hashIdx] - 3 : idxOut - backRef; 174 | 175 | while (--seqCnt >= 0) 176 | { 177 | var v = bufferOut[inRepIdx++]; 178 | mulEl = (mulEl << 8) | v; 179 | 180 | hashArr[(mulEl * Mul) >> (32 - HASH_TABLE_BITS)] = idxOut; 181 | 182 | bufferOut[idxOut++] = v; 183 | } 184 | } 185 | 186 | return idxOut; 187 | } 188 | 189 | /// 190 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 191 | /// 192 | /// 2 193 | public virtual void Dispose() 194 | { 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/BlockDecoderNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Force.Blazer.Algorithms 5 | { 6 | /// 7 | /// Native implementation of decoder of block version of Blazer algorithm 8 | /// 9 | /// This version provides relative good and fast compression but decompression rate is same as compression 10 | public class BlockDecoderNative : BlockDecoder 11 | { 12 | [DllImport(@"Blazer.Native.dll", CallingConvention = CallingConvention.Cdecl)] 13 | private static extern int blazer_block_decompress_block( 14 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength, int[] hashArr); 15 | 16 | /// 17 | /// Initializes encoder with information about maximum uncompressed block size 18 | /// 19 | public override void Init(int maxInBlockSize) 20 | { 21 | base.Init(maxInBlockSize); 22 | } 23 | 24 | /// 25 | /// Decompresses block of data 26 | /// 27 | public override int DecompressBlock( 28 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int idxOut, int bufferOutLength, bool doCleanup) 29 | { 30 | var res = blazer_block_decompress_block(bufferIn, bufferInOffset, bufferInLength, bufferOut, idxOut, bufferOutLength, _hashArr); 31 | if (doCleanup) 32 | Array.Clear(_hashArr, 0, HASH_TABLE_LEN + 1); 33 | 34 | if (res < 0) 35 | throw new InvalidOperationException("Invalid compressed data"); 36 | return res; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/BlockEncoderNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Force.Blazer.Algorithms 5 | { 6 | /// 7 | /// Native implementation of encoder of block version of Blazer algorithm 8 | /// 9 | /// This version provides relative good and fast compression but decompression rate is same as compression 10 | public class BlockEncoderNative : BlockEncoder 11 | { 12 | [DllImport(@"Blazer.Native.dll", CallingConvention = CallingConvention.Cdecl)] 13 | private static extern int blazer_block_compress_block( 14 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int[] hashArr); 15 | 16 | /// 17 | /// Compresses block of data 18 | /// 19 | public override int CompressBlock( 20 | byte[] bufferIn, 21 | int bufferInOffset, 22 | int bufferInCount, 23 | byte[] bufferOut, 24 | int bufferOutOffset, 25 | bool doCleanup) 26 | { 27 | var cnt = blazer_block_compress_block( 28 | bufferIn, 29 | bufferInOffset, 30 | bufferInCount, 31 | bufferOut, 32 | bufferOutOffset, 33 | _hashArr); 34 | 35 | if (doCleanup) 36 | Array.Clear(_hashArr, 0, HASH_TABLE_LEN + 1); 37 | 38 | return cnt; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/BufferInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms 2 | { 3 | /// 4 | /// Information about data buffer 5 | /// 6 | public struct BufferInfo 7 | { 8 | /// 9 | /// Data buffer 10 | /// 11 | public byte[] Buffer; 12 | 13 | /// 14 | /// Buffer offset 15 | /// 16 | public int Offset; 17 | 18 | /// 19 | /// Buffer data length (Offset + Count, right position of valid data in buffer) 20 | /// 21 | public int Length; 22 | 23 | /// 24 | /// Count of valid data in buffer) 25 | /// 26 | public int Count 27 | { 28 | get 29 | { 30 | return Length - Offset; 31 | } 32 | } 33 | 34 | /// 35 | /// BufferInfo constructor 36 | /// 37 | public BufferInfo(byte[] buffer, int offset, int length) 38 | { 39 | Buffer = buffer; 40 | Offset = offset; 41 | Length = length; 42 | } 43 | 44 | /// 45 | /// Extracts body to separate byte array 46 | /// 47 | /// new array 48 | public byte[] ExtractToSeparateArray() 49 | { 50 | return ExtractToSeparateArray(0); 51 | } 52 | 53 | /// 54 | /// Extracts body to separate byte array 55 | /// 56 | /// additional offset for new array 57 | /// new array 58 | public byte[] ExtractToSeparateArray(int offset) 59 | { 60 | var res = new byte[Count + offset]; 61 | System.Buffer.BlockCopy(Buffer, Offset, res, offset, Count); 62 | return res; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Crc32C/Crc32C.cs: -------------------------------------------------------------------------------- 1 | using Force.Blazer.Native; 2 | 3 | namespace Force.Blazer.Algorithms.Crc32C 4 | { 5 | /// 6 | /// Crc32C (Castagnoli) checksum implementation 7 | /// 8 | public static class Crc32C 9 | { 10 | private static readonly ICrc32CCalculator _calculator; 11 | 12 | static Crc32C() 13 | { 14 | _calculator = NativeHelper.IsNativeAvailable ? (ICrc32CCalculator)new Crc32CHardware() : new Crc32CSoftware(); 15 | } 16 | 17 | /// 18 | /// Calculates Crc32C data of given buffer 19 | /// 20 | public static uint Calculate(byte[] buffer, int offset, int count) 21 | { 22 | return Calculate(0, buffer, offset, count); 23 | } 24 | 25 | /// 26 | /// Calculates Crc32C data of given buffer, updates existing crc 27 | /// 28 | public static uint Calculate(uint currentCrc, byte[] buffer) 29 | { 30 | return Calculate(currentCrc, buffer, 0, buffer.Length); 31 | } 32 | 33 | /// 34 | /// Calculates Crc32C data of given buffer, updates existing crc 35 | /// 36 | public static uint Calculate(uint currentCrc, byte[] buffer, int offset, int count) 37 | { 38 | return _calculator.Calculate(currentCrc, buffer, offset, count); 39 | } 40 | 41 | /// 42 | /// Calculates Crc32C data of given buffer 43 | /// 44 | public static uint Calculate(byte[] buffer) 45 | { 46 | return Calculate(0, buffer, 0, buffer.Length); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Crc32C/Crc32CHardware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | using Force.Blazer.Native; 5 | 6 | namespace Force.Blazer.Algorithms.Crc32C 7 | { 8 | /// 9 | /// Native (hardware if available) Crc32C calculator. Do not use it directly, instead of use class 10 | /// 11 | public class Crc32CHardware : ICrc32CCalculator 12 | { 13 | [DllImport(@"Blazer.Native.dll", CallingConvention = CallingConvention.Cdecl)] 14 | private static extern uint crc32c_append(uint crc, IntPtr buffer, int length); 15 | 16 | /// 17 | /// Constructor, will throw exception if it impossible to use hardware implementation 18 | /// 19 | public Crc32CHardware() 20 | { 21 | if (!NativeHelper.IsNativeAvailable) 22 | throw new InvalidOperationException("You have no right for hardware implementation"); 23 | } 24 | 25 | uint ICrc32CCalculator.Calculate(uint crc, byte[] buffer, int offset, int count) 26 | { 27 | var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 28 | var res = crc32c_append(crc, handle.AddrOfPinnedObject() + offset, count); 29 | handle.Free(); 30 | return res; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Crc32C/Crc32CSoftware.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms.Crc32C 2 | { 3 | /// 4 | /// Software managed Crc32C calculator 5 | /// 6 | public class Crc32CSoftware : ICrc32CCalculator 7 | { 8 | private static readonly uint[] _table = new uint[16 * 256]; 9 | 10 | static Crc32CSoftware() 11 | { 12 | const uint Poly = 0x82f63b78; 13 | for (uint i = 0; i < 256; i++) 14 | { 15 | uint res = i; 16 | for (int t = 0; t < 16; t++) 17 | { 18 | for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1); 19 | _table[(t * 256) + i] = res; 20 | } 21 | } 22 | } 23 | 24 | uint ICrc32CCalculator.Calculate(uint crc, byte[] input, int offset, int length) 25 | { 26 | uint crcLocal = uint.MaxValue ^ crc; 27 | 28 | uint[] table = _table; 29 | while (length >= 16) 30 | { 31 | var a = table[(3 * 256) + input[offset + 12]] 32 | ^ table[(2 * 256) + input[offset + 13]] 33 | ^ table[(1 * 256) + input[offset + 14]] 34 | ^ table[(0 * 256) + input[offset + 15]]; 35 | 36 | var b = table[(7 * 256) + input[offset + 8]] 37 | ^ table[(6 * 256) + input[offset + 9]] 38 | ^ table[(5 * 256) + input[offset + 10]] 39 | ^ table[(4 * 256) + input[offset + 11]]; 40 | 41 | var c = table[(11 * 256) + input[offset + 4]] 42 | ^ table[(10 * 256) + input[offset + 5]] 43 | ^ table[(9 * 256) + input[offset + 6]] 44 | ^ table[(8 * 256) + input[offset + 7]]; 45 | 46 | var d = table[(15 * 256) + ((crcLocal ^ input[offset]) & 0xff)] 47 | ^ table[(14 * 256) + (((crcLocal >> 8) ^ input[offset + 1]) & 0xff)] 48 | ^ table[(13 * 256) + (((crcLocal >> 16) ^ input[offset + 2]) & 0xff)] 49 | ^ table[(12 * 256) + (((crcLocal >> 24) ^ input[offset + 3]) & 0xff)]; 50 | 51 | crcLocal = d ^ c ^ b ^ a; 52 | offset += 16; 53 | length -= 16; 54 | } 55 | 56 | while (--length >= 0) 57 | crcLocal = table[(crcLocal ^ input[offset++]) & 0xff] ^ crcLocal >> 8; 58 | 59 | return crcLocal ^ uint.MaxValue; 60 | 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Crc32C/ICrc32CCalculator.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms.Crc32C 2 | { 3 | /// 4 | /// Interface for Crc32 calculators 5 | /// 6 | public interface ICrc32CCalculator 7 | { 8 | /// 9 | /// Calculates Crc32C data for buffer 10 | /// 11 | uint Calculate(uint crc, byte[] buffer, int offset, int count); 12 | } 13 | } -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/EncoderDecoderFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Force.Blazer.Native; 4 | 5 | namespace Force.Blazer.Algorithms 6 | { 7 | /// 8 | /// Factory for creating default encoders and decoders for Blazer algorithmns 9 | /// 10 | public class EncoderDecoderFactory 11 | { 12 | /// 13 | /// Returns decoder for algorithm 14 | /// 15 | public static IDecoder GetDecoder(BlazerAlgorithm algorithm) 16 | { 17 | switch (algorithm) 18 | { 19 | case BlazerAlgorithm.NoCompress: return new NoCompressionDecoder(); 20 | case BlazerAlgorithm.Stream: return NativeHelper.IsNativeAvailable ? new StreamDecoderNative() : new StreamDecoder(); 21 | case BlazerAlgorithm.Block: return NativeHelper.IsNativeAvailable ? new BlockDecoderNative() : new BlockDecoder(); 22 | default: throw new NotImplementedException("Not supported algorithm: " + algorithm); 23 | } 24 | } 25 | 26 | /// 27 | /// Returns encoder for algorithm 28 | /// 29 | public static IEncoder GetEncoder(BlazerAlgorithm algorithm) 30 | { 31 | switch (algorithm) 32 | { 33 | case BlazerAlgorithm.NoCompress: return new NoCompressionEncoder(); 34 | case BlazerAlgorithm.Stream: return NativeHelper.IsNativeAvailable ? new StreamEncoderNative() : new StreamEncoder(); 35 | case BlazerAlgorithm.Block: return NativeHelper.IsNativeAvailable ? new BlockEncoderNative() : new BlockEncoder(); 36 | default: throw new NotImplementedException("Not supported algorithm: " + algorithm); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/IDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Algorithms 4 | { 5 | /// 6 | /// Inteface for implementing decoders for Blazer algoritms 7 | /// 8 | public interface IDecoder : IDisposable 9 | { 10 | /// 11 | /// Decodes given buffer 12 | /// 13 | BufferInfo Decode(byte[] buffer, int offset, int length, bool isCompressed); 14 | 15 | /// 16 | /// Initializes decoder with information about maximum uncompressed block size 17 | /// 18 | void Init(int maxUncompressedBlockSize); 19 | 20 | /// 21 | /// Returns algorithm id 22 | /// 23 | BlazerAlgorithm GetAlgorithmId(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/IEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Algorithms 4 | { 5 | /// 6 | /// Inteface for implementing encoders for Blazer algoritms 7 | /// 8 | public interface IEncoder : IDisposable 9 | { 10 | /// 11 | /// Encodes given buffer 12 | /// 13 | BufferInfo Encode(byte[] buffer, int offset, int length); 14 | 15 | /// 16 | /// Initializes encoder with information about maximum uncompressed block size 17 | /// 18 | void Init(int maxInBlockSize); 19 | 20 | /// 21 | /// Returns algorithm id 22 | /// 23 | BlazerAlgorithm GetAlgorithmId(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/NoCompressionDecoder.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms 2 | { 3 | /// 4 | /// Decoder of no compression version of Blazer algorithm 5 | /// 6 | /// This is dummy decoder can be used for testing or storing data with Blazer structure 7 | public class NoCompressionDecoder : IDecoder 8 | { 9 | /// 10 | /// Decodes given buffer 11 | /// 12 | public BufferInfo Decode(byte[] buffer, int offset, int length, bool isCompressed) 13 | { 14 | return new BufferInfo(buffer, offset, length); 15 | } 16 | 17 | /// 18 | /// Initializes decoder with information about maximum uncompressed block size 19 | /// 20 | public void Init(int maxUncompressedBlockSize) 21 | { 22 | } 23 | 24 | /// 25 | /// Returns algorithm id 26 | /// 27 | public BlazerAlgorithm GetAlgorithmId() 28 | { 29 | return BlazerAlgorithm.NoCompress; 30 | } 31 | 32 | /// 33 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 34 | /// 35 | /// 2 36 | public void Dispose() 37 | { 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/NoCompressionEncoder.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms 2 | { 3 | /// 4 | /// Encoder of no compression version of Blazer algorithm 5 | /// 6 | /// This is dummy decoder can be used for testing or storing data with Blazer structure 7 | public class NoCompressionEncoder : IEncoder 8 | { 9 | /// 10 | /// Encodes given buffer 11 | /// 12 | public BufferInfo Encode(byte[] buffer, int offset, int length) 13 | { 14 | return new BufferInfo(buffer, offset, length); 15 | } 16 | 17 | /// 18 | /// Initializes encoder with information about maximum uncompressed block size 19 | /// 20 | public void Init(int maxInBlockSize) 21 | { 22 | } 23 | 24 | /// 25 | /// Returns algorithm id 26 | /// 27 | public BlazerAlgorithm GetAlgorithmId() 28 | { 29 | return BlazerAlgorithm.NoCompress; 30 | } 31 | 32 | /// 33 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 34 | /// 35 | /// 2 36 | public void Dispose() 37 | { 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Patterned/BasePatternedCompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Threading; 4 | 5 | namespace Force.Blazer.Algorithms.Patterned 6 | { 7 | /// 8 | /// Base implementation of Patterned Compressor/Decompressor 9 | /// 10 | public abstract class BasePatternedCompressor : IPatternedCompressor 11 | { 12 | /// 13 | /// Calculates max compressed buffer size for specified uncompressed data length 14 | /// 15 | public abstract int CalculateMaxCompressedBufferLength(int uncompressedLength); 16 | 17 | /// 18 | /// Initializes internal hash array 19 | /// 20 | protected abstract void InitHashArray(); 21 | 22 | /// 23 | /// Restores internal hash array 24 | /// 25 | protected abstract void RestoreHashArray(); 26 | 27 | /// 28 | /// Returns algorithm id 29 | /// 30 | protected abstract byte GetAlgorithmId(); 31 | 32 | /// 33 | /// Inner buffer 34 | /// 35 | [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed. Suppression is OK here.")] 36 | protected byte[] _innerBuffer; 37 | 38 | /// 39 | /// Length of pattern 40 | /// 41 | [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed. Suppression is OK here.")] 42 | protected int _patternLength; 43 | 44 | /// 45 | /// Compress block of data 46 | /// 47 | protected abstract int CompressBlock(int countIn, byte[] bufferOut, int offsetOut); 48 | 49 | /// 50 | /// Prepares pattern. Should be called only once for one pattern 51 | /// 52 | public void PreparePattern(byte[] pattern, int offset, int count) 53 | { 54 | if (count <= 0) 55 | throw new InvalidOperationException("Invalid pattern length"); 56 | var patternLength = count; 57 | _innerBuffer = new byte[patternLength * 2]; 58 | var outerBuffer = new byte[CalculateMaxCompressedBufferLength(patternLength) + 1]; 59 | 60 | Buffer.BlockCopy(pattern, offset, _innerBuffer, 0, patternLength); 61 | CompressBlock(patternLength, outerBuffer, 0); 62 | _patternLength = patternLength; 63 | InitHashArray(); 64 | } 65 | 66 | /// 67 | /// Prepares pattern. Should be called only once for one pattern 68 | /// 69 | public void PreparePattern(byte[] pattern) 70 | { 71 | PreparePattern(pattern, 0, pattern.Length); 72 | } 73 | 74 | private int _isWorking; 75 | 76 | /// 77 | /// Encodes data with prepared pattern 78 | /// 79 | public int EncodeWithPattern(byte[] bufferIn, int offsetIn, int countIn, byte[] bufferOut, int offsetOut) 80 | { 81 | if (Interlocked.CompareExchange(ref _isWorking, 1, 0) != 0) 82 | throw new InvalidOperationException("Method is not thread safe "); 83 | 84 | try 85 | { 86 | var totalInLength = countIn + _patternLength; 87 | if (totalInLength > _innerBuffer.Length) 88 | { 89 | var oldInnerBuffer = _innerBuffer; 90 | _innerBuffer = new byte[totalInLength + 128]; 91 | Buffer.BlockCopy(oldInnerBuffer, 0, _innerBuffer, 0, _patternLength); 92 | } 93 | 94 | Buffer.BlockCopy(bufferIn, offsetIn, _innerBuffer, _patternLength, countIn); 95 | 96 | if (bufferOut.Length - offsetOut < CalculateMaxCompressedBufferLength(countIn)) 97 | throw new InvalidOperationException("Out buffer too small"); 98 | 99 | var len = CompressBlock(countIn, bufferOut, offsetOut); 100 | var writtenCnt = len - offsetOut - 1; 101 | writtenCnt >>= 16; 102 | var lenCnt = 0; 103 | while (writtenCnt > 0) 104 | { 105 | lenCnt++; 106 | writtenCnt >>= 1; 107 | } 108 | 109 | // writing some metainfo to out buffer, to allow decode and validate data 110 | bufferOut[offsetOut] = (byte)(lenCnt | (GetAlgorithmId() << 4)); 111 | 112 | RestoreHashArray(); 113 | return len - offsetOut; 114 | } 115 | finally 116 | { 117 | Interlocked.Exchange(ref _isWorking, 0); 118 | } 119 | } 120 | 121 | /// 122 | /// Decompress block of data 123 | /// 124 | protected abstract int DecompressBlock(byte[] bufferIn, int offsetIn, int countIn); 125 | 126 | /// 127 | /// Decodes data with prepared pattern 128 | /// 129 | public int DecodeWithPattern(byte[] bufferIn, int offsetIn, int countIn, byte[] bufferOut, int offsetOut) 130 | { 131 | if (Interlocked.CompareExchange(ref _isWorking, 1, 0) != 0) 132 | throw new InvalidOperationException("Method is not thread safe "); 133 | 134 | try 135 | { 136 | var algFlag = bufferIn[offsetIn]; 137 | var algorithm = algFlag >> 4; 138 | if (algorithm != GetAlgorithmId()) 139 | throw new InvalidOperationException("Encoded data is not patterned data"); 140 | var maxOutLength = ((algFlag + 1) & 0xf) << 16; 141 | 142 | var totalOutLength = _patternLength + maxOutLength; 143 | if (totalOutLength > _innerBuffer.Length) 144 | { 145 | var oldInnerBuffer = _innerBuffer; 146 | _innerBuffer = new byte[totalOutLength + 128]; 147 | Buffer.BlockCopy(oldInnerBuffer, 0, _innerBuffer, 0, _patternLength); 148 | } 149 | 150 | var res = DecompressBlock(bufferIn, offsetIn, countIn); 151 | if (offsetOut + res - _patternLength > bufferOut.Length) 152 | throw new InvalidOperationException("Out buffer too small"); 153 | Buffer.BlockCopy(_innerBuffer, _patternLength, bufferOut, offsetOut, res - _patternLength); 154 | return res - _patternLength; 155 | } 156 | finally 157 | { 158 | Interlocked.Exchange(ref _isWorking, 0); 159 | } 160 | } 161 | 162 | /// 163 | /// Encodes data with prepared pattern 164 | /// 165 | public byte[] EncodeWithPattern(byte[] buffer, int offset, int count) 166 | { 167 | var bufferOut = new byte[CalculateMaxCompressedBufferLength(count)]; 168 | var cnt = EncodeWithPattern(buffer, offset, count, bufferOut, 0); 169 | Array.Resize(ref bufferOut, cnt); 170 | return bufferOut; 171 | } 172 | 173 | /// 174 | /// Encodes data with prepared pattern 175 | /// 176 | public byte[] EncodeWithPattern(byte[] buffer) 177 | { 178 | return EncodeWithPattern(buffer, 0, buffer.Length); 179 | } 180 | 181 | /// 182 | /// Decodes data with prepared pattern 183 | /// 184 | public byte[] DecodeWithPattern(byte[] buffer, int offset, int count) 185 | { 186 | var algFlag = buffer[offset]; 187 | var maxOutLength = ((algFlag + 1) & 0xf) << 16; 188 | 189 | var bufferOut = new byte[maxOutLength]; 190 | var cnt = DecodeWithPattern(buffer, offset, count, bufferOut, 0); 191 | Array.Resize(ref bufferOut, cnt); 192 | return bufferOut; 193 | } 194 | 195 | /// 196 | /// Decodes data with prepared pattern 197 | /// 198 | public byte[] DecodeWithPattern(byte[] buffer) 199 | { 200 | return DecodeWithPattern(buffer, 0, buffer.Length); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Patterned/BlockPatternedCompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Algorithms.Patterned 4 | { 5 | /// 6 | /// Patterned Compressor/Decompressor for Blazer Block algorithm 7 | /// 8 | public class BlockPatternedCompressor : BasePatternedCompressor 9 | { 10 | private readonly BlockEncoder _encoder; 11 | 12 | private readonly BlockDecoder _decoder; 13 | 14 | private int[] _hashArrClone; 15 | 16 | /// 17 | /// Initializes patterned compressor 18 | /// 19 | public BlockPatternedCompressor() 20 | { 21 | _encoder = (BlockEncoder)EncoderDecoderFactory.GetEncoder(BlazerAlgorithm.Block); 22 | _decoder = (BlockDecoder)EncoderDecoderFactory.GetDecoder(BlazerAlgorithm.Block); 23 | } 24 | 25 | /// 26 | /// Calculates max compressed buffer size for specified uncompressed data length 27 | /// 28 | public override int CalculateMaxCompressedBufferLength(int uncompressedLength) 29 | { 30 | return uncompressedLength + (uncompressedLength >> 8) + 3 + /*_encoder.GetAdditionalInSize()*/ + 1; 31 | } 32 | 33 | /// 34 | /// Initializes HashArray for Encoder 35 | /// 36 | protected override void InitHashArray() 37 | { 38 | _hashArrClone = new int[_encoder.HashArr.Length]; 39 | Array.Copy(_encoder.HashArr, 0, _hashArrClone, 0, _hashArrClone.Length); 40 | } 41 | 42 | /// 43 | /// Restores HashArray for Encoder 44 | /// 45 | protected override void RestoreHashArray() 46 | { 47 | Array.Copy(_hashArrClone, 0, _encoder.HashArr, 0, _hashArrClone.Length); 48 | } 49 | 50 | /// 51 | /// Returns algorithm id 52 | /// 53 | protected override byte GetAlgorithmId() 54 | { 55 | return (byte)_encoder.GetAlgorithmId(); 56 | } 57 | 58 | /// 59 | /// Compress block of data 60 | /// 61 | protected override int CompressBlock(int countIn, byte[] bufferOut, int offsetOut) 62 | { 63 | return _encoder.CompressBlock(_innerBuffer, _patternLength, _patternLength + countIn, bufferOut, offsetOut + 1, false); 64 | } 65 | 66 | /// 67 | /// Decompress block of data 68 | /// 69 | protected override int DecompressBlock(byte[] bufferIn, int offsetIn, int countIn) 70 | { 71 | var res = _decoder.DecompressBlock(bufferIn, offsetIn + 1, offsetIn + countIn, _innerBuffer, _patternLength, _innerBuffer.Length, false); 72 | RestoreHashArray(); 73 | return res; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Patterned/IPatternedCompressor.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer.Algorithms.Patterned 2 | { 3 | /// 4 | /// Common interface for Pattened Encoder/Decoder 5 | /// 6 | public interface IPatternedCompressor 7 | { 8 | /// 9 | /// Calculates max compressed buffer size for specified uncompressed data length 10 | /// 11 | int CalculateMaxCompressedBufferLength(int uncompressedLength); 12 | 13 | /// 14 | /// Prepares pattern. Should be called only once for pattern 15 | /// 16 | void PreparePattern(byte[] pattern); 17 | 18 | /// 19 | /// Prepares pattern. Should be called only once for pattern 20 | /// 21 | void PreparePattern(byte[] pattern, int offset, int length); 22 | 23 | /// 24 | /// Encodes data with prepared pattern 25 | /// 26 | int EncodeWithPattern(byte[] bufferIn, int offsetIn, int countIn, byte[] bufferOut, int offsetOut); 27 | 28 | /// 29 | /// Decodes data with prepared pattern 30 | /// 31 | int DecodeWithPattern(byte[] bufferIn, int offsetIn, int countIn, byte[] bufferOut, int offsetOut); 32 | 33 | /// 34 | /// Encodes data with prepared pattern 35 | /// 36 | byte[] EncodeWithPattern(byte[] buffer, int offset, int count); 37 | 38 | /// 39 | /// Encodes data with prepared pattern 40 | /// 41 | byte[] EncodeWithPattern(byte[] buffer); 42 | 43 | /// 44 | /// Decodes data with prepared pattern 45 | /// 46 | byte[] DecodeWithPattern(byte[] buffer, int offset, int count); 47 | 48 | /// 49 | /// Decodes data with prepared pattern 50 | /// 51 | byte[] DecodeWithPattern(byte[] buffer); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Patterned/StreamHighPatternedCompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Algorithms.Patterned 4 | { 5 | /// 6 | /// Patterned Compressor/Decompressor for Blazer Stream High algorithm 7 | /// 8 | /// Method is very slow to use in normal situations. Use only when needed in your specific case 9 | public class StreamHighPatternedCompressor : StreamPatternedCompressor 10 | { 11 | private readonly StreamEncoderHigh _encoder; 12 | 13 | /// 14 | /// Initializes patterned compressor 15 | /// 16 | public StreamHighPatternedCompressor() 17 | { 18 | _encoder = new StreamEncoderHigh(); 19 | _encoder.Init(0); 20 | } 21 | 22 | private int[][] _hashArrClone; 23 | 24 | private int[] _hashArrPosClone; 25 | 26 | /// 27 | /// Initializes HashArray for Encoder 28 | /// 29 | protected override void InitHashArray() 30 | { 31 | _hashArrClone = new int[StreamEncoderHigh.HASHARR_CNT][]; 32 | var e = _encoder.HashArr2; 33 | for (var i = 0; i < StreamEncoderHigh.HASHARR_CNT; i++) 34 | { 35 | _hashArrClone[i] = new int[e[i].Length]; 36 | Array.Copy(e[i], 0, _hashArrClone[i], 0, e[i].Length); 37 | } 38 | 39 | _hashArrPosClone = new int[_encoder.HashArrPos.Length]; 40 | Array.Copy(_encoder.HashArrPos, 0, _hashArrPosClone, 0, _hashArrPosClone.Length); 41 | } 42 | 43 | /// 44 | /// Restores HashArray for Encoder 45 | /// 46 | protected override void RestoreHashArray() 47 | { 48 | var e = _encoder.HashArr2; 49 | for (var i = 0; i < StreamEncoderHigh.HASHARR_CNT; i++) 50 | { 51 | Array.Copy(_hashArrClone[i], 0, e[i], 0, e[i].Length); 52 | } 53 | 54 | Array.Copy(_hashArrPosClone, 0, _encoder.HashArrPos, 0, _hashArrPosClone.Length); 55 | } 56 | 57 | /// 58 | /// Compress block of data 59 | /// 60 | protected override int CompressBlock(int countIn, byte[] bufferOut, int offsetOut) 61 | { 62 | return _encoder.CompressBlock(_innerBuffer, _patternLength, _patternLength + countIn, 0, bufferOut, offsetOut + 1); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/Patterned/StreamPatternedCompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer.Algorithms.Patterned 4 | { 5 | /// 6 | /// Patterned Compressor/Decompressor for Blazer Stream algorithm 7 | /// 8 | public class StreamPatternedCompressor : BasePatternedCompressor 9 | { 10 | private readonly StreamEncoder _encoder; 11 | 12 | private readonly StreamDecoder _decoder; 13 | 14 | private int[] _hashArrClone; 15 | 16 | /// 17 | /// Initializes patterned compressor 18 | /// 19 | public StreamPatternedCompressor() 20 | { 21 | _encoder = (StreamEncoder)EncoderDecoderFactory.GetEncoder(BlazerAlgorithm.Stream); 22 | _decoder = (StreamDecoder)EncoderDecoderFactory.GetDecoder(BlazerAlgorithm.Stream); 23 | } 24 | 25 | /// 26 | /// Calculates max compressed buffer size for specified uncompressed data length 27 | /// 28 | public override int CalculateMaxCompressedBufferLength(int uncompressedLength) 29 | { 30 | return uncompressedLength + (uncompressedLength >> 8) + 3 + _encoder.GetAdditionalInSize() + 1; 31 | } 32 | 33 | /// 34 | /// Initializes HashArray for Encoder 35 | /// 36 | protected override void InitHashArray() 37 | { 38 | _hashArrClone = new int[_encoder.HashArr.Length]; 39 | Array.Copy(_encoder.HashArr, 0, _hashArrClone, 0, _hashArrClone.Length); 40 | } 41 | 42 | /// 43 | /// Restores HashArray for Encoder 44 | /// 45 | protected override void RestoreHashArray() 46 | { 47 | Array.Copy(_hashArrClone, 0, _encoder.HashArr, 0, _hashArrClone.Length); 48 | } 49 | 50 | /// 51 | /// Returns algorithm id 52 | /// 53 | protected override byte GetAlgorithmId() 54 | { 55 | return (byte)_encoder.GetAlgorithmId(); 56 | } 57 | 58 | /// 59 | /// Compress block of data 60 | /// 61 | protected override int CompressBlock(int countIn, byte[] bufferOut, int offsetOut) 62 | { 63 | return _encoder.CompressBlock(_innerBuffer, _patternLength, _patternLength + countIn, 0, bufferOut, offsetOut + 1); 64 | } 65 | 66 | /// 67 | /// Decompress block of data 68 | /// 69 | protected override int DecompressBlock(byte[] bufferIn, int offsetIn, int countIn) 70 | { 71 | return _decoder.DecompressBlock(bufferIn, offsetIn + 1, offsetIn + countIn, _innerBuffer, _patternLength, _innerBuffer.Length); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/StreamDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Force.Blazer.Algorithms 5 | { 6 | /// 7 | /// Decoder of Stream version of Blazer algorithm 8 | /// 9 | /// Stream version is good for 'live' streamss, slightly slower than Block, but support stream flushing without 10 | /// losing compression rate and has very fast decoder 11 | public class StreamDecoder : IDecoder 12 | { 13 | /// 14 | /// Inner buffer to store data between iterations 15 | /// 16 | [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed. Suppression is OK here.")] 17 | protected byte[] _innerBuffer; 18 | 19 | private int _innerBufferMaxLen = 0; 20 | 21 | private int _innerBufferLen = 0; 22 | 23 | /// 24 | /// Initializes decoder with information about maximum uncompressed block size 25 | /// 26 | public virtual void Init(int maxUncompressedBlockSize) 27 | { 28 | _innerBufferMaxLen = maxUncompressedBlockSize + StreamEncoder.MAX_BACK_REF + 1; 29 | _innerBuffer = new byte[_innerBufferMaxLen]; 30 | } 31 | 32 | /// 33 | /// Decodes given buffer 34 | /// 35 | public BufferInfo Decode(byte[] buffer, int offset, int length, bool isCompressed) 36 | { 37 | if (_innerBufferLen > StreamEncoder.MAX_BACK_REF) 38 | { 39 | Buffer.BlockCopy(_innerBuffer, _innerBufferLen - StreamEncoder.MAX_BACK_REF, _innerBuffer, 0, StreamEncoder.MAX_BACK_REF); 40 | _innerBufferLen = StreamEncoder.MAX_BACK_REF; 41 | } 42 | 43 | var outOffset = _innerBufferLen; 44 | 45 | if (isCompressed) 46 | _innerBufferLen = DecompressBlock(buffer, offset, length, _innerBuffer, _innerBufferLen, _innerBufferMaxLen); 47 | else 48 | { 49 | Buffer.BlockCopy(buffer, offset, _innerBuffer, _innerBufferLen, length - offset); 50 | _innerBufferLen += length - offset; 51 | } 52 | 53 | return new BufferInfo(_innerBuffer, outOffset, _innerBufferLen); 54 | } 55 | 56 | /// 57 | /// Returns algorithm id 58 | /// 59 | public BlazerAlgorithm GetAlgorithmId() 60 | { 61 | return BlazerAlgorithm.Stream; 62 | } 63 | 64 | /// 65 | /// Decompresses block of data 66 | /// 67 | public virtual int DecompressBlock( 68 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength) 69 | { 70 | return DecompressBlockExternal(bufferIn, bufferInOffset, bufferInLength, ref bufferOut, bufferOutOffset, bufferOutLength, false); 71 | } 72 | 73 | /// 74 | /// Decompresses independent block of data 75 | /// 76 | /// In buffer 77 | /// Decompressed data 78 | public static byte[] DecompressData( 79 | byte[] bufferIn) 80 | { 81 | var outBuffer = new byte[bufferIn.Length * 2]; 82 | var count = DecompressBlockExternal(bufferIn, 0, bufferIn.Length, ref outBuffer, 0, outBuffer.Length, true); 83 | Array.Resize(ref outBuffer, count); 84 | return outBuffer; 85 | } 86 | 87 | /// 88 | /// Decompresses block of data, can be used independently for byte arrays 89 | /// 90 | /// In buffer 91 | /// In buffer offset 92 | /// In buffer right offset (offset + count) 93 | /// Out buffer, should be enough size 94 | /// Out buffer offset 95 | /// Out buffer maximum right offset (offset + count) 96 | /// Resize out buffer if smaller than required 97 | /// Bytes count of decompressed data 98 | public static int DecompressBlockExternal(byte[] bufferIn, int bufferInOffset, int bufferInLength, ref byte[] bufferOut, int bufferOutOffset, int bufferOutLength, bool resizeOutBufferIfNeeded) 99 | { 100 | var idxIn = bufferInOffset; 101 | var idxOut = bufferOutOffset; 102 | while (idxIn < bufferInLength) 103 | { 104 | var elem = bufferIn[idxIn++]; 105 | var seqCntFirst = elem & 0xf; 106 | var litCntFirst = (elem >> 4) & 7; 107 | 108 | var litCnt = litCntFirst; 109 | int seqCnt; 110 | int backRef; 111 | 112 | if (elem >= 128) 113 | { 114 | backRef = (bufferIn[idxIn++] | bufferIn[idxIn++] << 8) + 257; 115 | seqCnt = seqCntFirst + 4; 116 | if (backRef == 0xffff + 257) 117 | { 118 | seqCntFirst = 0; 119 | seqCnt = 0; 120 | litCnt = elem - 128; 121 | litCntFirst = litCnt == 127 ? 7 : 0; 122 | // backRef = 0; 123 | } 124 | } 125 | else 126 | { 127 | backRef = bufferIn[idxIn++] + 1; 128 | seqCnt = seqCntFirst + 4; 129 | } 130 | 131 | if (litCntFirst == 7) 132 | { 133 | var litCntR = bufferIn[idxIn++]; 134 | if (litCntR < 253) litCnt += litCntR; 135 | else if (litCntR == 253) 136 | litCnt += 253 + bufferIn[idxIn++]; 137 | else if (litCntR == 254) 138 | litCnt += 253 + 256 + (bufferIn[idxIn++] | bufferIn[idxIn++] << 8); 139 | else 140 | litCnt += 253 + (256 * 256) + (bufferIn[idxIn++] << 0) + (bufferIn[idxIn++] << 8) + (bufferIn[idxIn++] << 16) + (bufferIn[idxIn++] << 24); 141 | } 142 | 143 | if (seqCntFirst == 15) 144 | { 145 | var seqCntR = bufferIn[idxIn++]; 146 | if (seqCntR < 253) seqCnt += seqCntR; 147 | else if (seqCntR == 253) 148 | seqCnt += 253 + bufferIn[idxIn++]; 149 | else if (seqCntR == 254) 150 | seqCnt += 253 + 256 + (bufferIn[idxIn++] << 0 | bufferIn[idxIn++] << 8); 151 | else 152 | seqCnt += 253 + (256 * 256) + (bufferIn[idxIn++] << 0) + (bufferIn[idxIn++] << 8) + (bufferIn[idxIn++] << 16) + (bufferIn[idxIn++] << 24); 153 | } 154 | 155 | var maxOutLength = idxOut + litCnt + seqCnt; 156 | if (maxOutLength > bufferOutLength) 157 | { 158 | if (resizeOutBufferIfNeeded) 159 | { 160 | bufferOutLength = Math.Max(bufferOut.Length * 2, maxOutLength); 161 | Array.Resize(ref bufferOut, bufferOutLength); 162 | } 163 | else 164 | throw new IndexOutOfRangeException("Invalid stream structure"); 165 | } 166 | 167 | if (litCnt >= 8) 168 | { 169 | Buffer.BlockCopy(bufferIn, idxIn, bufferOut, idxOut, litCnt); 170 | idxOut += litCnt; 171 | idxIn += litCnt; 172 | } 173 | else 174 | { 175 | while (--litCnt >= 0) bufferOut[idxOut++] = bufferIn[idxIn++]; 176 | } 177 | 178 | // exception wil be thrown anyway, but this check decreases decompression speed 179 | // if (idxOut - backRef < 0) 180 | // throw new InvalidOperationException("Invalid stream structure"); 181 | 182 | if (backRef >= seqCnt && seqCnt >= 8) 183 | { 184 | Buffer.BlockCopy(bufferOut, idxOut - backRef, bufferOut, idxOut, seqCnt); 185 | idxOut += seqCnt; 186 | } 187 | else 188 | { 189 | while (--seqCnt >= 0) 190 | { 191 | bufferOut[idxOut] = bufferOut[idxOut - backRef]; 192 | idxOut++; 193 | } 194 | } 195 | } 196 | 197 | return idxOut; 198 | } 199 | 200 | /// 201 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 202 | /// 203 | /// 2 204 | public virtual void Dispose() 205 | { 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/StreamDecoderNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Force.Blazer.Algorithms 5 | { 6 | /// 7 | /// Native implementation of decoder of Stream version of Blazer algorithm 8 | /// 9 | /// Stream version is good for 'live' streamss, slightly slower than Block, but support stream flushing without 10 | /// losing compression rate and has very fast decoder 11 | public class StreamDecoderNative : StreamDecoder 12 | { 13 | [DllImport(@"Blazer.Native.dll", CallingConvention = CallingConvention.Cdecl)] 14 | private static extern int blazer_stream_decompress_block( 15 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength); 16 | 17 | /// 18 | /// Initializes decoder with information about maximum uncompressed block size 19 | /// 20 | public override void Init(int maxUncompressedBlockSize) 21 | { 22 | // +8 for better copying speed. allow dummy copy by 8 bytes 23 | base.Init(maxUncompressedBlockSize + 8); 24 | } 25 | 26 | /// 27 | /// Decompresses block of data 28 | /// 29 | public override int DecompressBlock( 30 | byte[] bufferIn, int bufferInOffset, int bufferInLength, byte[] bufferOut, int bufferOutOffset, int bufferOutLength) 31 | { 32 | var cnt = blazer_stream_decompress_block( 33 | bufferIn, bufferInOffset, bufferInLength, bufferOut, bufferOutOffset, bufferOutLength); 34 | if (cnt < 0) 35 | throw new InvalidOperationException("Invalid compressed data"); 36 | return cnt; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Blazer.Net/Algorithms/StreamEncoderNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Force.Blazer.Algorithms 4 | { 5 | /// 6 | /// Native implementation of Stream version encoder of Blazer algorithm 7 | /// 8 | /// Stream version is good for 'live' streamss, slightly slower than Block, but support stream flushing without 9 | /// losing compression rate and has very fast decoder 10 | public class StreamEncoderNative : StreamEncoder 11 | { 12 | [DllImport(@"Blazer.Native.dll", CallingConvention = CallingConvention.Cdecl)] 13 | private static extern int blazer_stream_compress_block( 14 | byte[] bufferIn, int bufferInOffset, int bufferInLength, int globalOffset, byte[] bufferOut, int bufferOutOffset, int[] hashArr); 15 | 16 | /// 17 | /// Returns additional size for inner buffers. Can be used to store some data or for optimiations 18 | /// 19 | /// Size in bytes 20 | public override int GetAdditionalInSize() 21 | { 22 | return 8; 23 | } 24 | 25 | /// 26 | /// Compresses block of data. See for details 27 | /// 28 | public override int CompressBlock( 29 | byte[] bufferIn, 30 | int bufferInOffset, 31 | int bufferInLength, 32 | int bufferInShift, 33 | byte[] bufferOut, 34 | int bufferOutOffset) 35 | { 36 | return blazer_stream_compress_block( 37 | bufferIn, 38 | bufferInOffset, 39 | bufferInLength, 40 | bufferInShift, 41 | bufferOut, 42 | bufferOutOffset, 43 | _hashArr); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Blazer.Net/Blazer.Net.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {026EE2B9-3367-480A-8B46-118F4037C827} 8 | Library 9 | Properties 10 | Force.Blazer 11 | Blazer.Net 12 | v4.0 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | false 24 | 25 | 26 | none 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | false 33 | bin\Release\Blazer.Net.xml 34 | 35 | 36 | true 37 | 38 | 39 | true 40 | 41 | 42 | ..\public.snk 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | public.snk 100 | 101 | 102 | 103 | 104 | Resources\Blazer.Native.x64.dll 105 | 106 | 107 | Resources\Blazer.Native.x86.dll 108 | 109 | 110 | 111 | 112 | cmd /c if exist "$(ProjectDir)sign.cmd" "$(ProjectDir)sign.cmd" "$(TargetPath)" "$(SolutionDir)private.snk" 113 | 114 | 121 | -------------------------------------------------------------------------------- /Blazer.Net/Blazer.Net.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blazer.Net 5 | Blazer.Net 6 | 0.10.1 7 | force 8 | force 9 | https://github.com/force-net/blazer/blob/develop/LICENSE 10 | https://github.com/force-net/blazer 11 | http://files.force-net.com/blazer-nuget-ico.png 12 | false 13 | Blazer is high-performance, low compression archiver. 14 | 15 | Blazer is high-performance, low compression archiver. 16 | Main usage to work with streams, but can be used as general archiver or for reducing application size by compressing it resources. 17 | Compression rate is comparable (slightly better) to LZ4 and Snappy and compression speed is really faster than GZip. 18 | Blazer contains all standard features for archives, but also it is possible to use control commands in stream, encrypt archive, use it as simple (but fast) crc32c calulator, perform patterned compresstion and many others features. 19 | 20 | See project site for detailed information. 21 | 22 | 23 | - Fixed an issue with loading of native library for dotnet core on Windows 24 | - Fixed issue with seeking of MemoryStream with offset (Position/Seek gives different results) 25 | - Improved Api for DataArrayHelper 26 | 27 | Copyright by Force 2016-2018 28 | .NET fast compression archive crc32c archiver 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Blazer.Net/Blazer.Net.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0 6 | 7 | 8 | 9 | 10 | {1697F214-4621-4366-8313-CF729755BBDF} 11 | Blazer.Net 12 | .\obj 13 | .\bin\ 14 | v4.6.1 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer 2 | { 3 | /// 4 | /// Blazer algorithm 5 | /// 6 | public enum BlazerAlgorithm : byte 7 | { 8 | /// 9 | /// No compression. Can be used for non-compressible data or for keeping stream stucture 10 | /// 11 | NoCompress = 0, 12 | 13 | /// 14 | /// Stream compression. Effective for 'live' streams 15 | /// 16 | Stream = 1, 17 | 18 | /// 19 | /// Block compression. Effective for compressing files 20 | /// 21 | Block = 2 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerBlockType.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer 2 | { 3 | /// 4 | /// Block type of Blazer archive 5 | /// 6 | public enum BlazerBlockType : byte 7 | { 8 | /// 9 | /// Empty control block 10 | /// 11 | ControlDataEmpty = 0xf0, 12 | 13 | /// 14 | /// Control block 15 | /// 16 | ControlData = 0xf1, 17 | 18 | /// 19 | /// File info block 20 | /// 21 | Comment = 0xf9, 22 | 23 | // FileInfoIndex = 0xfc 24 | 25 | /// 26 | /// File info block 27 | /// 28 | FileInfo = 0xfd, 29 | 30 | // FileInfoRef = 0xfe 31 | 32 | /// 33 | /// Footer block 34 | /// 35 | Footer = 0xff 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerCompressionOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Force.Blazer.Algorithms; 5 | 6 | namespace Force.Blazer 7 | { 8 | /// 9 | /// Options for compression 10 | /// 11 | public class BlazerCompressionOptions 12 | { 13 | /// 14 | /// Encoder (realization of compression algorithm) 15 | /// 16 | public IEncoder Encoder { get; set; } 17 | 18 | private BlazerFlags _flags { get; set; } 19 | 20 | /// 21 | /// Password for encrypting data 22 | /// 23 | public string Password 24 | { 25 | get 26 | { 27 | return PasswordRaw == null ? null : Encoding.UTF8.GetString(PasswordRaw); 28 | } 29 | 30 | set 31 | { 32 | PasswordRaw = string.IsNullOrEmpty(value) ? null : Encoding.UTF8.GetBytes(value); 33 | } 34 | } 35 | 36 | /// 37 | /// Password for encrypting data (raw binary variant) 38 | /// 39 | public byte[] PasswordRaw { get; set; } 40 | 41 | /// 42 | /// Encrypt full flag. Fully encypted streams does not reveal any information about inner data (blazer header is also encypted) 43 | /// 44 | /// Flush can be unsupported with this mode 45 | public bool EncryptFull { get; set; } 46 | 47 | /// 48 | /// Leave inner stream open after closing blazer stream 49 | /// 50 | public bool LeaveStreamOpen { get; set; } 51 | 52 | /// 53 | /// Add Crc data to stream. If data are damaged, error will occure 54 | /// 55 | /// Blazer uses Crc32C checksum algorithm 56 | public bool IncludeCrc 57 | { 58 | get 59 | { 60 | return GetFlag(BlazerFlags.IncludeCrc); 61 | } 62 | 63 | set 64 | { 65 | SetFlag(BlazerFlags.IncludeCrc, value); 66 | } 67 | } 68 | 69 | /// 70 | /// Add header to stream. Stream without header requires manual flags set on decompression 71 | /// 72 | public bool IncludeHeader 73 | { 74 | get 75 | { 76 | return GetFlag(BlazerFlags.IncludeHeader); 77 | } 78 | 79 | set 80 | { 81 | SetFlag(BlazerFlags.IncludeHeader, value); 82 | } 83 | } 84 | 85 | /// 86 | /// Add footer to stream. Footer allows to check on decompression that stream is correct 87 | /// 88 | /// If on decompression stream is not seekable, footer will be validated only after decompressing data. If stream is seekable, before it. 89 | public bool IncludeFooter 90 | { 91 | get 92 | { 93 | return GetFlag(BlazerFlags.IncludeFooter); 94 | } 95 | 96 | set 97 | { 98 | SetFlag(BlazerFlags.IncludeFooter, value); 99 | } 100 | } 101 | 102 | /// 103 | /// Respect command. 104 | /// 105 | /// If it set, every flush will compress current block of data and Flush it into inner stream. Otherwise, flush commands are ignored 106 | public BlazerFlushMode FlushMode { get; set; } 107 | 108 | /// 109 | /// Maximum block size to compress. Larger blocks require more memory, but can produce higher compression 110 | /// 111 | /// Currently, block sizes from 512 bytes to 16Mb are supported 112 | public int MaxBlockSize 113 | { 114 | get 115 | { 116 | var cnt = (int)(_flags & BlazerFlags.InBlockSize16M); 117 | return 1 << (cnt + 9); 118 | } 119 | 120 | set 121 | { 122 | if (value < 0) 123 | throw new ArgumentOutOfRangeException("value", "Block size should be positive"); 124 | var v = value - 1; 125 | var cnt = 0; 126 | while (v > 0) 127 | { 128 | cnt++; 129 | v >>= 1; 130 | } 131 | 132 | cnt -= 9; 133 | if (cnt < 0) cnt = 0; 134 | 135 | if (cnt > 15) cnt = 15; 136 | _flags &= ~BlazerFlags.InBlockSize16M; 137 | _flags |= (BlazerFlags)cnt; 138 | } 139 | } 140 | 141 | /// 142 | /// Sets max block size from flags 143 | /// 144 | public void SetMaxBlockSizeFromFlags(BlazerFlags sizeFlags) 145 | { 146 | _flags &= ~BlazerFlags.InBlockSize16M; 147 | _flags |= sizeFlags & BlazerFlags.InBlockSize16M; 148 | } 149 | 150 | /// 151 | /// Gets default block size for Stream algorithm 152 | /// 153 | public static int DefaultStreamBlockSize 154 | { 155 | get 156 | { 157 | return 65536; 158 | } 159 | } 160 | 161 | /// 162 | /// Gets default block size for Block algorithm 163 | /// 164 | public static int DefaultBlockBlockSize 165 | { 166 | get 167 | { 168 | // 2Mb 169 | return 1 << 21; 170 | } 171 | } 172 | 173 | /// 174 | /// Creates default options for no compression algorithm 175 | /// 176 | public static BlazerCompressionOptions CreateNoCompression() 177 | { 178 | return new BlazerCompressionOptions 179 | { 180 | Encoder = EncoderDecoderFactory.GetEncoder(BlazerAlgorithm.NoCompress), 181 | _flags = BlazerFlags.DefaultStream 182 | }; 183 | } 184 | 185 | /// 186 | /// Creates default options for Stream algorithm 187 | /// 188 | public static BlazerCompressionOptions CreateStream() 189 | { 190 | return new BlazerCompressionOptions 191 | { 192 | Encoder = EncoderDecoderFactory.GetEncoder(BlazerAlgorithm.Stream), 193 | _flags = BlazerFlags.DefaultStream, 194 | FlushMode = BlazerFlushMode.RespectFlush 195 | }; 196 | } 197 | 198 | /// 199 | /// Creates default options for Stream algorithm with high compression 200 | /// 201 | public static BlazerCompressionOptions CreateStreamHigh() 202 | { 203 | return new BlazerCompressionOptions 204 | { 205 | Encoder = new StreamEncoderHigh(), 206 | _flags = BlazerFlags.DefaultStream, 207 | FlushMode = BlazerFlushMode.RespectFlush 208 | }; 209 | } 210 | 211 | /// 212 | /// Creates default options for Block algorithm 213 | /// 214 | public static BlazerCompressionOptions CreateBlock() 215 | { 216 | return new BlazerCompressionOptions 217 | { 218 | Encoder = EncoderDecoderFactory.GetEncoder(BlazerAlgorithm.Block), 219 | _flags = BlazerFlags.DefaultBlock 220 | }; 221 | } 222 | 223 | /// 224 | /// Returns bit flags for current settings 225 | /// 226 | public BlazerFlags GetFlags() 227 | { 228 | var flags = _flags; 229 | if (PasswordRaw != null && PasswordRaw.Length != 0) 230 | { 231 | flags |= EncryptFull ? BlazerFlags.EncryptOuter : BlazerFlags.EncryptInner; 232 | } 233 | 234 | if (FlushMode != BlazerFlushMode.IgnoreFlush) 235 | flags |= BlazerFlags.RespectFlush; 236 | 237 | return flags; 238 | } 239 | 240 | private void SetFlag(BlazerFlags flag, bool isSet) 241 | { 242 | if (isSet) _flags |= flag; 243 | else _flags &= ~flag; 244 | } 245 | 246 | private bool GetFlag(BlazerFlags flag) 247 | { 248 | return (_flags & flag) != 0; 249 | } 250 | 251 | /// 252 | /// Instantiates default encoder for specified algoritm 253 | /// 254 | public void SetEncoderByAlgorithm(BlazerAlgorithm algorithm) 255 | { 256 | Encoder = EncoderDecoderFactory.GetEncoder(algorithm); 257 | } 258 | 259 | private BlazerFileInfo _fileInfo; 260 | 261 | private byte[] _commentRaw; 262 | 263 | /// 264 | /// Gets or sets information about encoded file 265 | /// 266 | public BlazerFileInfo FileInfo 267 | { 268 | get 269 | { 270 | return _fileInfo; 271 | } 272 | 273 | set 274 | { 275 | if (GetFlag(BlazerFlags.MultipleFiles) && value != null) 276 | throw new InvalidOperationException("Do not set FileInfo in multiple files mode mode"); 277 | _fileInfo = value; 278 | SetFlag(BlazerFlags.OnlyOneFile, _fileInfo != null); 279 | } 280 | } 281 | 282 | /// 283 | /// Marks, that archive can contains multiple file, pass file info through 284 | /// 285 | public bool MultipleFiles 286 | { 287 | get 288 | { 289 | return GetFlag(BlazerFlags.MultipleFiles); 290 | } 291 | 292 | set 293 | { 294 | if (GetFlag(BlazerFlags.OnlyOneFile) && value) 295 | throw new InvalidOperationException("Do not set FileInfo in this mode"); 296 | SetFlag(BlazerFlags.MultipleFiles, value); 297 | } 298 | } 299 | 300 | /// 301 | /// Gets or sets archive comment 302 | /// 303 | public string Comment 304 | { 305 | get 306 | { 307 | return CommentRaw == null ? null : Encoding.UTF8.GetString(CommentRaw); 308 | } 309 | 310 | set 311 | { 312 | CommentRaw = value == null ? null : Encoding.UTF8.GetBytes(value); 313 | } 314 | } 315 | 316 | /// 317 | /// Gets or sets archive raw (binary) comment. Useful for adding some info for technical streams 318 | /// 319 | public byte[] CommentRaw 320 | { 321 | get 322 | { 323 | return _commentRaw; 324 | } 325 | 326 | set 327 | { 328 | _commentRaw = value; 329 | SetFlag(BlazerFlags.IncludeComment, value != null && value.Length > 0); 330 | } 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerDecompressionOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Force.Blazer.Algorithms; 5 | 6 | namespace Force.Blazer 7 | { 8 | /// 9 | /// Options for decompression 10 | /// 11 | public class BlazerDecompressionOptions 12 | { 13 | /// 14 | /// Initialize this property for archive without header, to provide required information for decompression 15 | /// 16 | public BlazerCompressionOptions CompressionOptions { get; set; } 17 | 18 | /// 19 | /// Initialize this property for archive without header, to provide custom decoder 20 | /// 21 | public IDecoder Decoder { get; set; } 22 | 23 | /// 24 | /// Set default decoder by algorithm 25 | /// 26 | public void SetDecoderByAlgorithm(BlazerAlgorithm algorithm) 27 | { 28 | Decoder = EncoderDecoderFactory.GetDecoder(algorithm); 29 | } 30 | 31 | /// 32 | /// Leave inner stream open after closing blazer stream 33 | /// 34 | public bool LeaveStreamOpen { get; set; } 35 | 36 | /// 37 | /// Password for encrypting data 38 | /// 39 | public string Password 40 | { 41 | get 42 | { 43 | return PasswordRaw == null ? null : Encoding.UTF8.GetString(PasswordRaw); 44 | } 45 | 46 | set 47 | { 48 | PasswordRaw = string.IsNullOrEmpty(value) ? null : Encoding.UTF8.GetBytes(value); 49 | } 50 | } 51 | 52 | /// 53 | /// Password for decrypting data (raw binary variant) 54 | /// 55 | public byte[] PasswordRaw { get; set; } 56 | 57 | /// 58 | /// Encrypt full flag. Fully encypted streams does not reveal any information about inner data (blazer header is also encypted) 59 | /// 60 | public bool EncyptFull { get; set; } 61 | 62 | /// 63 | /// Disable seeking for inner stream 64 | /// 65 | /// By default, checks is stream seekable. But with this flag this check can be disabled and seek will not be performed for any stream. 66 | /// This also can be useful for muliple joined streams, when only part of real stream is a Blazer archive 67 | public bool NoSeek { get; set; } 68 | 69 | /// 70 | /// Callback on control data block. If is set, will be called for every control data 71 | /// 72 | public Action ControlDataCallback { get; set; } 73 | 74 | /// 75 | /// Callbacks on new file info 76 | /// 77 | public Action FileInfoCallback { get; set; } 78 | 79 | /// 80 | /// Skip when archive contains only one file (can be useful, when file name is analyzed manually) 81 | /// 82 | public bool DoNotFireInfoCallbackOnOneFile { get; set; } 83 | 84 | /// 85 | /// Skip real decoding. This option can be useful for 'list' or 'test' modes, when it required to get some info (e.g. files list) without real decoding 86 | /// 87 | public bool DoNotPerformDecoding { get; set; } 88 | 89 | /// 90 | /// Create default options 91 | /// 92 | public static BlazerDecompressionOptions CreateDefault() 93 | { 94 | return new BlazerDecompressionOptions(); 95 | } 96 | 97 | /// 98 | /// Constructor for default options 99 | /// 100 | public BlazerDecompressionOptions() 101 | { 102 | } 103 | 104 | /// 105 | /// Constructor for default options with password 106 | /// 107 | public BlazerDecompressionOptions(string password) 108 | { 109 | Password = password; 110 | } 111 | 112 | /// 113 | /// Constructor for default options with raw password 114 | /// 115 | public BlazerDecompressionOptions(byte[] passwordRaw) 116 | { 117 | PasswordRaw = passwordRaw; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerFileInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Force.Blazer 5 | { 6 | /// 7 | /// Information about compressed file 8 | /// 9 | public class BlazerFileInfo 10 | { 11 | /// 12 | /// File name with path, or without it 13 | /// 14 | public string FileName { get; set; } 15 | 16 | /// 17 | /// File creation time in UTC 18 | /// 19 | public DateTime CreationTimeUtc { get; set; } 20 | 21 | /// 22 | /// Last write time in UTC 23 | /// 24 | public DateTime LastWriteTimeUtc { get; set; } 25 | 26 | /// 27 | /// File length 28 | /// 29 | /// This length is just for information. Real compressed data can have another lengh 30 | public long Length { get; set; } 31 | 32 | /// 33 | /// File attributes 34 | /// 35 | public FileAttributes Attributes { get; set; } 36 | 37 | /// 38 | /// Creates file info from with optional custom relative name 39 | /// 40 | /// If no relative name is passed, file name without path is used as file name. Otherwise, this relative name 41 | public static BlazerFileInfo FromFileInfo(FileSystemInfo info, string relativeFileName = null) 42 | { 43 | var bfi = new BlazerFileInfo(); 44 | bfi.FileName = relativeFileName ?? info.Name; 45 | bfi.Attributes = info.Attributes; 46 | bfi.CreationTimeUtc = info.CreationTimeUtc; 47 | bfi.LastWriteTimeUtc = info.LastWriteTimeUtc; 48 | var fileInfo = info as FileInfo; 49 | // directory has zero size 50 | bfi.Length = fileInfo != null ? fileInfo.Length : 0; 51 | return bfi; 52 | } 53 | 54 | /// 55 | /// Creates file info from file name 56 | /// 57 | public static BlazerFileInfo FromFileName(string fileName, bool leaveFullName) 58 | { 59 | if (File.Exists(fileName)) 60 | return FromFileInfo(new FileInfo(fileName), leaveFullName ? fileName : Path.GetFileName(fileName)); 61 | if (Directory.Exists(fileName)) 62 | return FromFileInfo(new DirectoryInfo(fileName), leaveFullName ? fileName : Path.GetFileName(fileName)); 63 | throw new FileNotFoundException("File not found", fileName); 64 | } 65 | 66 | /// 67 | /// Apply this file info to real file (set attributes and time) 68 | /// 69 | public void ApplyToFile() 70 | { 71 | if (File.Exists(FileName)) 72 | { 73 | File.SetAttributes(FileName, Attributes); 74 | File.SetCreationTimeUtc(FileName, CreationTimeUtc); 75 | File.SetLastWriteTimeUtc(FileName, LastWriteTimeUtc); 76 | } 77 | else if (Directory.Exists(FileName)) 78 | { 79 | new DirectoryInfo(FileName).Attributes = Attributes; 80 | Directory.SetCreationTimeUtc(FileName, CreationTimeUtc); 81 | Directory.SetLastWriteTimeUtc(FileName, LastWriteTimeUtc); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Blazer 4 | { 5 | /// 6 | /// Header flags for Blazer archive 7 | /// 8 | [Flags] 9 | public enum BlazerFlags : uint 10 | { 11 | /// 12 | /// No flags 13 | /// 14 | None = 0, 15 | 16 | #pragma warning disable 1591 17 | InBlockSize512 = 0, 18 | InBlockSize1K = 1, 19 | InBlockSize2K = 2, 20 | InBlockSize4K = 3, 21 | InBlockSize8K = 4, 22 | InBlockSize16K = 5, 23 | InBlockSize32K = 6, 24 | InBlockSize64K = 7, 25 | InBlockSize128K = 8, 26 | InBlockSize256K = 9, 27 | InBlockSize512K = 10, 28 | InBlockSize1M = 11, 29 | InBlockSize2M = 12, 30 | InBlockSize4M = 13, 31 | InBlockSize8M = 14, 32 | InBlockSize16M = 15, 33 | 34 | IncludeCrc = 256, 35 | IncludeHeader = 512, 36 | IncludeFooter = 1024, 37 | RespectFlush = 2048, 38 | 39 | // in theory, we can encrypt outer and inner two times, so, let's keep both flags 40 | EncryptInner = 4096, 41 | EncryptOuter = 8192, 42 | NotImplementedAddRecoveryInfo = 16384, 43 | 44 | NoFileInfo = 0, 45 | OnlyOneFile = 32768, 46 | MultipleFiles = 65536, 47 | NotImplementedMultipleIndexedFiles = OnlyOneFile | MultipleFiles, 48 | IncludeComment = 131072, 49 | 50 | Default = IncludeCrc | IncludeHeader | IncludeFooter | RespectFlush, 51 | DefaultStream = Default | InBlockSize64K, 52 | DefaultBlock = Default | InBlockSize2M, 53 | 54 | // all known flags for this time 55 | AllKnownFlags = InBlockSize16M | IncludeCrc | IncludeHeader | IncludeFooter | RespectFlush | EncryptInner | EncryptOuter | OnlyOneFile | MultipleFiles | IncludeComment | 0xf0 56 | #pragma warning restore 1591 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerFlushMode.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Blazer 2 | { 3 | /// 4 | /// Flush variants for stream 5 | /// 6 | public enum BlazerFlushMode 7 | { 8 | /// 9 | /// Ignore all flush requests (default variant) 10 | /// 11 | IgnoreFlush = 0, 12 | 13 | /// 14 | /// Respect flush requests 15 | /// 16 | RespectFlush = 1, 17 | 18 | /// 19 | /// Auto flush on every write 20 | /// 21 | AutoFlush = 2, 22 | 23 | /// 24 | /// Try to determine, when flush should be performed 25 | /// 26 | SmartFlush = 3 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Blazer.Net/BlazerPatternedHelper.cs: -------------------------------------------------------------------------------- 1 | using Force.Blazer.Algorithms.Patterned; 2 | 3 | namespace Force.Blazer 4 | { 5 | /// 6 | /// Helper for patterned compression 7 | /// 8 | public static class BlazerPatternedHelper 9 | { 10 | /// 11 | /// Creates Stream algorithm Patterned compressor. Good on compression, fast on decompression 12 | /// 13 | /// Do not use pattern data more than 64Kb. It is useless for this type 14 | public static IPatternedCompressor CreateStream() 15 | { 16 | return new StreamPatternedCompressor(); 17 | } 18 | 19 | /// 20 | /// Creates Stream algorithm Patterned compressor with high encoding. Slow on compression, fast on decompression 21 | /// 22 | /// Compression is very slow. Use only when needed 23 | public static IPatternedCompressor CreateStreamHigh() 24 | { 25 | return new StreamHighPatternedCompressor(); 26 | } 27 | 28 | /// 29 | /// Creates Block algorithm Patterned compressor. Good on compression, Good on decompression 30 | /// 31 | /// Use this algorithm whan pattern is big (greater than 64Kb) 32 | public static IPatternedCompressor CreateBlock() 33 | { 34 | return new BlockPatternedCompressor(); 35 | } 36 | 37 | /// 38 | /// Creates Patterned compressor and init it with pattern. Algorithm is selected by pattern size 39 | /// 40 | public static IPatternedCompressor CreateFromPatternAuto(byte[] pattern) 41 | { 42 | return CreateFromPatternAuto(pattern, 0, pattern.Length); 43 | } 44 | 45 | /// 46 | /// Creates Patterned compressor and init it with pattern. Algorithm is selected by pattern size 47 | /// 48 | public static IPatternedCompressor CreateFromPatternAuto(byte[] pattern, int offset, int count) 49 | { 50 | IPatternedCompressor c; 51 | if (count < 65536) c = CreateStream(); 52 | else c = CreateBlock(); 53 | 54 | c.PreparePattern(pattern, offset, count); 55 | 56 | return c; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Blazer.Net/Encyption/DecryptHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | using Force.Blazer.Algorithms; 9 | 10 | namespace Force.Blazer.Encyption 11 | { 12 | internal class NullDecryptHelper 13 | { 14 | public virtual BufferInfo Decrypt(byte[] data, int offset, int length) 15 | { 16 | return new BufferInfo(data, offset, length); 17 | } 18 | 19 | public virtual int AdjustLength(int inLength) 20 | { 21 | return inLength; 22 | } 23 | 24 | public virtual int GetHeaderLength() 25 | { 26 | return 0; 27 | } 28 | 29 | public virtual void Init(byte[] header, int maxBlockSize) 30 | { 31 | } 32 | } 33 | 34 | [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Reviewed. Suppression is OK here.")] 35 | internal class DecryptHelper : NullDecryptHelper 36 | { 37 | private const int PbkIterations = 20000; 38 | 39 | private Aes _aes; 40 | 41 | private byte[] _passwordRaw; 42 | 43 | private byte[] _buffer; 44 | 45 | public DecryptHelper(string password) 46 | { 47 | _passwordRaw = string.IsNullOrEmpty(password) ? null : Encoding.UTF8.GetBytes(password); 48 | } 49 | 50 | public DecryptHelper(byte[] passwordRaw) 51 | { 52 | _passwordRaw = passwordRaw; 53 | } 54 | 55 | public override int GetHeaderLength() 56 | { 57 | return 24; 58 | } 59 | 60 | private long _counter; 61 | 62 | private bool _useCounter; 63 | 64 | public override void Init(byte[] buffer, int maxBlockSize) 65 | { 66 | if (buffer.Length != GetHeaderLength()) 67 | throw new InvalidOperationException("Invalid header"); 68 | 69 | _buffer = new byte[AdjustLength(maxBlockSize)]; 70 | 71 | var salt = new byte[8]; 72 | Buffer.BlockCopy(buffer, 0, salt, 0, 8); 73 | var pass = new Rfc2898DeriveBytes(_passwordRaw, salt, PbkIterations); 74 | _passwordRaw = null; 75 | _aes = Aes.Create(); 76 | _aes.Key = pass.GetBytes(32); 77 | // zero. it is ok 78 | _aes.IV = new byte[16]; 79 | _aes.Mode = CipherMode.CBC; 80 | _aes.Padding = PaddingMode.Zeros; 81 | 82 | using (var encryptor = _aes.CreateEncryptor()) 83 | { 84 | var toEncrypt = new byte[16]; 85 | Buffer.BlockCopy(buffer, 8, toEncrypt, 0, 8); 86 | Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'?' }, 0, toEncrypt, 8, 8); 87 | var encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16); 88 | if (encoded.Take(8).Where((t, i) => buffer[i + 16] != t).Any()) 89 | { 90 | // trying second variant 91 | toEncrypt[15] = (byte)'!'; 92 | encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16); 93 | if (encoded.Take(8).Where((t, i) => buffer[i + 16] != t).Any()) 94 | { 95 | throw new InvalidOperationException("Invalid password"); 96 | } 97 | else 98 | { 99 | _useCounter = false; 100 | } 101 | } 102 | else 103 | { 104 | _useCounter = true; 105 | _counter = 0; 106 | } 107 | } 108 | } 109 | 110 | public override BufferInfo Decrypt(byte[] data, int offset, int length) 111 | { 112 | using (var decryptor = _aes.CreateDecryptor()) 113 | { 114 | var ob = _buffer; 115 | var cnt = decryptor.TransformBlock(data, offset, length - offset, ob, 0); 116 | if (_useCounter) 117 | { 118 | var cv = ((long)ob[0] << 0) | ((long)ob[1] << 8) | ((long)ob[2] << 16) | ((long)ob[3] << 24) | ((long)ob[4] << 32) | ((long)ob[5] << 40) | ((long)ob[6] << 48) | ((long)ob[7] << 56); 119 | if (_counter++ != cv) 120 | throw new InvalidOperationException("Invalid encrypted block. Duplicated or damaged."); 121 | } 122 | 123 | // else dummy data in header (8) 124 | return new BufferInfo(ob, 8, cnt); 125 | } 126 | } 127 | 128 | public override int AdjustLength(int inLength) 129 | { 130 | return ((inLength - 1 + 8) | 15) + 1; 131 | } 132 | 133 | public static Stream ConvertStreamToDecyptionStream(Stream inner, byte[] password) 134 | { 135 | var salt = new byte[8]; 136 | // ensure read 8 bytes 137 | var cnt = 8; 138 | while (cnt > 0) 139 | { 140 | var readed = inner.Read(salt, 8 - cnt, cnt); 141 | if (readed == 0 && cnt > 0) 142 | throw new InvalidOperationException("Invalid input stream"); 143 | cnt -= readed; 144 | } 145 | 146 | var pass = new Rfc2898DeriveBytes(password, salt, 4096); 147 | var aes = Aes.Create(); 148 | aes.Key = pass.GetBytes(32); 149 | // zero. it is ok - we use random password (due salt), so, anyway it will be different 150 | aes.IV = new byte[16]; 151 | aes.Mode = CipherMode.CBC; 152 | var cryptoTransform = aes.CreateDecryptor(); 153 | #if NETCORE 154 | aes.Padding = PaddingMode.PKCS7; // here we will use such padding 155 | cryptoTransform = new Iso10126TransformEmulator(cryptoTransform); 156 | #else 157 | aes.Padding = PaddingMode.ISO10126; // here we will use such padding 158 | #endif 159 | aes.Padding = PaddingMode.PKCS7; // here we will use such padding 160 | return new CryptoStream(inner, cryptoTransform, CryptoStreamMode.Read); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Blazer.Net/Encyption/EncryptHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | 7 | using Force.Blazer.Algorithms; 8 | 9 | namespace Force.Blazer.Encyption 10 | { 11 | internal class NullEncryptHelper 12 | { 13 | public virtual BufferInfo Encrypt(byte[] data, int offset, int length) 14 | { 15 | return new BufferInfo(data, offset, length); 16 | } 17 | 18 | public virtual byte[] AppendHeader(byte[] header) 19 | { 20 | return header; 21 | } 22 | } 23 | 24 | [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Reviewed. Suppression is OK here.")] 25 | internal class EncryptHelper : NullEncryptHelper 26 | { 27 | private const int PrefixSize = 8; 28 | 29 | private const int PbkIterations = 20000; 30 | 31 | private readonly Aes _aes; 32 | 33 | private readonly byte[] _headerToWrite; 34 | 35 | private readonly RandomNumberGenerator _rng; 36 | 37 | // private readonly Random _random; 38 | 39 | private readonly byte[] _buffer; 40 | 41 | // private readonly byte[] _randomBlock8; 42 | 43 | private readonly byte[] _randomBlock16; 44 | 45 | private int AdjustLength(int inLength) 46 | { 47 | return ((inLength + PrefixSize - 1) | 15) + 1; 48 | } 49 | 50 | public EncryptHelper(string password, int maxBufferSize) 51 | : this(string.IsNullOrEmpty(password) ? null : Encoding.UTF8.GetBytes(password), maxBufferSize) 52 | { 53 | } 54 | 55 | public EncryptHelper(byte[] password, int maxBufferSize) 56 | { 57 | _buffer = new byte[AdjustLength(maxBufferSize)]; // additional 8 bytes for adding random data to every block and whole block is multiple by 16 58 | // _randomBlock8 = new byte[PrefixSize]; 59 | _randomBlock16 = new byte[16]; 60 | 61 | // we write to header 8 byte of salt + 8 byte of random data 62 | // after that, we encrypt 8 bytes of random data (pad with static to 16 and write first 8) 63 | // this 8 bytes will be used for checking correctness on decryption 64 | // this is fine for fast checking "is password correct", but does not 65 | // give full information about is it the required password 66 | _rng = RandomNumberGenerator.Create(); 67 | // _random = new Random(); 68 | var salt = new byte[8]; 69 | _rng.GetBytes(salt); 70 | var pass = new Rfc2898DeriveBytes(password, salt, PbkIterations); 71 | _aes = Aes.Create(); 72 | _aes.Key = pass.GetBytes(32); 73 | // zero. it is ok - we use data with salted random and do not need to use additional IV here 74 | _aes.IV = new byte[16]; 75 | _aes.Mode = CipherMode.CBC; 76 | _aes.Padding = PaddingMode.None; // other padding will add additional block, we manually will add random padding 77 | _headerToWrite = new byte[24]; 78 | 79 | var random = new byte[8]; 80 | _rng.GetBytes(random); 81 | var toEncrypt = new byte[16]; 82 | 83 | Buffer.BlockCopy(random, 0, toEncrypt, 0, 8); 84 | 85 | Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'?' }, 0, toEncrypt, 8, 8); 86 | 87 | Buffer.BlockCopy(salt, 0, _headerToWrite, 0, 8); 88 | Buffer.BlockCopy(random, 0, _headerToWrite, 8, 8); 89 | 90 | // currently, we use salt for password, so every encryption has own key, as result we do not need to use other values for counter 91 | // nonce is useful when password is static 92 | // _counter = ((long)salt[0] << 0) | ((long)salt[1] << 8) | ((long)salt[2] << 16) | ((long)salt[3] << 24) | ((long)salt[4] << 32) | ((long)salt[5] << 40) | ((long)salt[6] << 48) | ((long)salt[7] << 56); 93 | _counter = 0; 94 | 95 | using (var encryptor = _aes.CreateEncryptor()) 96 | { 97 | var encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16); 98 | Buffer.BlockCopy(encoded, 0, _headerToWrite, 16, 8); 99 | } 100 | } 101 | 102 | private long _counter; 103 | 104 | [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "Reviewed. Suppression is OK here.")] 105 | public override BufferInfo Encrypt(byte[] data, int offset, int length) 106 | { 107 | var count = length - offset; 108 | // we can use random iv here, but is simplier to use zero iv and write some dummy bytes 109 | // in block header. on decoding, we just skip it 110 | // this is will elimitate CBC problem with same blocks (if data is repeatable) 111 | using (var encryptor = _aes.CreateEncryptor()) 112 | { 113 | // currently, we're not supporting multi-threading, so, we do not need to use Interlocked operations 114 | var c = _counter++; 115 | _buffer[0] = (byte)((c >> 00) & 0xff); _buffer[1] = (byte)((c >> 08) & 0xff); _buffer[2] = (byte)((c >> 16) & 0xff); _buffer[3] = (byte)((c >> 24) & 0xff); 116 | _buffer[4] = (byte)((c >> 32) & 0xff); _buffer[5] = (byte)((c >> 40) & 0xff); _buffer[6] = (byte)((c >> 48) & 0xff); _buffer[7] = (byte)((c >> 56) & 0xff); 117 | // _rng.GetBytes(_randomBlock8); 118 | // copying prefix 119 | // Buffer.BlockCopy(_randomBlock8, 0, _buffer, 0, PrefixSize); 120 | 121 | // copying real data 122 | Buffer.BlockCopy(data, offset, _buffer, PrefixSize, count); 123 | 124 | var addRandomCnt = 16 - ((count + PrefixSize) & 15); 125 | if (addRandomCnt < 16) 126 | { 127 | // here is no security required, but it faster 128 | _rng.GetBytes(_randomBlock16); 129 | Buffer.BlockCopy(_randomBlock16, 0, _buffer, PrefixSize + count, addRandomCnt); 130 | } 131 | else 132 | { 133 | addRandomCnt = 0; 134 | } 135 | 136 | var encLength = PrefixSize + count + addRandomCnt; 137 | encryptor.TransformBlock(_buffer, 0, encLength, _buffer, 0); 138 | 139 | return new BufferInfo(_buffer, 0, encLength); 140 | } 141 | } 142 | 143 | public override byte[] AppendHeader(byte[] header) 144 | { 145 | if (header == null) header = new byte[0]; 146 | var res = new byte[header.Length + _headerToWrite.Length]; 147 | Buffer.BlockCopy(header, 0, res, 0, header.Length); 148 | Buffer.BlockCopy(_headerToWrite, 0, res, header.Length, _headerToWrite.Length); 149 | return res; 150 | } 151 | 152 | public static Stream ConvertStreamToEncyptionStream(Stream inner, byte[] password) 153 | { 154 | var rng = RandomNumberGenerator.Create(); 155 | var salt = new byte[8]; 156 | rng.GetBytes(salt); 157 | var pass = new Rfc2898DeriveBytes(password, salt, 4096); 158 | var aes = Aes.Create(); 159 | aes.Key = pass.GetBytes(32); 160 | // zero. it is ok - we use random password (due salt), so, anyway it will be different 161 | aes.IV = new byte[16]; 162 | aes.Mode = CipherMode.CBC; 163 | aes.Padding = PaddingMode.PKCS7; // here we will use such padding 164 | inner.Write(salt, 0, 8); 165 | return new CryptoStream(inner, aes.CreateEncryptor(), CryptoStreamMode.Write); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Blazer.Net/Encyption/Iso10126TransformEmulator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Force.Blazer.Encyption 5 | { 6 | /// 7 | /// This class emulates ISO10126 AES transform for .NET Core 8 | /// 9 | public class Iso10126TransformEmulator : ICryptoTransform 10 | { 11 | private readonly ICryptoTransform _origTransofrm; 12 | 13 | /// 14 | /// Constructor. Origignal transform should be aes with PKCS7 padding 15 | /// 16 | /// 17 | public Iso10126TransformEmulator(ICryptoTransform origTransofrm) 18 | { 19 | _origTransofrm = origTransofrm; 20 | } 21 | 22 | /// 23 | /// Dispose current transform 24 | /// 25 | public void Dispose() 26 | { 27 | _origTransofrm.Dispose(); 28 | } 29 | 30 | int ICryptoTransform.TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) 31 | { 32 | return _origTransofrm.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); 33 | } 34 | 35 | byte[] ICryptoTransform.TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) 36 | { 37 | var outBuffer = new byte[_origTransofrm.OutputBlockSize + inputCount]; 38 | var dummyBuffer = new byte[_origTransofrm.InputBlockSize + inputCount]; 39 | Buffer.BlockCopy(inputBuffer, inputOffset, dummyBuffer, 0, inputCount); 40 | _origTransofrm.TransformBlock(dummyBuffer, 0, dummyBuffer.Length, outBuffer, 0); 41 | Array.Resize(ref outBuffer, outBuffer.Length - outBuffer[outBuffer.Length - 1]); 42 | return outBuffer; 43 | } 44 | 45 | int ICryptoTransform.InputBlockSize 46 | { 47 | get 48 | { 49 | return _origTransofrm.InputBlockSize; 50 | } 51 | } 52 | 53 | int ICryptoTransform.OutputBlockSize 54 | { 55 | get 56 | { 57 | return _origTransofrm.OutputBlockSize; 58 | } 59 | } 60 | 61 | bool ICryptoTransform.CanTransformMultipleBlocks 62 | { 63 | get 64 | { 65 | return _origTransofrm.CanTransformMultipleBlocks; 66 | } 67 | } 68 | 69 | bool ICryptoTransform.CanReuseTransform 70 | { 71 | get 72 | { 73 | return _origTransofrm.CanReuseTransform; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Blazer.Net/Helpers/BlazerFileHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Force.Blazer.Helpers 5 | { 6 | /// 7 | /// File Helper class 8 | /// 9 | public static class BlazerFileHelper 10 | { 11 | /// 12 | /// Helper method for convenient read multiple files from blazer archive and writing result to stream 13 | /// 14 | /// Out streams type 15 | /// Input blazer stream 16 | /// Callback on new directory arrival 17 | /// Callback on new file arrival, method should return stream for writing or null to skip it 18 | /// Callback on file end (called only for files, not for directories) 19 | public static void ReadFilesFromStream( 20 | BlazerOutputStream blazerStream, 21 | Action directoryCallback, 22 | Func fileStartCallback, 23 | Action fileEndCallback) 24 | where T : Stream 25 | { 26 | if (directoryCallback == null) directoryCallback = info => { }; 27 | if (fileStartCallback == null) fileStartCallback = info => null; 28 | if (fileEndCallback == null) fileEndCallback = (info, arg2) => { }; 29 | 30 | // we've missed original event, so, emulate it for client manually 31 | if (!blazerStream.HaveMultipleFiles) 32 | { 33 | if ((blazerStream.FileInfo.Attributes & FileAttributes.Directory) != 0) 34 | directoryCallback(blazerStream.FileInfo); 35 | else 36 | { 37 | var s = fileStartCallback(blazerStream.FileInfo); 38 | if (s != null) 39 | blazerStream.CopyTo(s); 40 | fileEndCallback(blazerStream.FileInfo, s); 41 | } 42 | 43 | return; 44 | } 45 | 46 | T[] outFile = new T[1]; 47 | BlazerFileInfo prevFile = null; 48 | 49 | blazerStream.FileInfoCallback = fInfo => 50 | { 51 | if (prevFile != null) 52 | { 53 | if (fileEndCallback != null) fileEndCallback(prevFile, outFile[0]); 54 | prevFile = null; 55 | } 56 | 57 | prevFile = fInfo; 58 | if ((fInfo.Attributes & FileAttributes.Directory) != 0) 59 | { 60 | directoryCallback(fInfo); 61 | } 62 | else 63 | { 64 | outFile[0] = fileStartCallback(fInfo); 65 | } 66 | }; 67 | 68 | var buf = new byte[blazerStream.MaxUncompressedBlockSize]; 69 | while (true) 70 | { 71 | var cnt = blazerStream.Read(buf, 0, buf.Length); 72 | if (cnt == 0) 73 | break; 74 | if (outFile[0] != null) 75 | outFile[0].Write(buf, 0, cnt); 76 | } 77 | 78 | if (prevFile != null && ((prevFile.Attributes & FileAttributes.Directory) == 0) && outFile[0] != null) 79 | { 80 | fileEndCallback(prevFile, outFile[0]); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Blazer.Net/Helpers/DataArrayCompressorHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | using Force.Blazer.Algorithms; 4 | 5 | namespace Force.Blazer.Helpers 6 | { 7 | /// 8 | /// Helper class for creating compressed arrays with data with length prefix 9 | /// Can be useful for resources preparation 10 | /// No algorithm information is stored 11 | /// 12 | public static class DataArrayCompressorHelper 13 | { 14 | /// 15 | /// Compresses source data with specified encoder 16 | /// 17 | /// array of source data 18 | /// selected encoder 19 | /// array of compressed data 20 | public static byte[] CompressDataToArray(byte[] sourceData, IEncoder encoder) 21 | { 22 | return CompressDataToArray(sourceData, 0, sourceData.Length, encoder); 23 | } 24 | 25 | /// 26 | /// Compresses source data with specified encoder 27 | /// 28 | /// array of source data 29 | /// offset of array of source data 30 | /// count data to read from array of source data 31 | /// selected encoder 32 | /// array of compressed data 33 | public static byte[] CompressDataToArray(byte[] sourceData, int offset, int count, IEncoder encoder) 34 | { 35 | encoder.Init(sourceData.Length); 36 | var res = encoder.Encode(sourceData, offset, offset + count); 37 | var targetBuf = res.ExtractToSeparateArray(4); 38 | targetBuf[0] = (byte)count; 39 | targetBuf[1] = (byte)(count >> 8); 40 | targetBuf[2] = (byte)(count >> 16); 41 | targetBuf[3] = (byte)(count >> 24); 42 | return targetBuf; 43 | } 44 | 45 | /// 46 | /// Compresses source data with specified encoder and writes to stream 47 | /// 48 | /// array of source data 49 | /// selected encoder 50 | /// stream to write data 51 | public static void CompressDataToArrayAndWriteToStream(byte[] sourceData, IEncoder encoder, Stream outStream) 52 | { 53 | CompressDataToArrayAndWriteToStream(sourceData, 0, sourceData.Length, encoder, outStream); 54 | } 55 | 56 | /// 57 | /// Compresses source data with specified encoder and writes to stream 58 | /// 59 | /// array of source data 60 | /// offset of array of source data 61 | /// count data to read from array of source data 62 | /// selected encoder 63 | /// stream to write data 64 | public static void CompressDataToArrayAndWriteToStream(byte[] sourceData, int offset, int count, IEncoder encoder, Stream outStream) 65 | { 66 | encoder.Init(sourceData.Length); 67 | var res = encoder.Encode(sourceData, offset, offset + count); 68 | outStream.Write(new[] { (byte)count, (byte)(count >> 8), (byte)(count >> 16), (byte)(count >> 24) }, 0, 4); 69 | outStream.Write(res.Buffer, res.Offset, res.Count); 70 | } 71 | 72 | /// 73 | /// Decompresses data with specified decoder 74 | /// 75 | /// array of compressed data with length prefix 76 | /// selected decoder 77 | /// array of compressed data 78 | public static byte[] DecompressDataArray(byte[] comprData, IDecoder decoder) 79 | { 80 | return DecompressDataArray(comprData, 0, comprData.Length, decoder); 81 | } 82 | 83 | /// 84 | /// Decompresses data with specified decoder 85 | /// 86 | /// array of compressed data with length prefix 87 | /// offset of array of source data 88 | /// count data to read from array of source data 89 | /// selected decoder 90 | /// array of compressed data 91 | public static byte[] DecompressDataArray(byte[] comprData, int offset, int count, IDecoder decoder) 92 | { 93 | var uncomprLength = comprData[offset] | (comprData[offset + 1] << 8) | (comprData[offset + 2] << 16) | (comprData[offset + 3] << 24); 94 | decoder.Init(uncomprLength); 95 | var decoded = decoder.Decode(comprData, offset + 4, offset + count, true); 96 | return decoded.ExtractToSeparateArray(); 97 | } 98 | 99 | /// 100 | /// Decompresses data with specified decoder into stream 101 | /// 102 | /// array of compressed data with length prefix 103 | /// selected decoder 104 | /// Stream with uncompressed data 105 | public static Stream DecompressDataArrayToReadableStream(byte[] comprData, IDecoder decoder) 106 | { 107 | return DecompressDataArrayToReadableStream(comprData, 0, comprData.Length, decoder); 108 | } 109 | 110 | /// 111 | /// Decompresses data with specified decoder into stream 112 | /// 113 | /// array of compressed data with length prefix 114 | /// offset of array of source data 115 | /// count data to read from array of source data 116 | /// selected decoder 117 | /// Stream with uncompressed data 118 | public static Stream DecompressDataArrayToReadableStream(byte[] comprData, int offset, int count, IDecoder decoder) 119 | { 120 | var uncomprLength = comprData[offset] | (comprData[offset + 1] << 8) | (comprData[offset + 2] << 16) | (comprData[offset + 3] << 24); 121 | decoder.Init(uncomprLength); 122 | var decoded = decoder.Decode(comprData, offset + 4, offset + count, true); 123 | return new MemoryStream(decoded.Buffer, decoded.Offset, decoded.Count); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Blazer.Net/Helpers/FileHeaderHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Force.Blazer.Helpers 6 | { 7 | internal static class FileHeaderHelper 8 | { 9 | private static readonly DateTime MinFileTime = new DateTime(1601, 1, 1); 10 | 11 | public static byte[] GenerateFileHeader(BlazerFileInfo info) 12 | { 13 | if (info.FileName == null) info.FileName = string.Empty; 14 | if (info.CreationTimeUtc < MinFileTime) 15 | info.CreationTimeUtc = MinFileTime; 16 | if (info.LastWriteTimeUtc < MinFileTime) 17 | info.LastWriteTimeUtc = MinFileTime; 18 | 19 | var fileNameBytes = Encoding.UTF8.GetBytes(info.FileName); 20 | var totalHeader = new byte[8 + // length 21 | 8 + // creation time 22 | 8 + // last write time 23 | 2 + // attributes 24 | fileNameBytes.Length]; 25 | 26 | Buffer.BlockCopy(BitConverter.GetBytes(info.Length), 0, totalHeader, 0, 8); 27 | Buffer.BlockCopy(BitConverter.GetBytes(info.CreationTimeUtc.ToFileTimeUtc()), 0, totalHeader, 0 + 8, 8); 28 | Buffer.BlockCopy(BitConverter.GetBytes(info.LastWriteTimeUtc.ToFileTimeUtc()), 0, totalHeader, 0 + 8 + 8, 8); 29 | totalHeader[0 + 8 + 8 + 8] = (byte)info.Attributes; 30 | totalHeader[0 + 8 + 8 + 8 + 1] = (byte)((int)info.Attributes >> 8); 31 | Buffer.BlockCopy(fileNameBytes, 0, totalHeader, 0 + 8 + 8 + 8 + 2, fileNameBytes.Length); 32 | return totalHeader; 33 | } 34 | 35 | public static BlazerFileInfo ParseFileHeader(byte[] header, int offset, int count) 36 | { 37 | const int NonNameSize = 8 + 8 + 8 + 2; 38 | if (count < NonNameSize) 39 | throw new InvalidOperationException("Invalid file header"); 40 | var fileInfo = new BlazerFileInfo(); 41 | fileInfo.Length = BitConverter.ToInt64(header, offset); 42 | fileInfo.CreationTimeUtc = DateTime.FromFileTimeUtc(BitConverter.ToInt64(header, 8 + offset)); 43 | fileInfo.LastWriteTimeUtc = DateTime.FromFileTimeUtc(BitConverter.ToInt64(header, 8 + 8 + offset)); 44 | fileInfo.Attributes = (FileAttributes)BitConverter.ToInt16(header, 8 + 8 + 8 + offset); 45 | fileInfo.FileName = Encoding.UTF8.GetString(header, NonNameSize + offset, count - NonNameSize); 46 | return fileInfo; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Blazer.Net/Native/NativeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Force.Blazer.Native 7 | { 8 | /// 9 | /// Helper for native implementation ofr Blazer algorithms 10 | /// 11 | public static class NativeHelper 12 | { 13 | // check tests if changed 14 | private const string NativeSuffix = "0.8.3.9"; 15 | 16 | #if NETCORE 17 | private const string ResourcePrefix = "Blazer.Net.Resources.Blazer.Native."; 18 | #else 19 | private const string ResourcePrefix = "Force.Blazer.Resources.Blazer.Native."; 20 | #endif 21 | 22 | private static readonly bool _isNativePossible; 23 | 24 | [DllImport("Kernel32.dll")] 25 | private static extern IntPtr LoadLibrary(string path); 26 | 27 | /// 28 | /// Returns is native library is available for usage 29 | /// 30 | public static bool IsNativeAvailable { get; private set; } 31 | 32 | static NativeHelper() 33 | { 34 | _isNativePossible = Init(); 35 | IsNativeAvailable = _isNativePossible; 36 | } 37 | 38 | private static bool Init() 39 | { 40 | try 41 | { 42 | InitInternal(); 43 | return true; 44 | } 45 | catch (Exception) // will use software realization 46 | { 47 | return false; 48 | } 49 | } 50 | 51 | private static void InitInternal() 52 | { 53 | var architectureSuffix = IntPtr.Size == 8 ? "x64" : "x86"; 54 | #if NETCORE 55 | var assembly = typeof(NativeHelper).GetTypeInfo().Assembly; 56 | #else 57 | var assembly = typeof(NativeHelper).Assembly; 58 | #endif 59 | using (var stream = 60 | assembly.GetManifestResourceStream(ResourcePrefix + architectureSuffix + ".dll")) 61 | { 62 | if (stream == null) 63 | throw new InvalidOperationException("Missing resource stream"); 64 | 65 | #if NETCORE 66 | var assemblyName = assembly.GetName(); 67 | #else 68 | var assemblyName = assembly.GetName(false); 69 | #endif 70 | 71 | var dllPath = Path.Combine(Path.GetTempPath(), assemblyName.Name + "." + NativeSuffix, architectureSuffix); 72 | 73 | var fileName = Path.Combine(dllPath, "Blazer.Native.dll"); 74 | if (!File.Exists(fileName) || new FileInfo(fileName).Length == 0) 75 | { 76 | Directory.CreateDirectory(dllPath); 77 | using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, 16386/*, FileOptions.DeleteOnClose*/)) 78 | { 79 | stream.CopyTo(fs); 80 | } 81 | } 82 | 83 | if (LoadLibrary(fileName) == IntPtr.Zero) 84 | throw new InvalidOperationException("Unexpected error in dll loading"); 85 | } 86 | } 87 | 88 | /// 89 | /// Sets native implementation is enabled. 90 | /// 91 | /// Native implementation can be turned off manually. If current environment does not support native implementation, software will be used anyway 92 | public static void SetNativeImplementation(bool isEnable) 93 | { 94 | IsNativeAvailable = isEnable && _isNativePossible; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Blazer.Net/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Blazer.Net")] 8 | [assembly: AssemblyDescription("Blazer archiver .NET library")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Force")] 11 | [assembly: AssemblyProduct("Blazer Archiver")] 12 | [assembly: AssemblyCopyright("Copyright © Force 2016-2018")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("05672ef8-45b2-45c1-b0bb-853142618ab4")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("0.10.0.0")] 35 | [assembly: AssemblyFileVersion("0.10.1.14")] 36 | -------------------------------------------------------------------------------- /Blazer.Net/pack.cmd: -------------------------------------------------------------------------------- 1 | C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe ..\Blazer.sln /t:Rebuild /p:Configuration=Release /p:SolutionDir=%~dp0..\ 2 | dotnet build -c BuildCore -f .NETStandard1.3 project.json 3 | "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\sn.exe" -R bin\Release\Blazer.NET.dll ..\private.snk 4 | "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\sn.exe" -R bin\BuildCore\netstandard1.3\Blazer.NET.dll ..\private.snk 5 | 6 | ..\.nuget\nuget.exe pack Blazer.Net.nuspec 7 | xcopy *.nupkg _releases 8 | del *.nupkg 9 | -------------------------------------------------------------------------------- /Blazer.Net/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.10.1", 3 | "frameworks": { 4 | "net40": { 5 | }, 6 | "netstandard1.3": { 7 | "buildOptions": { 8 | "define": ["NETCORE"] 9 | }, 10 | "dependencies": { 11 | "NETStandard.Library": "1.6.1" 12 | } 13 | } 14 | }, 15 | "buildOptions": { 16 | "embed": ["Resources/Blazer.Native.x64.dll", "Resources/Blazer.Native.x86.dll"], 17 | "xmlDoc": true, 18 | "keyFile": "../public.snk", 19 | "delaySign": true, 20 | "optimize": true 21 | }, 22 | "configurations": { 23 | "BuildCore": { 24 | "optimize": true 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Blazer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Blazer.Native", "Blazer.Native\Blazer.Native.vcxproj", "{A649C696-0A11-42C2-B66D-7645DCE20AFE}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazer.Net", "Blazer.Net\Blazer.Net.csproj", "{026EE2B9-3367-480A-8B46-118F4037C827}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazer.Benchmark", "Blazer.Benchmark\Blazer.Benchmark.csproj", "{C3B4452D-103F-4191-A3FC-7188D7F9D082}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1492CDF4-0A8F-46AE-828A-C6C98C7D52BC}" 11 | ProjectSection(SolutionItems) = preProject 12 | .nuget\NuGet.Config = .nuget\NuGet.Config 13 | .nuget\NuGet.exe = .nuget\NuGet.exe 14 | .nuget\NuGet.targets = .nuget\NuGet.targets 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazer.Exe", "Blazer.Exe\Blazer.Exe.csproj", "{D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazer.Net.Tests", "Blazer.Net.Tests\Blazer.Net.Tests.csproj", "{7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Debug|AnyCPU = Debug|AnyCPU 25 | Debug|Mixed Platforms = Debug|Mixed Platforms 26 | Debug|x64 = Debug|x64 27 | Debug|x86 = Debug|x86 28 | Release|Any CPU = Release|Any CPU 29 | Release|AnyCPU = Release|AnyCPU 30 | Release|Mixed Platforms = Release|Mixed Platforms 31 | Release|x64 = Release|x64 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|Any CPU.ActiveCfg = Debug|Win32 36 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|AnyCPU.ActiveCfg = Debug|x64 37 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 38 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|x64.ActiveCfg = Debug|x64 39 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|x64.Build.0 = Debug|x64 40 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Debug|x86.ActiveCfg = Debug|Win32 41 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|Any CPU.ActiveCfg = Release|Win32 42 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|AnyCPU.ActiveCfg = Release|x64 43 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|AnyCPU.Build.0 = Release|x64 44 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|Mixed Platforms.ActiveCfg = Release|x64 45 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|x64.ActiveCfg = Release|x64 46 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|x64.Build.0 = Release|x64 47 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|x86.ActiveCfg = Release|Win32 48 | {A649C696-0A11-42C2-B66D-7645DCE20AFE}.Release|x86.Build.0 = Release|Win32 49 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU 52 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|AnyCPU.Build.0 = Debug|Any CPU 53 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 54 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 55 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|x64.ActiveCfg = Debug|Any CPU 56 | {026EE2B9-3367-480A-8B46-118F4037C827}.Debug|x86.ActiveCfg = Debug|Any CPU 57 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|AnyCPU.ActiveCfg = Release|Any CPU 60 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|AnyCPU.Build.0 = Release|Any CPU 61 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 62 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|Mixed Platforms.Build.0 = Release|Any CPU 63 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|x64.ActiveCfg = Release|Any CPU 64 | {026EE2B9-3367-480A-8B46-118F4037C827}.Release|x86.ActiveCfg = Release|Any CPU 65 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU 68 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|AnyCPU.Build.0 = Debug|Any CPU 69 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 70 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 71 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|x64.ActiveCfg = Debug|Any CPU 72 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Debug|x86.ActiveCfg = Debug|Any CPU 73 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|AnyCPU.ActiveCfg = Release|Any CPU 76 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|AnyCPU.Build.0 = Release|Any CPU 77 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 78 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|Mixed Platforms.Build.0 = Release|Any CPU 79 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|x64.ActiveCfg = Release|Any CPU 80 | {C3B4452D-103F-4191-A3FC-7188D7F9D082}.Release|x86.ActiveCfg = Release|Any CPU 81 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU 84 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|AnyCPU.Build.0 = Debug|Any CPU 85 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 86 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 87 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|x64.ActiveCfg = Debug|Any CPU 88 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Debug|x86.ActiveCfg = Debug|Any CPU 89 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|AnyCPU.ActiveCfg = Release|Any CPU 92 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|AnyCPU.Build.0 = Release|Any CPU 93 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 94 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|Mixed Platforms.Build.0 = Release|Any CPU 95 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|x64.ActiveCfg = Release|Any CPU 96 | {D1BE8FC3-7D77-4C08-A0D8-D2399A8D903F}.Release|x86.ActiveCfg = Release|Any CPU 97 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU 100 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|AnyCPU.Build.0 = Debug|Any CPU 101 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 102 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 103 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|x64.ActiveCfg = Debug|Any CPU 104 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Debug|x86.ActiveCfg = Debug|Any CPU 105 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 106 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|Any CPU.Build.0 = Release|Any CPU 107 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|AnyCPU.ActiveCfg = Release|Any CPU 108 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|AnyCPU.Build.0 = Release|Any CPU 109 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 110 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|Mixed Platforms.Build.0 = Release|Any CPU 111 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|x64.ActiveCfg = Release|Any CPU 112 | {7C1D5EDB-476B-453F-A7EE-E4E8CB8AB2CF}.Release|x86.ActiveCfg = Release|Any CPU 113 | EndGlobalSection 114 | GlobalSection(SolutionProperties) = preSolution 115 | HideSolutionNode = FALSE 116 | EndGlobalSection 117 | EndGlobal 118 | -------------------------------------------------------------------------------- /Doc/Images/chart_blocksize1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Doc/Images/chart_blocksize1.png -------------------------------------------------------------------------------- /Doc/Images/chart_comprrate1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Doc/Images/chart_comprrate1.png -------------------------------------------------------------------------------- /Doc/Images/chart_comprrate2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Doc/Images/chart_comprrate2.png -------------------------------------------------------------------------------- /Doc/Images/chart_pattern1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/Doc/Images/chart_pattern1.png -------------------------------------------------------------------------------- /Doc/PatternedCompression.md: -------------------------------------------------------------------------------- 1 | # Compression with preliminary pattern 2 | 3 | Imagine, you have lot of similar data. This data can be log messages, SOAP integration XMLs, status logs. And you want to save this data **independently** (if you don't need to do this, you can compress all messages into solid archive. It is best variant for compression, but as result all messages should be extracted if only one needed for view). 4 | These messages can be stored in file system or database, but you want to store each message separately to keep fast access to it. 5 | 6 | You want to compress this messages to save storage, but these messages are small and compress not very good as result. 7 | 8 | But Blazer has a feature to compress these messages with pattern. As pattern you can choose any of these messages or prepare it manually to include more data. 9 | 10 | ``` 11 | // creating patterned compressor 12 | var p = BlazerPatternedHelper.CreateStream(); 13 | // one-time init of data 14 | p.PreparePattern(patternedData); 15 | 16 | // every time you want to compress data, use similar line 17 | p.EncodeWithPattern(dataToCompress) 18 | 19 | // same code for decompress 20 | p.DecodeWithPattern(dataToDecompress) 21 | ``` 22 | 23 | ## What is benefit in numbers? 24 | This is complex question. If all messages are same, every new message will be compressed in 3-6 bytes and benefit will be fantastic. 25 | If all messages are totally different, there are no benefits for this compression type. 26 | 27 | Also, there are some philosophic question for pattern selection. It can be any message (e.g. first) or specially constructed template. This can increase compression rate for some per cents. 28 | 29 | For example, we can choose messages with this format: 30 | 31 | ``` 32 | 33 | 34 | 2016-07-04T01:09:51 35 | INFO 36 | System 37 | 25 38 | xsxlnt e fg qamm 39 | 40 | ``` 41 | 42 | All fields can be changed, but in some limits (e.g. EventDate is always date, Level is INFO, DEBUG, ERROR or WARN). With these messages results are: 43 | 44 | ![Pattern Example](Images/chart_pattern1.png) 45 | 46 | Or in table view: 47 | 48 | Name | Rate (lower is better) 49 | --------------------|------------------------- 50 | GZip (only deflated block, without headers) | 66.266% 51 | Blazer Independent compression | 82.157% 52 | Blazer Patterned compression | **22.616%** 53 | Blazer Patterned compression with another template | **20.506%** 54 | 55 | So, patterned compression in this case is **3 times better** than GZip! (and really faster). 56 | 57 | _(You can find code which generates messages and produces results in sources)_ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 force 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /blazer-nuget-ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/blazer-nuget-ico.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test" ], 3 | "sdk": { 4 | "version": "1.0.0-preview2-1-003177" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/blazer/5520e017a2f5d1b55f5ab978036b138faa765e38/public.snk --------------------------------------------------------------------------------