Example #1
0
        public static void FormatHex(byte[] hex, StringBuilder output, HexCasing casing)
        {
            if (hex == null)
            {
                throw new ArgumentNullException("hex");
            }

            char[] digits;
            if (casing == HexCasing.Upper)
            {
                digits = hexDigitsU;
            }
            else if (casing == HexCasing.Lower)
            {
                digits = hexDigitsL;
            }
            else
            {
                throw new ArgumentException("Invalid value for casing.", "casing");
            }


            for (int i = 0; i < hex.Length; i++)
            {
                int hexVal = hex[i];

                int highNib = hexVal >> 4;
                int lowNib  = hexVal & 0xF;

                output.Append(digits[highNib]);
                output.Append(digits[lowNib]);
            }
        }
Example #2
0
        /// <summary>
        /// Returns a string representation of the <see cref="Sha1"/> or <see cref="Sha256"/> instance using the 'n' or 'N' format.
        /// </summary>
        /// <param name="sha"></param>
        /// <param name="casing">The ASCII casing to use.</param>
        /// <returns></returns>
        internal static string ToString(ReadOnlySpan <byte> sha, HexCasing casing)
        {
            int byteLength = sha.Length; // 20|32

            Debug.Assert(byteLength == Sha1.ByteLength || byteLength == Sha256.ByteLength);

            // Text is treated as 5|8 groups of 8 chars (5|8 x 4 bytes)
            int hexLength = byteLength * 2; // 40|64

#if !NETSTANDARD2_0
            Span <char> span = stackalloc char[hexLength];
#else
            var span = new char[hexLength];
#endif
            int pos = 0;
            for (int i = 0; i < byteLength; i++) // 20|32
            {
                // Each byte is two hexits
                // Output *highest* index first so codegen can elide all but first bounds check
                byte byt = sha[i];
                span[pos + 1] = (char)(HexChars[byt & 15] | (uint)casing); // == b % 16
                span[pos]     = (char)(HexChars[byt >> 4] | (uint)casing); // == b / 16

                pos += 2;
            }

            string str = new string(span);
            return(str);
        }
Example #3
0
        public static string FormatHex(byte[] hex, HexCasing casing)
        {
            if (hex == null)
            {
                throw new ArgumentNullException("hex");
            }

            StringBuilder result = new StringBuilder(hex.Length * 2);

            FormatHex(hex, result, casing);
            return(result.ToString());
        }
Example #4
0
        /// <summary>
        /// Returns a string representation of the <see cref="Sha1"/> or <see cref="Sha256"/> instance.
        /// n: a9993e364706816aba3e25717850c26c9cd0d89d, cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0
        /// d: a9993e36-4706816a-ba3e2571-7850c26c-9cd0d89d, cdc76e5c-9914fb92-81a1c7e2-84d73e67-f1809a48-a497200e-046d39cc-c7112cd0
        /// s: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d, cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0
        /// </summary>
        /// <param name="sha"></param>
        /// <param name="separator"></param>
        /// <param name="casing">The ASCII casing to use.</param>
        /// <returns></returns>
        internal static string ToString(ReadOnlySpan <byte> sha, char separator, HexCasing casing)
        {
            Debug.Assert(separator == '-' || separator == ' ');

            int byteLength = sha.Length; // 20|32

            Debug.Assert(byteLength == Sha1.ByteLength || byteLength == Sha256.ByteLength);

            // Text is treated as 5|8 groups of 8 chars (5|8 x 4 bytes) with 4|7 separators
            int hexLength = byteLength * 2; // 40|64
            int n         = hexLength / 8;  // 5|8

#if !NETSTANDARD2_0
            Span <char> span = stackalloc char[hexLength + n - 1]; // + 4|7
#else
            var span = new char[hexLength + n - 1];                // + 4|7
#endif
            int pos = 0;
            int sep = 8;
            for (int i = 0; i < byteLength; i++) // 20|32
            {
                // Each byte is two hexits (convention is lowercase)
                // Output *highest* index first so codegen can elide all but first bounds check
                byte byt = sha[i];
                span[pos + 1] = (char)(HexChars[byt & 15] | (uint)casing); // == b % 16
                span[pos]     = (char)(HexChars[byt >> 4] | (uint)casing); // == b / 16

                pos += 2;

                // Append a separator if required
                if (pos == sep) // pos >= 2, sep = 0|N
                {
                    span[pos++] = separator;

                    sep = pos + 8;
                    if (sep >= span.Length)
                    {
                        sep = 0; // Prevent IndexOutOfRangeException
                    }
                }
            }

            string str = new string(span);
            return(str);
        }
Example #5
0
        /// <summary>
        /// Converts the <see cref="Sha1"/> or <see cref="Sha256"/> instance to a string using the 'n' or 'N' format,
        /// and returns the value split into two tokens.
        /// </summary>
        /// <param name="sha">The sha value.</param>
        /// <param name="prefixLength">The length of the first token.</param>
        /// <param name="casing">The ASCII casing to use.</param>
        /// <returns></returns>
        internal static KeyValuePair <string, string> Split(ReadOnlySpan <byte> sha, int prefixLength, HexCasing casing)
        {
            int byteLength = sha.Length; // 20|32

            Debug.Assert(byteLength == Sha1.ByteLength || byteLength == Sha256.ByteLength);

            // Text is treated as 5|8 groups of 8 chars (5|8 x 4 bytes)
            int hexLength = byteLength * 2;

#if !NETSTANDARD2_0
            Span <char> span = stackalloc char[hexLength];
#else
            var span = new char[hexLength];
#endif
            int pos = 0;
            for (int i = 0; i < byteLength; i++) // 20|32
            {
                // Each byte is two hexits
                // Output *highest* index first so codegen can elide all but first bounds check
                byte byt = sha[i];
                span[pos + 1] = (char)(HexChars[byt & 15] | (uint)casing); // == b % 16
                span[pos]     = (char)(HexChars[byt >> 4] | (uint)casing); // == b / 16

                pos += 2;
            }

            if (prefixLength >= hexLength)
            {
                string pfx = new string(span);
                return(new KeyValuePair <string, string>(pfx, string.Empty));
            }

            if (prefixLength <= 0)
            {
                string ext = new string(span);
                return(new KeyValuePair <string, string>(string.Empty, ext));
            }

#if !NETSTANDARD2_0
            Span <char> p = span.Slice(0, prefixLength);
            Span <char> e = span.Slice(prefixLength, hexLength - prefixLength);

            string prefix = new string(p);
            string extra  = new string(e);
#else
            string prefix = new string(span, 0, prefixLength);
            string extra  = new string(span, prefixLength, hexLength - prefixLength);
#endif
            var kvp = new KeyValuePair <string, string>(prefix, extra);
            return(kvp);
        }
Example #6
0
        public static void WriteHexByte(byte value, Span <byte> buffer, int startingIndex = 0, HexCasing casing = HexCasing.Uppercase)
        {
            // We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ],
            // where HHHH and LLLL are the high and low nibbles of the incoming byte. Then
            // subtract this integer from a constant minuend as shown below.
            //
            //   [ 1000 1001 1000 1001 ]
            // - [ 0000 HHHH 0000 LLLL ]
            // =========================
            //   [ *YYY **** *ZZZ **** ]
            //
            // The end result of this is that YYY is 0b000 if HHHH <= 9, and YYY is 0b111 if HHHH >= 10.
            // Similarly, ZZZ is 0b000 if LLLL <= 9, and ZZZ is 0b111 if LLLL >= 10.
            // (We don't care about the value of asterisked bits.)
            //
            // To turn a nibble in the range [ 0 .. 9 ] into hex, we calculate hex := nibble + 48 (ascii '0').
            // To turn a nibble in the range [ 10 .. 15 ] into hex, we calculate hex := nibble - 10 + 65 (ascii 'A').
            //                                                                => hex := nibble + 55.
            // The difference in the starting ASCII offset is (55 - 48) = 7, depending on whether the nibble is <= 9 or >= 10.
            // Since 7 is 0b111, this conveniently matches the YYY or ZZZ value computed during the earlier subtraction.

            // The commented out code below is code that directly implements the logic described above.

            //uint packedOriginalValues = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU);
            //uint difference = 0x8989U - packedOriginalValues;
            //uint add7Mask = (difference & 0x7070U) >> 4; // line YYY and ZZZ back up with the packed values
            //uint packedResult = packedOriginalValues + add7Mask + 0x3030U /* ascii '0' */;

            // The code below is equivalent to the commented out code above but has been tweaked
            // to allow codegen to make some extra optimizations.

            uint difference   = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU) - 0x8989U;
            uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing;

            // The low byte of the packed result contains the hex representation of the incoming byte's low nibble.
            // The adjacent byte of the packed result contains the hex representation of the incoming byte's high nibble.

            // Finally, write to the output buffer starting with the *highest* index so that codegen can
            // elide all but the first bounds check. (This only works if 'startingIndex' is a compile-time constant.)

            buffer[startingIndex + 1] = (byte)(packedResult);
            buffer[startingIndex]     = (byte)(packedResult >> 8);
        }