Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33cdfd77cf | |||
| a072a86436 | |||
| 52a5737806 | |||
| 4b10a142c9 | |||
| 99df93ff44 | |||
| 6abc31a57c | |||
| 7069618a48 | |||
| a76cad1aa8 | |||
| 9fe35362fc | |||
| 675b87e6ee | |||
| 39b62740c8 | |||
| a43a517a28 |
67
README.md
67
README.md
@@ -1,28 +1,57 @@
|
|||||||
# .Net library for compressing URLs
|
# .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
|
## Usage
|
||||||
|
|
||||||
### VAR.UrlCompressor
|
### VAR.UrlCompressor
|
||||||
Add the resulting assembly as reference in your projects, and this line on code:
|
Add the resulting assembly as reference in your projects, and this line on code:
|
||||||
|
|
||||||
using VAR.UrlCompressor;
|
```csharp
|
||||||
|
using VAR.UrlCompressor;
|
||||||
|
```
|
||||||
|
|
||||||
Compress an URL with:
|
Compress an URL with:
|
||||||
|
|
||||||
string compressedUrl = UrlCompressor.Compress("https:\\google.com");
|
```csharp
|
||||||
|
string compressedUrl = UrlCompressor.Compress("https:\\google.com");
|
||||||
|
// compressedUrl = "Hk30TGDxt8jOOW6"
|
||||||
|
```
|
||||||
|
|
||||||
Decompress an URL with:
|
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
|
### UrlCompressor.Tests
|
||||||
It is a simple console application, to test basic funcitionallity of the library.
|
It is a simple console application, to test basic funcitionallity of the library.
|
||||||
|
|
||||||
## Building
|
## 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:
|
The build generates a DLL and a Nuget package.
|
||||||
VAR.UrlCompressor\Build.NuGet.cmd
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
1. Fork it!
|
1. Fork it!
|
||||||
@@ -33,27 +62,3 @@ A .nuget package can be build using:
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* Valeriano Alfonso Rodriguez.
|
* 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.
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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")]
|
|
||||||
@@ -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>
|
|
||||||
328
VAR.UrlCompressor.Tests/UrlCompressorTests.cs
Normal file
328
VAR.UrlCompressor.Tests/UrlCompressorTests.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
26
VAR.UrlCompressor.Tests/VAR.UrlCompressor.Tests.csproj
Normal file
26
VAR.UrlCompressor.Tests/VAR.UrlCompressor.Tests.csproj
Normal 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>
|
||||||
@@ -1,34 +1,37 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.26430.15
|
VisualStudioVersion = 16.0.31402.337
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.UrlCompressor", "VAR.UrlCompressor\VAR.UrlCompressor.csproj", "{016AE05D-12AF-40C6-8D0C-064970004F0B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.UrlCompressor", "VAR.UrlCompressor\VAR.UrlCompressor.csproj", "{016AE05D-12AF-40C6-8D0C-064970004F0B}"
|
||||||
EndProject
|
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}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notes", "Notes", "{C6BD1525-8B61-4E6A-8146-A18B4468842E}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
LICENSE.txt = LICENSE.txt
|
LICENSE.txt = LICENSE.txt
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VAR.UrlCompressor.Tests", "VAR.UrlCompressor.Tests\VAR.UrlCompressor.Tests.csproj", "{845C1853-EB77-45DA-8388-A7A4F866B3EE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
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.ActiveCfg = Debug|Any CPU
|
||||||
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Debug|Any CPU.Build.0 = Debug .Net 4.6.1|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 .Net 4.6.1|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 .Net 4.6.1|Any CPU
|
{016AE05D-12AF-40C6-8D0C-064970004F0B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{29D6812A-566D-4A92-A7A4-FC6AE0B3DB39}.Release|Any CPU.Build.0 = Release|Any CPU
|
{845C1853-EB77-45DA-8388-A7A4F866B3EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {1843DBAF-8D99-42B8-A5AE-D44A48FAFD71}
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace VAR.UrlCompressor
|
|||||||
for (int i = 0; i < base62.Length; i++)
|
for (int i = 0; i < base62.Length; i++)
|
||||||
{
|
{
|
||||||
int charIdx = Base62CodingSpace.IndexOf(base62[i]);
|
int charIdx = Base62CodingSpace.IndexOf(base62[i]);
|
||||||
|
if (charIdx == -1) { continue; }
|
||||||
if ((i + 1) == base62.Length)
|
if ((i + 1) == base62.Length)
|
||||||
{
|
{
|
||||||
// Last symbol
|
// Last symbol
|
||||||
@@ -66,7 +67,7 @@ namespace VAR.UrlCompressor
|
|||||||
bytes.WriteBit(bitPosition, 3 - pad, (charIdx & 0x04) > 0);
|
bytes.WriteBit(bitPosition, 3 - pad, (charIdx & 0x04) > 0);
|
||||||
bytes.WriteBit(bitPosition, 4 - pad, (charIdx & 0x02) > 0);
|
bytes.WriteBit(bitPosition, 4 - pad, (charIdx & 0x02) > 0);
|
||||||
bytes.WriteBit(bitPosition, 5 - pad, (charIdx & 0x01) > 0);
|
bytes.WriteBit(bitPosition, 5 - pad, (charIdx & 0x01) > 0);
|
||||||
|
bitPosition += (6 - pad);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +101,8 @@ namespace VAR.UrlCompressor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Array.Resize(ref bytes, (int)Math.Ceiling((double)bitPosition / 8));
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
public byte[] Encode(byte[] data)
|
||||||
{
|
{
|
||||||
byte[] scratch = new byte[data.Length * 2];
|
_scratch = new byte[data.Length * 2];
|
||||||
int bitPosition = 0;
|
_bitPosition = 0;
|
||||||
var encodedSymbol = new List<bool>();
|
|
||||||
|
|
||||||
for (int i = 0; i < data.Length; i++)
|
for (int i = 0; i < data.Length; i++)
|
||||||
{
|
{
|
||||||
encodedSymbol.Clear();
|
EncodeChar((char)data[i]);
|
||||||
encodedSymbol = Root.Traverse((char)data[i], encodedSymbol);
|
|
||||||
foreach(bool v in encodedSymbol)
|
|
||||||
{
|
|
||||||
scratch.WriteBit(bitPosition, 0, v);
|
|
||||||
bitPosition++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
encodedSymbol.Clear();
|
EncodeChar(EOD);
|
||||||
encodedSymbol = Root.Traverse(EOD, encodedSymbol);
|
|
||||||
foreach (bool v in encodedSymbol)
|
int byteLenght = (int)Math.Ceiling((double)_bitPosition / 8);
|
||||||
{
|
|
||||||
scratch.WriteBit(bitPosition, 0, v);
|
|
||||||
bitPosition++;
|
|
||||||
}
|
|
||||||
int byteLenght = (int)Math.Ceiling((double)bitPosition / 8);
|
|
||||||
byte[] compressedData = new byte[byteLenght];
|
byte[] compressedData = new byte[byteLenght];
|
||||||
Array.Copy(scratch, compressedData, byteLenght);
|
Array.Copy(_scratch, compressedData, byteLenght);
|
||||||
|
_scratch = null;
|
||||||
|
|
||||||
return compressedData;
|
return compressedData;
|
||||||
}
|
}
|
||||||
@@ -98,15 +102,15 @@ namespace VAR.UrlCompressor
|
|||||||
public byte[] Decode(byte[] data)
|
public byte[] Decode(byte[] data)
|
||||||
{
|
{
|
||||||
HuffmanTreeNode current = Root;
|
HuffmanTreeNode current = Root;
|
||||||
byte[] scratch = new byte[data.Length];
|
_scratch = new byte[data.Length];
|
||||||
int bitPosition = 0;
|
_bitPosition = 0;
|
||||||
int bytePosition = 0;
|
int bytePosition = 0;
|
||||||
|
|
||||||
int lenght = data.Length * 8;
|
int lenght = data.Length * 8;
|
||||||
while (bitPosition < lenght)
|
while (_bitPosition < lenght)
|
||||||
{
|
{
|
||||||
bool bit = data.ReadBit(bitPosition, 0);
|
bool bit = data.ReadBit(_bitPosition, 0);
|
||||||
bitPosition++;
|
_bitPosition++;
|
||||||
if (bit)
|
if (bit)
|
||||||
{
|
{
|
||||||
if (current.Right != null)
|
if (current.Right != null)
|
||||||
@@ -125,14 +129,15 @@ namespace VAR.UrlCompressor
|
|||||||
if (current.IsLeaf())
|
if (current.IsLeaf())
|
||||||
{
|
{
|
||||||
if (current.Symbol == EOD) { break; }
|
if (current.Symbol == EOD) { break; }
|
||||||
scratch = scratch.WriteByte(bytePosition, (byte)current.Symbol);
|
_scratch = _scratch.WriteByte(bytePosition, (byte)current.Symbol);
|
||||||
bytePosition++;
|
bytePosition++;
|
||||||
current = Root;
|
current = Root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] decompressedData = new byte[bytePosition];
|
byte[] decompressedData = new byte[bytePosition];
|
||||||
Array.Copy(scratch, decompressedData, bytePosition);
|
Array.Copy(_scratch, decompressedData, bytePosition);
|
||||||
|
_scratch = null;
|
||||||
|
|
||||||
return decompressedData;
|
return decompressedData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
136
VAR.UrlCompressor/Url.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace VAR.UrlCompressor
|
namespace VAR.UrlCompressor
|
||||||
@@ -6,7 +7,7 @@ namespace VAR.UrlCompressor
|
|||||||
public class UrlCompressor
|
public class UrlCompressor
|
||||||
{
|
{
|
||||||
private static HuffmanTree _huffmanTree = null;
|
private static HuffmanTree _huffmanTree = null;
|
||||||
|
|
||||||
private static void InitHuffmanTree()
|
private static void InitHuffmanTree()
|
||||||
{
|
{
|
||||||
if (_huffmanTree != null) { return; }
|
if (_huffmanTree != null) { return; }
|
||||||
@@ -81,21 +82,21 @@ namespace VAR.UrlCompressor
|
|||||||
{ '8', 1000},
|
{ '8', 1000},
|
||||||
{ '9', 1000},
|
{ '9', 1000},
|
||||||
|
|
||||||
// Common simbols
|
// Common symbols
|
||||||
{ ' ', 100},
|
{ ' ', 100},
|
||||||
{ '!', 100},
|
{ '!', 100},
|
||||||
{ '"', 100},
|
{ '"', 100},
|
||||||
{ '#', 50000}, // NOTE: Exagerate to minimize bitstream of this symbol '#'
|
{ '#', 20000}, // 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 '$'
|
||||||
{ '%', 100},
|
{ '%', 100},
|
||||||
{ '&', 100},
|
{ '&', 100},
|
||||||
{ '\'', 100},
|
{ '\'', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '/'
|
||||||
{ '(', 100},
|
{ '(', 100},
|
||||||
{ '*', 100},
|
{ '*', 100},
|
||||||
{ '+', 100},
|
{ '+', 100},
|
||||||
{ ',', 100},
|
{ ',', 100},
|
||||||
{ '-', 100},
|
{ '-', 100},
|
||||||
{ '.', 100},
|
{ '.', 20000}, // NOTE: Exagerate to minimize bitstream of this symbol '.'
|
||||||
{ '/', 100},
|
{ '/', 100},
|
||||||
{ ':', 100},
|
{ ':', 100},
|
||||||
{ ';', 100},
|
{ ';', 100},
|
||||||
@@ -118,46 +119,120 @@ namespace VAR.UrlCompressor
|
|||||||
_huffmanTree = new HuffmanTree(frequencies);
|
_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();
|
InitHuffmanTree();
|
||||||
|
|
||||||
// Replace protocol indicator
|
Url oUrl = Url.CreateFromString(url);
|
||||||
if (url.StartsWith("https://") || url.StartsWith("HTTPS://"))
|
|
||||||
|
// "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('.');
|
||||||
if (url.StartsWith("http://") || url.StartsWith("HTTP://"))
|
for (int i = 0; i < urlHostParts.Length; i++)
|
||||||
{
|
{
|
||||||
url = string.Format("#{0}", url.Substring("http://".Length));
|
foreach (KeyValuePair<string, string> hostConversion in hostConversions)
|
||||||
|
{
|
||||||
|
if (urlHostParts[i] == hostConversion.Key)
|
||||||
|
{
|
||||||
|
urlHostParts[i] = hostConversion.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oUrl.Host = string.Join(".", urlHostParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url = oUrl.ToShortString();
|
||||||
|
|
||||||
|
// Reduce entropy
|
||||||
byte[] urlBytes = Encoding.ASCII.GetBytes(url);
|
byte[] urlBytes = Encoding.ASCII.GetBytes(url);
|
||||||
|
urlBytes = _huffmanTree.Encode(urlBytes);
|
||||||
byte[] compressedUrlBytes = _huffmanTree.Encode(urlBytes);
|
urlBytes = ChecksumAdd(urlBytes);
|
||||||
|
return Base62.Encode(urlBytes);
|
||||||
return Base62.Encode(compressedUrlBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Decompress(string compressedUrl)
|
public static string Decompress(string compressedUrl, Dictionary<string, string> hostConversions = null)
|
||||||
{
|
{
|
||||||
InitHuffmanTree();
|
InitHuffmanTree();
|
||||||
|
|
||||||
byte[] urlBytes = Base62.Decode(compressedUrl);
|
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);
|
||||||
|
|
||||||
|
// "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)); }
|
||||||
|
|
||||||
string url = Encoding.ASCII.GetString(decompressedUrlBytes);
|
if (hostConversions != null)
|
||||||
|
|
||||||
// Restore protocol indicator
|
|
||||||
if (url.StartsWith("#"))
|
|
||||||
{
|
{
|
||||||
url = string.Format("http://{0}", url.Substring(1));
|
// "Decompress" hosts
|
||||||
}
|
string[] urlHostParts = oUrl.Host.Split('.');
|
||||||
if (url.StartsWith("$"))
|
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;
|
return url;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<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>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{016AE05D-12AF-40C6-8D0C-064970004F0B}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<IsPackable>true</IsPackable>
|
||||||
<RootNamespace>VAR.UrlCompressor</RootNamespace>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<AssemblyName>VAR.UrlCompressor</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug .Net 4.6.1|AnyCPU' ">
|
<PropertyGroup>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<PackageId>VAR.UrlCompressor</PackageId>
|
||||||
<DebugType>full</DebugType>
|
<Title>VAR.UrlCompressor</Title>
|
||||||
<Optimize>false</Optimize>
|
<Version>1.2.0</Version>
|
||||||
<OutputPath>bin\Debug\net461\</OutputPath>
|
<Description>.Net library for compressing URLs</Description>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<Authors>VAR</Authors>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<Company>VAR</Company>
|
||||||
<WarningLevel>4</WarningLevel>
|
<Copyright>Copyright © VAR 2016-2017</Copyright>
|
||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
|
||||||
</PropertyGroup>
|
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 4.6.1|AnyCPU' ">
|
<PackageProjectUrl>https://github.com/Kableado/VAR.UrlCompressor</PackageProjectUrl>
|
||||||
<DebugType>pdbonly</DebugType>
|
<PackageTags>URL Compression Library</PackageTags>
|
||||||
<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>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Content Include="..\LICENSE.txt" Link="LICENSE.txt" Pack="true" PackagePath=""/>
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<Target Name="CopyPackage" AfterTargets="Pack">
|
||||||
<Compile Include="Base62.cs" />
|
<Copy
|
||||||
<Compile Include="ByteExtensions.cs" />
|
SourceFiles="$(OutputPath)..\$(PackageId).$(PackageVersion).nupkg"
|
||||||
<Compile Include="Huffman.cs" />
|
DestinationFolder="Nuget\"
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
/>
|
||||||
<Compile Include="UrlCompressor.cs" />
|
</Target>
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Build.NuGet.cmd" />
|
|
||||||
<None Include="packages.config" />
|
|
||||||
<None Include="VAR.UrlCompressor.nuspec" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user