You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
8.8 KiB

using Apewer;
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Internals
{
internal class UrlEncoding
{
public static string Encode(string plain)
{
return TextUtility.FromBytes(Encode(TextUtility.Bytes(plain)));
}
public static string Decode(string escaped)
{
return Decode(escaped, Encoding.UTF8);
}
private UrlEncoding() { }
#region encode static
/// <summary>对 URL 编码。</summary>
static byte[] Encode(byte[] bytes)
{
if (bytes == null) return null;
if (bytes.Length < 1) return BytesUtility.Empty;
var offset = 0;
var count = bytes.Length;
int num = 0;
int num2 = 0;
for (int i = 0; i < count; i++)
{
char c = (char)bytes[offset + i];
if (c == ' ')
{
num++;
}
else if (!IsUrlSafeChar(c))
{
num2++;
}
}
if (num == 0 && num2 == 0)
{
if (offset == 0 && bytes.Length == count)
{
return bytes;
}
byte[] array = new byte[count];
Buffer.BlockCopy(bytes, offset, array, 0, count);
return array;
}
byte[] array2 = new byte[count + num2 * 2];
int num3 = 0;
for (int j = 0; j < count; j++)
{
byte b = bytes[offset + j];
char c2 = (char)b;
if (IsUrlSafeChar(c2))
{
array2[num3++] = b;
}
else if (c2 == ' ')
{
array2[num3++] = 43;
}
else
{
array2[num3++] = 37;
array2[num3++] = (byte)IntToHex(b >> 4 & 0xF);
array2[num3++] = (byte)IntToHex(b & 0xF);
}
}
return array2;
}
static bool IsUrlSafeChar(char ch)
{
//const string lower = "abcdefghijklmnopqrstuvwxyz";
//const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//const string numbers = "0123456789";
//const string symbols = "!()*-._";
if (ch >= 'a' && ch <= 'z') return true;
if (ch >= 'A' && ch <= 'Z') return true;
if (ch >= '0' && ch <= '9') return true;
switch (ch)
{
case '!':
case '(':
case ')':
case '*':
case '-':
case '.':
case '_':
return true;
default:
return false;
}
}
static char IntToHex(int n)
{
if (n <= 9)
{
return (char)(n + 48);
}
return (char)(n - 10 + 97);
}
#endregion
#region decode instance
private int _bufferSize;
private int _numChars;
private char[] _charBuffer;
private int _numBytes;
private byte[] _byteBuffer;
private Encoding _encoding;
private void FlushBytes()
{
if (_numBytes > 0)
{
_numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
_numBytes = 0;
}
}
internal UrlEncoding(int bufferSize, Encoding encoding)
{
_bufferSize = bufferSize;
_encoding = encoding;
_charBuffer = new char[bufferSize];
}
internal void AddChar(char ch)
{
if (_numBytes > 0)
{
FlushBytes();
}
_charBuffer[_numChars++] = ch;
}
internal void AddByte(byte b)
{
if (_byteBuffer == null)
{
_byteBuffer = new byte[_bufferSize];
}
_byteBuffer[_numBytes++] = b;
}
internal string GetString()
{
if (_numBytes > 0)
{
FlushBytes();
}
if (_numChars > 0)
{
return new string(_charBuffer, 0, _numChars);
}
return string.Empty;
}
#endregion
#region decode static
static string Decode(string value, Encoding encoding)
{
if (value == null)
{
return null;
}
int length = value.Length;
var urlDecoder = new UrlEncoding(length, encoding);
for (int i = 0; i < length; i++)
{
char c = value[i];
switch (c)
{
case '+':
c = ' ';
goto default;
case '%':
if (i < length - 2)
{
if (value[i + 1] == 'u' && i < length - 5)
{
int num = HexToInt(value[i + 2]);
int num2 = HexToInt(value[i + 3]);
int num3 = HexToInt(value[i + 4]);
int num4 = HexToInt(value[i + 5]);
if (num >= 0 && num2 >= 0 && num3 >= 0 && num4 >= 0)
{
c = (char)(num << 12 | num2 << 8 | num3 << 4 | num4);
i += 5;
urlDecoder.AddChar(c);
break;
}
}
else
{
int num5 = HexToInt(value[i + 1]);
int num6 = HexToInt(value[i + 2]);
if (num5 >= 0 && num6 >= 0)
{
byte b = (byte)(num5 << 4 | num6);
i += 2;
urlDecoder.AddByte(b);
break;
}
}
}
goto default;
default:
if ((c & 0xFF80) == 0)
{
urlDecoder.AddByte((byte)c);
}
else
{
urlDecoder.AddChar(c);
}
break;
}
}
return ValidateString(urlDecoder.GetString());
}
static int HexToInt(char h)
{
if (h >= '0' && h <= '9')
{
return h - 48;
}
if (h >= 'a' && h <= 'f')
{
return h - 97 + 10;
}
if (h >= 'A' && h <= 'F')
{
return h - 65 + 10;
}
return -1;
}
static string ValidateString(string input)
{
var skipUtf16Validation = true;
if (!skipUtf16Validation && !string.IsNullOrEmpty(input))
{
int num = -1;
int num2 = 0;
while (num2 < input.Length)
{
if (!char.IsSurrogate(input[num2]))
{
num2++;
continue;
}
num = num2;
break;
}
if (num < 0)
{
return input;
}
char[] array = input.ToCharArray();
for (int i = num; i < array.Length; i++)
{
char c = array[i];
if (char.IsLowSurrogate(c))
{
array[i] = '�';
}
else if (char.IsHighSurrogate(c))
{
if (i + 1 < array.Length && char.IsLowSurrogate(array[i + 1]))
{
i++;
}
else
{
array[i] = '�';
}
}
}
return new string(array);
}
return input;
}
#endregion
}
}