#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Text;
namespace CSharpTest.Net.Formatting
{
///
/// This encoding produces a 'url' safe string from bytes, similar to base64 encoding yet
/// it replaces '+' with '-', '/' with '_' and omits padding.
///
public static class Safe64Encoding
{
internal static readonly byte[] chTable64;
internal static readonly byte[] chValue64;
const int MIN = '-';
const int MAX = 'z' + 1;
static Safe64Encoding()
{
chTable64 = Encoding.ASCII.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
chValue64 = new byte[MAX - MIN];
for (byte i = 0; i < 64; i++)
chValue64[chTable64[i] - MIN] = i;
}
/// Returns a encoded string of ascii characters that are URI safe
public static string EncodeBytes(byte[] input)
{ return EncodeBytes(input, 0, Check.NotNull(input).Length); }
/// Returns a encoded string of ascii characters that are URI safe
public static string EncodeBytes(byte[] input, int start, int length)
{
byte[] output = new byte[(int)Math.Ceiling((length << 3) / 6d)];
int len = EncodeBytes(input, start, length, output, 0);
return Encoding.ASCII.GetString(output, 0, len);
}
/// Returns a encoded string of ascii characters that are URI safe
public static int EncodeBytes(byte[] input, int start, int length, byte[] output, int offset)
{
if (output.Length < (offset + ((int)Math.Ceiling((length << 3) / 6d))))
throw new ArgumentOutOfRangeException();
int leftover = length % 3;
int stop = start + (length - leftover);
int index = offset;
int pos;
for (pos = start; pos < stop; pos += 3)
{
output[index] = chTable64[(input[pos] & 0xfc) >> 2];
output[index + 1] = chTable64[((input[pos] & 3) << 4) | ((input[pos + 1] & 240) >> 4)];
output[index + 2] = chTable64[((input[pos + 1] & 15) << 2) | ((input[pos + 2] & 0xc0) >> 6)];
output[index + 3] = chTable64[input[pos + 2] & 0x3f];
index += 4;
}
switch (leftover)
{
case 1:
output[index] = chTable64[(input[pos] & 0xfc) >> 2];
output[index + 1] = chTable64[(input[pos] & 3) << 4];
index += 2;
break;
case 2:
output[index] = chTable64[(input[pos] & 0xfc) >> 2];
output[index + 1] = chTable64[((input[pos] & 3) << 4) | ((input[pos + 1] & 240) >> 4)];
output[index + 2] = chTable64[(input[pos + 1] & 15) << 2];
index += 3;
break;
}
return index - offset;
}
/// Decodes the ascii text from the bytes provided into the original byte array
public static byte[] DecodeBytes(string input)
{ return DecodeBytes(input, 0, Check.NotNull(input).Length); }
/// Decodes the ascii text from the bytes provided into the original byte array
public static byte[] DecodeBytes(string input, int start, int length)
{ return DecodeBytes(System.Text.Encoding.ASCII.GetBytes(Check.NotNull(input)), start, length); }
/// Decodes the ascii text from the bytes provided into the original byte array
public static byte[] DecodeBytes(byte[] input)
{ return DecodeBytes(input, 0, Check.NotNull(input).Length); }
/// Decodes the ascii text from the bytes provided into the original byte array
public static byte[] DecodeBytes(byte[] input, int start, int length)
{
byte[] results = new byte[(length * 6) >> 3];
int used = DecodeBytes(input, start, length, results, 0);
if (used != results.Length)
Array.Resize(ref results, used);
return results;
}
/// Decodes the ascii text from the bytes provided into the original byte array
public static int DecodeBytes(byte[] input, int start, int length, byte[] output, int offset)
{
if (output.Length < (offset + ((length * 6) >> 3)))
throw new ArgumentOutOfRangeException();
int leftover = length % 4;
int stop = start + (length - leftover);
int index = offset;
int pos;
for (pos = start; pos < stop; pos += 4)
{
output[index] = (byte)((chValue64[input[pos] - MIN] << 2) | (chValue64[input[pos + 1] - MIN] >> 4));
output[index + 1] = (byte)(((chValue64[input[pos + 1] - MIN]) << 4) | (chValue64[input[pos + 2] - MIN] >> 2));
output[index + 2] = (byte)(((chValue64[input[pos + 2] - MIN]) << 6) | (chValue64[input[pos + 3] - MIN]));
index += 3;
}
if (leftover == 2)
{
output[index] = (byte)((chValue64[input[pos] - MIN] << 2) | (chValue64[input[pos + 1] - MIN] >> 4));
index += 1;
}
else if (leftover == 3)
{
output[index] = (byte)((chValue64[input[pos] - MIN] << 2) | (chValue64[input[pos + 1] - MIN] >> 4));
output[index + 1] = (byte)(((chValue64[input[pos + 1] - MIN]) << 4) | (chValue64[input[pos + 2] - MIN] >> 2));
index += 2;
}
return index - offset;
}
}
}