12 Commits
1_0 ... Main

Author SHA1 Message Date
33cdfd77cf WIP 2022-10-19 23:31:57 +02:00
a072a86436 Update README.md 2021-06-17 03:07:30 +02:00
52a5737806 Rearrangements
Migrate to Sdk projects.
Migrate tests to xUnit.
Bump version 1.2.0.
Migrate to NetStandar2.0.
2021-06-17 03:04:17 +02:00
4b10a142c9 Fixes on README.md 2017-07-13 02:55:20 +02:00
99df93ff44 Bump version 1.1 2017-07-13 02:53:24 +02:00
6abc31a57c Update README.md 2017-07-13 02:53:09 +02:00
7069618a48 UrlCompressor: Add checksums for self-verification and to cause cascading changes on results. 2017-07-13 02:08:15 +02:00
a76cad1aa8 Base62.Decode: Crop extra bits while decoding, for the cases of 5 bits. 2017-07-13 02:07:06 +02:00
9fe35362fc UrlCompressor: Use typical host names for extra reduction. Can be user defined. 2017-07-13 00:01:05 +02:00
675b87e6ee Url class for url parsing. 2017-07-12 23:59:57 +02:00
39b62740c8 Huffman: Refactor EncodeChar. 2017-07-12 23:59:11 +02:00
a43a517a28 Change weights on HuffmanTree generation. 2017-07-12 23:58:17 +02:00
13 changed files with 704 additions and 309 deletions

View File

@@ -1,28 +1,57 @@
# .Net library for compressing URLs
## Instalation
You can install the NuGet package using this command on the package manager:
Install-Package VAR.UrlCompressor
Alternativelly you can copy and reference the assembly resulting of the project VAR.UrlCompressor.
## Usage
### VAR.UrlCompressor
Add the resulting assembly as reference in your projects, and this line on code:
```csharp
using VAR.UrlCompressor;
```
Compress an URL with:
```csharp
string compressedUrl = UrlCompressor.Compress("https:\\google.com");
// compressedUrl = "Hk30TGDxt8jOOW6"
```
Decompress an URL with:
string decompressedUrl = UrlCompressor.Compress("xGncYbYfopHYpG0");
```csharp
string decompressedUrl = UrlCompressor.Decompress("Hk30TGDxt8jOOW6");
// decompressedUrl = "https:\\google.com";
```
For extra compression use host conversions. For example:
```csharp
Dictionary<string, string> hostConversions = new Dictionary<string, string> {
{ "google", "G" }
{ "com", "C" }
}
string compressedUrl = UrlCompressor.Compress("https:\\google.com", );
// compressedUrl = "oMyuFVR41"
string decompressedUrl = UrlCompressor.Decompress("oMyuFVR41");
// decompressedUrl = "https:\\google.com";
```
### UrlCompressor.Tests
It is a simple console application, to test basic funcitionallity of the library.
## Building
A Visual Studio 2015 and 2010 solutions are provided. Simply, click build on the IDE.
A Visual Studio solution is provided. Simply, click build on the IDE.
A .nuget package can be build using:
VAR.UrlCompressor\Build.NuGet.cmd
The build generates a DLL and a Nuget package.
## Contributing
1. Fork it!
@@ -33,27 +62,3 @@ A .nuget package can be build using:
## Credits
* Valeriano Alfonso Rodriguez.
## License
The MIT License (MIT)
Copyright (c) 2016-2017 Valeriano Alfonso Rodriguez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,37 +0,0 @@
using System;
namespace UrlCompressor.Tests
{
class Program
{
static void Main(string[] args)
{
TestUrl("http://google.com");
TestUrl("https://google.com");
TestUrl("http://facebook.com");
TestUrl("https://facebook.com");
TestUrl("https://twitter.com");
TestUrl("https://twitter.com/Kableado");
TestUrl("https://github.com/Kableado");
TestUrl("https://varstudio.net");
Console.Read();
}
private static bool TestUrl(string url)
{
Console.WriteLine("---------------------------------------------");
Console.WriteLine(" Url: {0}", url);
string compressedUrl = VAR.UrlCompressor.UrlCompressor.Compress(url);
Console.WriteLine(" CompressedUrl: {0}", compressedUrl);
string decompressedUrl = VAR.UrlCompressor.UrlCompressor.Decompress(compressedUrl);
Console.WriteLine("DecompressedUrl: {0}", decompressedUrl);
if(url!= decompressedUrl)
{
Console.WriteLine("!!!!! Failed !!!!!");
return false;
}
return true;
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// La información general de un ensamblado se controla mediante el siguiente
// conjunto de atributos. Cambie estos valores de atributo para modificar la información
// asociada con un ensamblado.
[assembly: AssemblyTitle("UrlCompressor.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UrlCompressor.Tests")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Si establece ComVisible en false, los tipos de este ensamblado no estarán visibles
// para los componentes COM. Si es necesario obtener acceso a un tipo en este ensamblado desde
// COM, establezca el atributo ComVisible en true en este tipo.
[assembly: ComVisible(false)]
// El siguiente GUID sirve como id. de typelib si este proyecto se expone a COM.
[assembly: Guid("29d6812a-566d-4a92-a7a4-fc6ae0b3db39")]
// La información de versión de un ensamblado consta de los cuatro valores siguientes:
//
// Versión principal
// Versión secundaria
// Número de compilación
// Revisión
//
// Puede especificar todos los valores o utilizar los números de compilación y de revisión predeterminados
// mediante el carácter '*', como se muestra a continuación:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>UrlCompressor.Tests</RootNamespace>
<AssemblyName>UrlCompressor.Tests</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VAR.UrlCompressor\VAR.UrlCompressor.csproj">
<Project>{016ae05d-12af-40c6-8d0c-064970004f0b}</Project>
<Name>VAR.UrlCompressor</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,328 @@
using System.Collections.Generic;
using Xunit;
namespace VAR.UrlCompressor.Tests
{
public class UrlCompressorTests
{
#region Helpers
private Dictionary<string, string> GenerateHostsConversions()
{
Dictionary<string, string> hostConversions = new Dictionary<string, string> {
{ "com", "C" },
{ "net", "N" },
{ "org", "O" },
{ "localhost", "L" },
{ "azurewebsites", "A" },
{ "unifikas", "U" },
{ "cyc", "Y" },
{ "es", "E" },
{ "google", "G" },
{ "facebook", "F" },
{ "twitter", "T" },
{ "github", "GH" },
{ "varstudio", "V" },
};
return hostConversions;
}
#endregion Helpers
#region Compress
[Fact]
public void Compress__Http_Google_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://google.com";
string compressedUrl = "zBUW9UxS1";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Google_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://google.com";
string compressedUrl = "oMyuFVR41";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Http_Facebook_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://facebook.com";
string compressedUrl = "EAyWnGuy0";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Facebook_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://facebook.com";
string compressedUrl = "2AyutHOa0";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Twitter_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://twitter.com";
string compressedUrl = "bpzuhuDB1";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Twitter_Com_Kableado()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://twitter.com/Kableado";
string compressedUrl = "zYfD7gXd8ApW9HZtR4";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Github_Com_Kableado()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://github.com/Kableado";
string compressedUrl = "JzJv5CTnllbrEVp1BhF";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Http_Localhost_30000_0()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://localhost:30000|0";
string compressedUrl = "0fT4k50uE3WwvqMB42";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Http_Localhost_30000_1()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://localhost:30000|0";
string compressedUrl = "0fT4k50uE3WwvqMB42";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Unifikas_Azurewebsites_Net_1()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://unifikas.azurewebsites.net|1";
string compressedUrl = "7R15qGtuLPGD";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Unifikas_Azurewebsites_Net_2()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://unifikas.azurewebsites.net|2";
string compressedUrl = "7B54q0pvLPKC";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
[Fact]
public void Compress__Https_Varstudio_Net()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://varstudio.net";
string compressedUrl = "5B1u05oK";
string result = UrlCompressor.Compress(url, hostConversions);
Assert.Equal(compressedUrl, result);
}
#endregion Compress
#region Decompress
[Fact]
public void Decompress__Http_Google_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://google.com";
string compressedUrl = "zBUW9UxS1";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Google_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://google.com";
string compressedUrl = "oMyuFVR41";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Http_Facebook_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://facebook.com";
string compressedUrl = "EAyWnGuy0";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Facebook_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://facebook.com";
string compressedUrl = "2AyutHOa0";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Twitter_Com()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://twitter.com";
string compressedUrl = "bpzuhuDB1";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Twitter_Com_Kableado()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://twitter.com/Kableado";
string compressedUrl = "zYfD7gXd8ApW9HZtR4";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Github_Com_Kableado()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://github.com/Kableado";
string compressedUrl = "JzJv5CTnllbrEVp1BhF";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Http_Localhost_30000_0()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://localhost:30000|0";
string compressedUrl = "0fT4k50uE3WwvqMB42";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Http_Localhost_30000_1()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "http://localhost:30000|0";
string compressedUrl = "0fT4k50uE3WwvqMB42";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Unifikas_Azurewebsites_Net_1()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://unifikas.azurewebsites.net|1";
string compressedUrl = "7R15qGtuLPGD";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Unifikas_Azurewebsites_Net_2()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://unifikas.azurewebsites.net|2";
string compressedUrl = "7B54q0pvLPKC";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
[Fact]
public void Decompress__Https_Varstudio_Net()
{
Dictionary<string, string> hostConversions = GenerateHostsConversions();
string url = "https://varstudio.net";
string compressedUrl = "5B1u05oK";
string result = UrlCompressor.Decompress(compressedUrl, hostConversions);
Assert.Equal(url, result);
}
#endregion Decompress
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VAR.UrlCompressor\VAR.UrlCompressor.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,34 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.15
# Visual Studio Version 16
VisualStudioVersion = 16.0.31402.337
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.UrlCompressor", "VAR.UrlCompressor\VAR.UrlCompressor.csproj", "{016AE05D-12AF-40C6-8D0C-064970004F0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlCompressor.Tests", "UrlCompressor.Tests\UrlCompressor.Tests.csproj", "{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notes", "Notes", "{C6BD1525-8B61-4E6A-8146-A18B4468842E}"
ProjectSection(SolutionItems) = preProject
LICENSE.txt = LICENSE.txt
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VAR.UrlCompressor.Tests", "VAR.UrlCompressor.Tests\VAR.UrlCompressor.Tests.csproj", "{845C1853-EB77-45DA-8388-A7A4F866B3EE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Debug|Any CPU.ActiveCfg = Debug .Net 4.6.1|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Debug|Any CPU.Build.0 = Debug .Net 4.6.1|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Release|Any CPU.ActiveCfg = Release .Net 4.6.1|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Release|Any CPU.Build.0 = Release .Net 4.6.1|Any CPU
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Release|Any CPU.Build.0 = Release|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Release|Any CPU.Build.0 = Release|Any CPU
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1843DBAF-8D99-42B8-A5AE-D44A48FAFD71}
EndGlobalSection
EndGlobal

View File

@@ -53,6 +53,7 @@ namespace VAR.UrlCompressor
for (int i = 0; i < base62.Length; i++)
{
int charIdx = Base62CodingSpace.IndexOf(base62[i]);
if (charIdx == -1) { continue; }
if ((i + 1) == base62.Length)
{
// Last symbol
@@ -66,7 +67,7 @@ namespace VAR.UrlCompressor
bytes.WriteBit(bitPosition, 3 - pad, (charIdx & 0x04) > 0);
bytes.WriteBit(bitPosition, 4 - pad, (charIdx & 0x02) > 0);
bytes.WriteBit(bitPosition, 5 - pad, (charIdx & 0x01) > 0);
bitPosition += (6 - pad);
break;
}
@@ -100,6 +101,8 @@ namespace VAR.UrlCompressor
}
}
Array.Resize(ref bytes, (int)Math.Ceiling((double)bitPosition / 8));
return bytes;
}
}

View File

@@ -65,32 +65,36 @@ namespace VAR.UrlCompressor
}
}
private int _bitPosition = 0;
private List<bool> _encodedSymbol = new List<bool>();
private byte[] _scratch = null;
private void EncodeChar(char data)
{
_encodedSymbol.Clear();
_encodedSymbol = Root.Traverse(data, _encodedSymbol);
foreach (bool v in _encodedSymbol)
{
_scratch.WriteBit(_bitPosition, 0, v);
_bitPosition++;
}
}
public byte[] Encode(byte[] data)
{
byte[] scratch = new byte[data.Length * 2];
int bitPosition = 0;
var encodedSymbol = new List<bool>();
_scratch = new byte[data.Length * 2];
_bitPosition = 0;
for (int i = 0; i < data.Length; i++)
{
encodedSymbol.Clear();
encodedSymbol = Root.Traverse((char)data[i], encodedSymbol);
foreach(bool v in encodedSymbol)
{
scratch.WriteBit(bitPosition, 0, v);
bitPosition++;
EncodeChar((char)data[i]);
}
}
encodedSymbol.Clear();
encodedSymbol = Root.Traverse(EOD, encodedSymbol);
foreach (bool v in encodedSymbol)
{
scratch.WriteBit(bitPosition, 0, v);
bitPosition++;
}
int byteLenght = (int)Math.Ceiling((double)bitPosition / 8);
EncodeChar(EOD);
int byteLenght = (int)Math.Ceiling((double)_bitPosition / 8);
byte[] compressedData = new byte[byteLenght];
Array.Copy(scratch, compressedData, byteLenght);
Array.Copy(_scratch, compressedData, byteLenght);
_scratch = null;
return compressedData;
}
@@ -98,15 +102,15 @@ namespace VAR.UrlCompressor
public byte[] Decode(byte[] data)
{
HuffmanTreeNode current = Root;
byte[] scratch = new byte[data.Length];
int bitPosition = 0;
_scratch = new byte[data.Length];
_bitPosition = 0;
int bytePosition = 0;
int lenght = data.Length * 8;
while (bitPosition < lenght)
while (_bitPosition < lenght)
{
bool bit = data.ReadBit(bitPosition, 0);
bitPosition++;
bool bit = data.ReadBit(_bitPosition, 0);
_bitPosition++;
if (bit)
{
if (current.Right != null)
@@ -125,14 +129,15 @@ namespace VAR.UrlCompressor
if (current.IsLeaf())
{
if (current.Symbol == EOD) { break; }
scratch = scratch.WriteByte(bytePosition, (byte)current.Symbol);
_scratch = _scratch.WriteByte(bytePosition, (byte)current.Symbol);
bytePosition++;
current = Root;
}
}
byte[] decompressedData = new byte[bytePosition];
Array.Copy(scratch, decompressedData, bytePosition);
Array.Copy(_scratch, decompressedData, bytePosition);
_scratch = null;
return decompressedData;
}

View File

@@ -1,14 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VAR.UrlCompressor")]
[assembly: AssemblyDescription(".Net library for compressing URLs")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("VAR")]
[assembly: AssemblyProduct("VAR.UrlCompressor")]
[assembly: AssemblyCopyright("Copyright © VAR 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("016ae05d-12af-40c6-8d0c-064970004f0b")]
[assembly: AssemblyVersion("1.0.*")]

136
VAR.UrlCompressor/Url.cs Normal file
View File

@@ -0,0 +1,136 @@
using System;
namespace VAR.UrlCompressor
{
class Url
{
public string Protocol { get; set; }
public string Host { get; set; }
public string Port { get; set; }
public string Path { get; set; }
private Url() { }
private enum ParseStatus
{
ParsingProtocol,
ParsingHost,
ParsingPort,
ParsingPath,
};
private ParseStatus _status = ParseStatus.ParsingProtocol;
private int _i0 = 0;
private int _i = 0;
private void ResetParser()
{
_status = ParseStatus.ParsingProtocol;
_i0 = 0;
_i = 0;
}
private void ParseUrl(string url)
{
while (_i < url.Length)
{
switch (_status)
{
case ParseStatus.ParsingProtocol:
if (url[_i] == ':')
{
Protocol = url.Substring(_i0, _i - _i0);
if (_i + 2 >= url.Length)
{
throw new Exception(string.Format("Unexpected end of URL, while parsing protocol. \"{0}\"", url));
}
if (url[_i + 1] != '/' || url[_i + 2] != '/')
{
throw new Exception(string.Format("Unexpected end of URL, while parsing protocol. \"{0}\"", url));
}
_status = ParseStatus.ParsingHost;
_i0 = _i + 3;
_i = _i0;
}
break;
case ParseStatus.ParsingHost:
if (char.IsLetterOrDigit(url[_i]) == false && url[_i] != '.')
{
Host = url.Substring(_i0, _i - _i0);
_i0 = _i;
if (url[_i] == ':')
{
_status = ParseStatus.ParsingPort;
}
else
{
_status = ParseStatus.ParsingPath;
}
}
break;
case ParseStatus.ParsingPort:
if (char.IsDigit(url[_i]) == false)
{
Port = url.Substring(_i0, _i - _i0);
_i0 = _i;
_status = ParseStatus.ParsingPath;
}
break;
case ParseStatus.ParsingPath:
_i = url.Length - 1;
break;
}
_i++;
}
switch (_status)
{
case ParseStatus.ParsingProtocol:
Protocol = url.Substring(_i0, _i - _i0);
break;
case ParseStatus.ParsingHost:
Host = url.Substring(_i0, _i - _i0);
break;
case ParseStatus.ParsingPort:
Port = url.Substring(_i0, _i - _i0);
break;
case ParseStatus.ParsingPath:
Path = url.Substring(_i0, _i - _i0);
break;
}
}
public static Url CreateFromString(string url)
{
var newUrl = new Url();
newUrl.ParseUrl(url);
return newUrl;
}
public static Url CreateFromShortString(string url)
{
var newUrl = new Url()
{
Protocol = url[0].ToString(),
_status = ParseStatus.ParsingHost,
_i = 1,
_i0 = 1,
};
newUrl.ParseUrl(url);
return newUrl;
}
public override string ToString()
{
return string.Format("{0}://{1}{2}{3}", Protocol, Host, Port, Path);
}
public string ToShortString()
{
return string.Format("{0}{1}{2}{3}", Protocol[0], Host, Port, Path);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text;
namespace VAR.UrlCompressor
@@ -81,21 +82,21 @@ namespace VAR.UrlCompressor
{ '8', 1000},
{ '9', 1000},
// Common simbols
// Common symbols
{ ' ', 100},
{ '!', 100},
{ '"', 100},
{ '#', 50000}, // NOTE: Exagerate to minimize bitstream of this symbol '#'
{ '$', 50000}, // NOTE: Exagerate to minimize bitstream of this symbol '$'
{ '#', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '#'
{ '$', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '$'
{ '%', 100},
{ '&', 100},
{ '\'', 100},
{ '\'', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '/'
{ '(', 100},
{ '*', 100},
{ '+', 100},
{ ',', 100},
{ '-', 100},
{ '.', 100},
{ '.', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '.'
{ '/', 100},
{ ':', 100},
{ ';', 100},
@@ -118,46 +119,120 @@ namespace VAR.UrlCompressor
_huffmanTree = new HuffmanTree(frequencies);
}
public static string Compress(string url)
private static void XorData(byte[] data, byte xorKey)
{
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)(data[i] ^ xorKey);
}
}
private static byte ChecksumCalculate(byte[] data)
{
byte checksum = 0;
foreach(byte b in data)
{
checksum = (byte)(checksum ^ b);
}
return checksum;
}
private static byte[] ChecksumAdd(byte[] data)
{
byte[] newData = new byte[data.Length + 1];
byte checksum = ChecksumCalculate(data);
XorData(data, checksum);
Array.Copy(data, 0, newData, 1, data.Length);
newData[0] = checksum;
return newData;
}
private static byte[] ChecksumCheck(byte[] data)
{
byte[] newData = new byte[data.Length - 1];
Array.Copy(data, 1, newData, 0, data.Length - 1);
byte oldChecksum = data[0];
XorData(newData, oldChecksum);
byte checksum = ChecksumCalculate(newData);
if (checksum != oldChecksum) { throw new Exception("Checksum mismatch."); }
return newData;
}
public static string Compress(string url, Dictionary<string, string> hostConversions = null)
{
InitHuffmanTree();
// Replace protocol indicator
if (url.StartsWith("https://") || url.StartsWith("HTTPS://"))
Url oUrl = Url.CreateFromString(url);
// "Compress" protocol
if (oUrl.Protocol == "http" || oUrl.Protocol == null) { oUrl.Protocol = "#"; }
else if (oUrl.Protocol == "https") { oUrl.Protocol = "$"; }
else if (oUrl.Protocol == "ftp") { oUrl.Protocol = "F"; }
else { throw new Exception(string.Format("Unkown protocol \"{0}\"", oUrl.Protocol)); }
if (hostConversions != null)
{
url = string.Format("${0}", url.Substring("https://".Length));
// "Compress" hosts
string[] urlHostParts = oUrl.Host.Split('.');
for (int i = 0; i < urlHostParts.Length; i++)
{
foreach (KeyValuePair<string, string> hostConversion in hostConversions)
{
if (urlHostParts[i] == hostConversion.Key)
{
urlHostParts[i] = hostConversion.Value;
break;
}
if (url.StartsWith("http://") || url.StartsWith("HTTP://"))
{
url = string.Format("#{0}", url.Substring("http://".Length));
}
}
oUrl.Host = string.Join(".", urlHostParts);
}
url = oUrl.ToShortString();
// Reduce entropy
byte[] urlBytes = Encoding.ASCII.GetBytes(url);
byte[] compressedUrlBytes = _huffmanTree.Encode(urlBytes);
return Base62.Encode(compressedUrlBytes);
urlBytes = _huffmanTree.Encode(urlBytes);
urlBytes = ChecksumAdd(urlBytes);
return Base62.Encode(urlBytes);
}
public static string Decompress(string compressedUrl)
public static string Decompress(string compressedUrl, Dictionary<string, string> hostConversions = null)
{
InitHuffmanTree();
byte[] urlBytes = Base62.Decode(compressedUrl);
urlBytes = ChecksumCheck(urlBytes);
urlBytes = _huffmanTree.Decode(urlBytes);
string url = Encoding.ASCII.GetString(urlBytes);
byte[] decompressedUrlBytes = _huffmanTree.Decode(urlBytes);
Url oUrl = Url.CreateFromShortString(url);
string url = Encoding.ASCII.GetString(decompressedUrlBytes);
// "Decompress" protocol
if (oUrl.Protocol == "#") { oUrl.Protocol = "http"; }
else if (oUrl.Protocol == "$") { oUrl.Protocol = "https"; }
else if (oUrl.Protocol == "F") { oUrl.Protocol = "ftp"; }
else { throw new Exception(string.Format("Unkown protocol \"{0}\"", oUrl.Protocol)); }
// Restore protocol indicator
if (url.StartsWith("#"))
if (hostConversions != null)
{
url = string.Format("http://{0}", url.Substring(1));
}
if (url.StartsWith("$"))
// "Decompress" hosts
string[] urlHostParts = oUrl.Host.Split('.');
for (int i = 0; i < urlHostParts.Length; i++)
{
url = string.Format("https://{0}", url.Substring(1));
foreach (KeyValuePair<string, string> hostConversion in hostConversions)
{
if (urlHostParts[i] == hostConversion.Value)
{
urlHostParts[i] = hostConversion.Key;
break;
}
}
}
oUrl.Host = string.Join(".", urlHostParts);
}
url = oUrl.ToString();
return url;
}

View File

@@ -1,73 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{016AE05D-12AF-40C6-8D0C-064970004F0B}</ProjectGuid>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VAR.UrlCompressor</RootNamespace>
<AssemblyName>VAR.UrlCompressor</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsPackable>true</IsPackable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug .Net 4.6.1|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\net461\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 4.6.1|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\net461\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug .Net 3.5|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\net35\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release .Net 3.5|AnyCPU'">
<OutputPath>bin\Release\net35\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<PropertyGroup>
<PackageId>VAR.UrlCompressor</PackageId>
<Title>VAR.UrlCompressor</Title>
<Version>1.2.0</Version>
<Description>.Net library for compressing URLs</Description>
<Authors>VAR</Authors>
<Company>VAR</Company>
<Copyright>Copyright © VAR 2016-2017</Copyright>
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageProjectUrl>https://github.com/Kableado/VAR.UrlCompressor</PackageProjectUrl>
<PackageTags>URL Compression Library</PackageTags>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Content Include="..\LICENSE.txt" Link="LICENSE.txt" Pack="true" PackagePath=""/>
</ItemGroup>
<ItemGroup>
<Compile Include="Base62.cs" />
<Compile Include="ByteExtensions.cs" />
<Compile Include="Huffman.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UrlCompressor.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Build.NuGet.cmd" />
<None Include="packages.config" />
<None Include="VAR.UrlCompressor.nuspec" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy
SourceFiles="$(OutputPath)..\$(PackageId).$(PackageVersion).nupkg"
DestinationFolder="Nuget\"
/>
</Target>
</Project>