Files
stream-cinema/CinemaLib/Webshare/BaseEncoding.cs
Roman Vaníček 0bcb6d571f
All checks were successful
continuous-integration/drone/push Build is passing
Rename to Cinema
2024-11-30 00:50:10 +01:00

245 lines
7.2 KiB
C#

using System;
using System.Text;
namespace Cinema.Webshare;
/// <summary>
/// Performs generic binary-to-text encoding.
/// </summary>
class BaseEncoding : Encoding
{
int _bitCount;
int _bitMask;
string _characters;
bool _msbComesFirst;
Dictionary<char, int> _values;
/// <summary>
/// Defines a binary-to-text encoding.
/// </summary>
/// <param name="characterSet">The characters of the encoding.</param>
/// <param name="msbComesFirst">
/// <c>true</c> to begin with the most-significant bit of each byte.
/// Otherwise, the encoding begins with the least-significant bit.
/// </param>
public BaseEncoding(string characterSet, bool msbComesFirst)
: this(characterSet, msbComesFirst, null)
{
}
/// <summary>
/// Defines a binary-to-text encoding.
/// Additional decode characters let you add aliases, and a filter callback can be used
/// to make decoding case-insensitive among other things.
/// </summary>
/// <param name="characterSet">The characters of the encoding.</param>
/// <param name="msbComesFirst">
/// <c>true</c> to begin with the most-significant bit of each byte.
/// Otherwise, the encoding begins with the least-significant bit.
/// </param>
/// <param name="additionalDecodeCharacters">
/// A dictionary of alias characters, or <c>null</c> if no aliases are desired.
/// </param>
/// <param name="decodeFilterCallback">
/// A callback to map arbitrary characters onto the characters that can be decoded.
/// </param>
public BaseEncoding(string characterSet, bool msbComesFirst,
IDictionary<char, int> additionalDecodeCharacters)
{
if (characterSet == null)
throw new ArgumentNullException();
if (!IsPositivePowerOf2(characterSet.Length))
{
throw new ArgumentException("Length must be a power of 2.", "characterSet");
}
if (characterSet.Length > 256)
{
throw new ArgumentException("Character sets with over 256 characters are not supported.", "characterSet");
}
_bitCount = 31 - CountLeadingZeros(characterSet.Length);
_bitMask = (1 << _bitCount) - 1;
_characters = characterSet;
_msbComesFirst = msbComesFirst;
_values = additionalDecodeCharacters != null
? new Dictionary<char, int>(additionalDecodeCharacters)
: new Dictionary<char, int>();
for (int i = 0; i < characterSet.Length; i++)
{
char ch = characterSet[i];
if (_values.ContainsKey(ch))
{
throw new ArgumentException("Duplicate characters are not supported.", "characterSet");
}
_values.Add(ch, (byte)i);
}
}
/// <summary>
/// Gets the value corresponding to the specified character.
/// </summary>
/// <param name="character">A character.</param>
/// <returns>A value, or <c>-1</c> if the character is not part of the encoding.</returns>
public virtual int GetValue(char character)
{
int value;
return _values.TryGetValue(character, out value) ? value : -1;
}
/// <summary>
/// Gets the character corresponding to the specified value.
/// </summary>
/// <param name="value">A value.</param>
/// <returns>A character.</returns>
public virtual char GetChar(int value)
{
return _characters[value & BitMask];
}
/// <summary>
/// The bit mask for a single character in the current encoding.
/// </summary>
public int BitMask
{
get { return _bitMask; }
}
/// <summary>
/// The number of bits per character in the current encoding.
/// </summary>
public int BitsPerCharacter
{
get { return _bitCount; }
}
/// <summary>
/// <c>true</c> if the encoding begins with the most-significant bit of each byte.
/// Otherwise, the encoding begins with the least-significant bit.
/// </summary>
public bool MsbComesFirst
{
get { return _msbComesFirst; }
}
public override int GetMaxCharCount(int byteCount)
{
if (byteCount < 0)
throw new ArgumentOutOfRangeException();
return checked((byteCount * 8 + BitsPerCharacter - 1) / BitsPerCharacter);
}
/// <inheritdoc />
public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
{
int charCount = GetCharCount(bytes, byteIndex, byteCount);
return GetChars(bytes, byteIndex, byteCount, chars, charIndex, charCount);
}
/// <summary>
/// Converts bytes from their binary representation to a text representation.
/// </summary>
/// <param name="bytes">An input array of bytes.</param>
/// <param name="byteIndex">The index of the first byte.</param>
/// <param name="byteCount">The number of bytes to read.</param>
/// <param name="chars">An output array of characters.</param>
/// <param name="charIndex">The index of the first character.</param>
/// <param name="charCount">The number of characters to write.</param>
/// <returns>The number of characters written.</returns>
public int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, int charCount)
{
if (charIndex < 0 || charCount < 0 || chars.Length - charIndex < charCount
|| byteIndex < 0 || byteCount < 0 || bytes.Length - byteIndex < byteCount)
throw new ArgumentOutOfRangeException();
int byteEnd = checked(byteIndex + byteCount);
int bitStartOffset = 0;
for (int i = 0; i < charCount; i++)
{
byte value;
byte thisByte = byteIndex + 0 < byteEnd ? bytes[byteIndex + 0] : (byte)0;
byte nextByte = byteIndex + 1 < byteEnd ? bytes[byteIndex + 1] : (byte)0;
int bitEndOffset = bitStartOffset + BitsPerCharacter;
if (MsbComesFirst)
{
value = ShiftRight(thisByte, 8 - bitStartOffset - BitsPerCharacter);
if (bitEndOffset > 8)
{
value |= ShiftRight(nextByte, 16 - bitStartOffset - BitsPerCharacter);
}
}
else
{
value = ShiftRight(thisByte, bitStartOffset);
if (bitEndOffset > 8)
{
value |= ShiftRight(nextByte, bitStartOffset - 8);
}
}
bitStartOffset = bitEndOffset;
if (bitStartOffset >= 8)
{
bitStartOffset -= 8; byteIndex++;
}
chars[i] = GetChar(value);
}
return charCount;
}
/// <inheritdoc />
public override int GetCharCount(byte[] bytes, int index, int count)
{
if (index < 0 || count < 0 || bytes.Length - index < count)
throw new ArgumentOutOfRangeException();
return GetMaxCharCount(count);
}
public override int GetByteCount(char[] chars, int index, int count)
{
throw new NotSupportedException();
}
public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
{
throw new NotSupportedException();
}
public override int GetMaxByteCount(int charCount)
{
throw new NotSupportedException();
}
private static bool IsPositivePowerOf2(int value)
{
return 0 < value && 0 == (value & (value - 1));
}
private static int CountLeadingZeros(int value)
{
int count;
for (count = 0; count < 32 && 0 == (value & (0x80000000 >> count)); count++) ;
return count;
}
private static byte ShiftLeft(byte value, int bits)
{
return (byte)(bits > 0 ? value << bits : value >> (-bits));
}
private static byte ShiftRight(byte value, int bits)
{
return ShiftLeft(value, -bits);
}
}