Esempio n. 1
0
        /// <summary>
        /// Constructs a byte array from a string of hexadecimal digits.
        /// </summary>
        /// <param name="hex">The string to translate to bytes.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="hex" /> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="hex"/> is not of even length,
        /// OR, if <paramref name="hex"/> contains characters that don't correspond to hexadecimal digits.
        /// </exception>
        /// <returns>the resulting byte array.</returns>
        public static byte[] ToByte(this string hex)
        {
            Guard.NotNull(() => hex, hex);

            if ((hex.Length & 1) != 0)
            {
                throw new ArgumentException(CommonStrings.StringLengthMustBeEven, nameof(hex));
            }

            const string HexDigits = @"0123456789ABCDEF";

            var    bytes       = new byte[hex.Length >> 1];
            int    arrayLength = bytes.Length;
            string uppercase   = hex.ToUpperInvariant();

            for (int i = 0; i < arrayLength; i++)
            {
                int index = i << 1;
                int digit = HexDigits.IndexOf(uppercase[index]);
                if (digit == -1)
                {
                    throw new ArgumentException(CommonStrings.StringMustContainOnlyHexDigits, nameof(hex));
                }

                var b = (byte)(digit << 4);

                digit = HexDigits.IndexOf(uppercase[index | 1]);
                if (digit == -1)
                {
                    throw new ArgumentException(CommonStrings.StringMustContainOnlyHexDigits, nameof(hex));
                }

                b       |= (byte)digit;
                bytes[i] = b;
            }

            return(bytes);
        }
Esempio n. 2
0
        /// <summary>
        /// Given a raw array of bytes intended to be converted to a UTF-8 string, and whose origins are a string,
        /// analyze the contents and un-escape the contents to prepare for final conversion from bytes to string.
        /// </summary>
        /// <param name="rawCharacterPayload">The raw bytes that are to be un-escaped as necessary.</param>
        /// <param name="enclosingQuoteIndexes">A <see cref="Range{int}"/> that describes the index of the first and
        /// last quotation mark characters in <paramref name="rawCharacterPayload"/>.</param>
        /// <returns>The un-escaped string.</returns>
        /// <remarks>The <paramref name="enclosingQuoteIndexes"/> argument may be <c>null</c>. When <c>null</c>, the entire
        /// <paramref name="rawCharacterPayload"/> is analyzed. When <paramref name="enclosingQuoteIndexes"/> is not <c>null</c>,
        /// it must specify a valid, non-empty range.</remarks>
        /// <exception cref="System.NullReferenceException">Thrown if <paramref name="rawCharacterPayload"/> is null and
        /// <paramref name="enclosingQuoteIndexes"/> is <c>null</c> or specifies a range of more than one.</exception>
        /// <exception cref="System.IndexOutOfRangeException">Thrown if <paramref name="enclosingQuoteIndexes"/> specifies
        /// an index that is before the beginning of the array.</exception>
        public static string UnescapeFromBytePayload(this byte[] rawCharacterPayload, Range <int> enclosingQuoteIndexes)
        {
            var unescapedPayload = rawCharacterPayload;
            var needsTrim        = true;

            if (enclosingQuoteIndexes == null)
            {
                enclosingQuoteIndexes = new Range <int>(-1, rawCharacterPayload.Length);
            }
            if (enclosingQuoteIndexes.IsValid && (enclosingQuoteIndexes.Maximum > enclosingQuoteIndexes.Minimum))
            {
                /* ------------------------------------------------------------------------ */
                /*  String unquoting/unescaping rules:                                      */
                /*                                                                          */
                /*  1.  If the string does not begin *and* end with double-quotes, it is    */
                /*      returned as-is.                                                     */
                /*                                                                          */
                /*  2.  A backslash followed by 'x' and two hexadecimal digits gets         */
                /*      replaced by a character whose value is represented by the digits.   */
                /*                                                                          */
                /*  3.  A backslash followed by three octal digits gets replaced by a       */
                /*      character whose value is represented by the digits, masked to 8     */
                /*      bits. (e.g. \777 and \377 both map to 0xFF.)                        */
                /*                                                                          */
                /*  4.  A backslash followed by 't', 'n', or 'r' gets replaced by 0x09,     */
                /*      0x0A, or 0x0D, respectively.                                        */
                /*                                                                          */
                /*  5.  A backslash followed by any other character gets replaced with      */
                /*      the character that follows the backslash.                           */
                /* ------------------------------------------------------------------------ */
                unescapedPayload = new byte[enclosingQuoteIndexes.Maximum - enclosingQuoteIndexes.Minimum];
                var unescapedPayloadIndex = -1;

                var firstQuoteIndex = enclosingQuoteIndexes.Minimum;
                var lastQuoteIndex  = enclosingQuoteIndexes.Maximum;
                for (var i = firstQuoteIndex + 1; i < lastQuoteIndex; ++i)
                {
                    var byteCharacter      = rawCharacterPayload[i];
                    var unescapedCharacter = byteCharacter;
                    if (byteCharacter == '\\')
                    {
                        // an escaped character!
                        if (i < lastQuoteIndex)
                        {
                            byteCharacter      = (i + 1) < lastQuoteIndex ? rawCharacterPayload[++i] : (byte)0;
                            unescapedCharacter = byteCharacter;
                            switch ((char)byteCharacter)
                            {
                            case 't':     // escaped tab
                                unescapedCharacter = (byte)'\t';
                                break;

                            case 'r':     // escaped carriage return
                                unescapedCharacter = (byte)'\r';
                                break;

                            case 'n':     // escaped line feed
                                unescapedCharacter = (byte)'\n';
                                break;

                            case 'x':     // escaped hex
                                if ((i + 1) < lastQuoteIndex)
                                {
                                    const string HexDigits  = "0123456789ABCDEF";
                                    var          highNybble = HexDigits.IndexOf(char.ToUpperInvariant((char)rawCharacterPayload[++i]));
                                    var          lowNybble  = (i + 1) < lastQuoteIndex?HexDigits.IndexOf(char.ToUpperInvariant((char)rawCharacterPayload[++i])) : -1;

                                    if ((highNybble >= 0) && (lowNybble >= 0))
                                    {
                                        unescapedCharacter = (byte)(((highNybble & 0x0F) << 4) | (lowNybble & 0x0F));
                                    }
                                }
                                break;

                            case '0':     // escaped octal
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                                if (i < lastQuoteIndex)
                                {
                                    const string OctalDigits = "01234567";
                                    var          highTrybble = OctalDigits.IndexOf(char.ToUpperInvariant((char)byteCharacter));
                                    var          midTrybble  = (i + 1) < lastQuoteIndex?OctalDigits.IndexOf(char.ToUpperInvariant((char)rawCharacterPayload[++i])) : -1;

                                    var lowTrybble = (i + 1) < lastQuoteIndex?OctalDigits.IndexOf(char.ToUpperInvariant((char)rawCharacterPayload[++i])) : -1;

                                    if ((midTrybble >= 0) && (lowTrybble >= 0))
                                    {
                                        unescapedCharacter = (byte)(((highTrybble & 0x03) << 6) | ((midTrybble & 0x07) << 3) | (lowTrybble & 0x07));
                                    }
                                }
                                break;

                            default:
                                break;
                            }
                        }
                    }
                    if (unescapedCharacter > 0)
                    {
                        unescapedPayload[++unescapedPayloadIndex] = unescapedCharacter;
                    }
                }
                needsTrim = unescapedPayloadIndex < 0;
            }

            var unescapedPayloadString = System.Text.Encoding.UTF8.GetString(unescapedPayload, 0, unescapedPayload.Length).Trim('\0');

            if (needsTrim)
            {
                unescapedPayloadString = unescapedPayloadString.Trim();
            }
            return(unescapedPayloadString);
        }