using System; using System.Text; namespace JellyfinCinemaStream.Webshare; /// /// Performs generic binary-to-text encoding. /// class BaseEncoding : Encoding { int _bitCount; int _bitMask; string _characters; bool _msbComesFirst; Dictionary _values; /// /// Defines a binary-to-text encoding. /// /// The characters of the encoding. /// /// true to begin with the most-significant bit of each byte. /// Otherwise, the encoding begins with the least-significant bit. /// public BaseEncoding(string characterSet, bool msbComesFirst) : this(characterSet, msbComesFirst, null) { } /// /// 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. /// /// The characters of the encoding. /// /// true to begin with the most-significant bit of each byte. /// Otherwise, the encoding begins with the least-significant bit. /// /// /// A dictionary of alias characters, or null if no aliases are desired. /// /// /// A callback to map arbitrary characters onto the characters that can be decoded. /// public BaseEncoding(string characterSet, bool msbComesFirst, IDictionary 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(additionalDecodeCharacters) : new Dictionary(); 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); } } /// /// Gets the value corresponding to the specified character. /// /// A character. /// A value, or -1 if the character is not part of the encoding. public virtual int GetValue(char character) { int value; return _values.TryGetValue(character, out value) ? value : -1; } /// /// Gets the character corresponding to the specified value. /// /// A value. /// A character. public virtual char GetChar(int value) { return _characters[value & BitMask]; } /// /// The bit mask for a single character in the current encoding. /// public int BitMask { get { return _bitMask; } } /// /// The number of bits per character in the current encoding. /// public int BitsPerCharacter { get { return _bitCount; } } /// /// true if the encoding begins with the most-significant bit of each byte. /// Otherwise, the encoding begins with the least-significant bit. /// 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); } /// 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); } /// /// Converts bytes from their binary representation to a text representation. /// /// An input array of bytes. /// The index of the first byte. /// The number of bytes to read. /// An output array of characters. /// The index of the first character. /// The number of characters to write. /// The number of characters written. 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; } /// 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); } }