/// <summary> /// Converts a span of 8-bit unsigned integers to its equivalent string representation that is encoded with hex characters. /// A parameter specifies whether to insert line breaks in the return value and whether to insert upper- or lowercase hex characters. /// </summary> /// <param name="bytes">A span of 8-bit unsigned integers.</param> /// <param name="options"><see cref="HexFormattingOptions.Lowercase"/> to produce lowercase output. <see cref="HexFormattingOptions.InsertLineBreaks"/> to insert a line break every 72 characters. <see cref="HexFormattingOptions.None"/> to do neither.</param> /// <returns>The string representation in hex of the elements in <paramref name="bytes"/>.</returns> /// <exception cref="ArgumentException"><paramref name="options"/> is not a valid <see cref="HexFormattingOptions"/> value.</exception> public static string ToHexString(ReadOnlySpan <byte> bytes, HexFormattingOptions options = default) { if (options < HexFormattingOptions.None || options > (HexFormattingOptions.Lowercase | HexFormattingOptions.InsertLineBreaks)) { throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); } if (bytes.Length == 0) { return(string.Empty); } var insertLineBreaks = (options & HexFormattingOptions.InsertLineBreaks) != 0; var outlen = ToHex_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); unsafe { fixed(byte *bytesPtr = &MemoryMarshal.GetReference(bytes)) { return(string.Create(outlen, (Bytes: (IntPtr)bytesPtr, bytes.Length, Options: options), (span, state) => { fixed(char *outPtr = &MemoryMarshal.GetReference(span)) { ConvertToHexArray(outPtr, span.Length, (byte *)state.Bytes, state.Length, state.Options); } })); } } }
/// <summary> /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with hex characters. /// Parameters specify the subset as an offset in the input array, the number of elements in the array to convert, /// whether to insert line breaks in the return value, and whether to insert upper- or lowercase hex characters. /// </summary> /// <param name="inArray">An array of 8-bit unsigned integers.</param> /// <param name="offset">An offset in <paramref name="inArray"/>.</param> /// <param name="length">The number of elements of <paramref name="inArray"/> to convert.</param> /// <param name="options"><see cref="HexFormattingOptions.Lowercase"/> to produce lowercase output. <see cref="HexFormattingOptions.InsertLineBreaks"/> to insert a line break every 72 characters. <see cref="HexFormattingOptions.None"/> to do neither.</param> /// <returns>The string representation in hex of <paramref name="length"/> elements of <paramref name="inArray"/>, starting at position <paramref name="offset"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="inArray"/> is <code>null</code>.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="length"/> is negative.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> plus <paramref name="length"/> is greater than the length of <paramref name="inArray"/>.</exception> /// <exception cref="ArgumentException"><paramref name="options"/> is not a valid <see cref="HexFormattingOptions"/> value.</exception> public static string ToHexString(byte[] inArray, int offset, int length, HexFormattingOptions options = default) { if (inArray == null) { throw new ArgumentNullException(nameof(inArray)); } if (length < 0) { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive); } if (offset > (inArray.Length - length)) { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength); } if (options < HexFormattingOptions.None || options > (HexFormattingOptions.Lowercase | HexFormattingOptions.InsertLineBreaks)) { throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); } if (length == 0) { return(string.Empty); } var insertLineBreaks = (options & HexFormattingOptions.InsertLineBreaks) != 0; var outlen = ToHex_CalculateAndValidateOutputLength(inArray.Length - offset, insertLineBreaks); #if SPAN return(string.Create(outlen, (arr: inArray, off: offset, len: length, opt: options), (span, state) => { unsafe { fixed(byte *inPtr = state.arr) fixed(char *outPtr = &MemoryMarshal.GetReference(span)) { ConvertToHexArray(outPtr, span.Length, inPtr + state.off, state.len, state.opt); } } })); #else var result = new string('\0', outlen); unsafe { fixed(byte *inPtr = inArray) fixed(char *outPtr = result) { ConvertToHexArray(outPtr, outlen, inPtr + offset, length, options); } } return(result); #endif }
public void Encode(int length, HexFormattingOptions options) { var inArray = new byte[length]; _random.NextBytes(inArray); var expected = EncodeSlow(inArray, options); var result = ProcessBytes(inArray, expected.Length, options); Assert.Equal(expected, result); }
protected override string ProcessBytes(byte[] bytes, int expectedLength, HexFormattingOptions options) { var resultLength = _random.Next(expectedLength + 3, expectedLength + 10); var resultOffset = _random.Next(0, resultLength - expectedLength); var result = new char[resultLength]; var written = Convert.ToHexCharArray(bytes, 0, bytes.Length, result, resultOffset, options); Assert.Equal(expectedLength, written); return(new string(result, resultOffset, written)); }
private static string EncodeSlow(byte[] input, HexFormattingOptions options) { const int insertLineBreaksEvery = 72; var result = BitConverter.ToString(input).Replace("-", string.Empty); if (options.HasFlag(HexFormattingOptions.InsertLineBreaks) && result.Length > insertLineBreaksEvery) { var newResult = new StringBuilder(); for (var i = 0; i < result.Length; i += insertLineBreaksEvery) { newResult.Append(result.Substring(i, Math.Min(insertLineBreaksEvery, result.Length - i))); newResult.Append("\r\n"); // Convert does not add platform-specific new-lines } result = newResult.ToString().TrimEnd(); } if (options.HasFlag(HexFormattingOptions.Lowercase)) { return(result.ToLower()); } return(result); }
protected abstract void AcceptOptions(HexFormattingOptions options);
private static unsafe void ConvertToHexArray(char *outChars, int outLength, byte *inData, int inLength, HexFormattingOptions options) { const int perLine = hexLineBreakPosition / 2; var remaining = inLength; var src = inData + inLength; var dest = outChars + outLength; var toLower = (options & HexFormattingOptions.Lowercase) != 0; var insertLineBreaks = (options & HexFormattingOptions.InsertLineBreaks) != 0; if (!insertLineBreaks) { #if INTRINSICS if (Avx2.IsSupported & remaining >= 32) { remaining -= Utf16HexFormatter.Avx2.Format(ref src, ref dest, remaining, toLower); Debug.Assert(remaining < 32); } if (remaining >= 16) { if (Ssse3.IsSupported) { remaining -= Utf16HexFormatter.Ssse3.Format(ref src, ref dest, remaining, toLower); Debug.Assert(remaining < 16); } else if (Sse2.IsSupported) { remaining -= Utf16HexFormatter.Sse2.Format(ref src, ref dest, remaining, toLower); Debug.Assert(remaining < 16); } } #endif if (remaining > 0) { remaining -= Utf16HexFormatter.Fixed.Format(ref src, ref dest, remaining, toLower); } Debug.Assert(remaining == 0); } else { var remainder = remaining % perLine; var remainingOnCurrentLine = remainder == 0 ? perLine : remainder; var bytesInIteration = remainingOnCurrentLine; while (remaining != 0) { #if INTRINSICS if (Avx2.IsSupported) { if (remainingOnCurrentLine >= 32) { remainingOnCurrentLine -= Utf16HexFormatter.Avx2.Format(ref src, ref dest, remainingOnCurrentLine, toLower); Debug.Assert(remainingOnCurrentLine < 32); } } else if (remainingOnCurrentLine >= 16) { // Never run SSE code if AVX2 is available, as the remaining 8 bytes per line cannot be SSE-processed if (Ssse3.IsSupported) { remainingOnCurrentLine -= Utf16HexFormatter.Ssse3.Format(ref src, ref dest, remainingOnCurrentLine, toLower); Debug.Assert(remainingOnCurrentLine < 16); } else if (Sse2.IsSupported) { remainingOnCurrentLine -= Utf16HexFormatter.Sse2.Format(ref src, ref dest, remainingOnCurrentLine, toLower); Debug.Assert(remainingOnCurrentLine < 16); } } #endif if (remainingOnCurrentLine > 0) { remainingOnCurrentLine -= Utf16HexFormatter.Fixed.Format(ref src, ref dest, remainingOnCurrentLine, toLower); } Debug.Assert(remainingOnCurrentLine == 0); remaining -= bytesInIteration; if (remaining == 0) { return; } *(--dest) = '\n'; *(--dest) = '\r'; remainingOnCurrentLine = perLine; bytesInIteration = remainingOnCurrentLine; } } }
/// <summary> /// Converts a span of 8-bit unsigned integers to an equivalent span of a Unicode characters encoded with hex characters. /// A parameter specifies whether to insert line breaks in the return value and whether to insert upper- or lowercase hex characters. /// </summary> /// <param name="bytes">The input span.</param> /// <param name="chars">The output span</param> /// <param name="charsWritten">The number of bytes written into <paramref name="chars"/>.</param> /// <param name="options"><see cref="HexFormattingOptions.Lowercase"/> to produce lowercase output. <see cref="HexFormattingOptions.InsertLineBreaks"/> to insert a line break every 72 characters. <see cref="HexFormattingOptions.None"/> to do neither.</param> /// <returns>Whether the conversion operation completed successfully.</returns> /// <exception cref="ArgumentException"><paramref name="options"/> is not a valid <see cref="HexFormattingOptions"/> value.</exception> public static bool TryToHexChars(ReadOnlySpan <byte> bytes, Span <char> chars, out int charsWritten, HexFormattingOptions options = default) { if (options < HexFormattingOptions.None || options > (HexFormattingOptions.Lowercase | HexFormattingOptions.InsertLineBreaks)) { throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); } if (bytes.Length == 0) { charsWritten = 0; return(true); } var insertLineBreaks = (options & HexFormattingOptions.InsertLineBreaks) != 0; var outlen = ToHex_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); if (chars.Length < outlen) { charsWritten = 0; return(false); } unsafe { fixed(byte *inPtr = &MemoryMarshal.GetReference(bytes)) fixed(char *outPtr = &MemoryMarshal.GetReference(chars)) { ConvertToHexArray(outPtr, chars.Length, inPtr, bytes.Length, options); } } charsWritten = outlen; return(true); }
/// <summary> /// Converts a subset of an 8-bit unsigned integer array to an equivalent subset of a Unicode character array encoded with hex characters. /// Parameters specify the subsets as offsets in the input and output arrays, the number of elements in the input array to convert, /// whether line breaks are inserted in the output array, and whether to insert upper- or lowercase hex characters. /// </summary> /// <param name="inArray">An input array of 8-bit unsigned integers.</param> /// <param name="offsetIn">A position within <paramref name="inArray"/>.</param> /// <param name="length">The number of elements of <paramref name="inArray"/> to convert.</param> /// <param name="outArray">An output array of Unicode characters.</param> /// <param name="offsetOut">A position within <paramref name="outArray"/>.</param> /// <param name="options"><see cref="HexFormattingOptions.Lowercase"/> to produce lowercase output. <see cref="HexFormattingOptions.InsertLineBreaks"/> to insert a line break every 72 characters. <see cref="HexFormattingOptions.None"/> to do neither.</param> /// <returns>A 32-bit signed integer containing the number of bytes in <paramref name="outArray"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="inArray"/> or <paramref name="outArray"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offsetIn"/>, <paramref name="offsetOut"/>, or <paramref name="length"/> is negative.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offsetIn"/> plus <paramref name="length"/> is greater than the length of <paramref name="inArray"/>.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offsetOut"/> plus the number of elements to return is greater than the length of <paramref name="outArray"/>.</exception> /// <exception cref="ArgumentException"><paramref name="options"/> is not a valid <see cref="HexFormattingOptions"/> value.</exception> public static int ToHexCharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut, HexFormattingOptions options = default) { if (inArray == null) { throw new ArgumentNullException(nameof(inArray)); } if (length < 0) { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); } if (offsetIn < 0) { throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_GenericPositive); } if (offsetIn > (inArray.Length - length)) { throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_OffsetLength); } if (outArray == null) { throw new ArgumentNullException(nameof(outArray)); } if (offsetOut < 0) { throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_GenericPositive); } if (options < HexFormattingOptions.None || options > (HexFormattingOptions.Lowercase | HexFormattingOptions.InsertLineBreaks)) { throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); } var insertLineBreaks = (options & HexFormattingOptions.InsertLineBreaks) != 0; var outlen = ToHex_CalculateAndValidateOutputLength(inArray.Length - offsetIn, insertLineBreaks); if (offsetOut > outArray.Length - outlen) { throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_OffsetOut); } unsafe { fixed(byte *inPtr = inArray) fixed(char *outPtr = outArray) { ConvertToHexArray(outPtr + offsetOut, outlen, inPtr + offsetIn, length, options); } } return(outlen); }
protected override string ProcessBytes(byte[] bytes, int expectedLength, HexFormattingOptions options) { var result = Convert.ToHexString(bytes.AsSpan(), options); Assert.Equal(expectedLength, result.Length); return(result); }
protected override void AcceptOptions(HexFormattingOptions options) { Convert.ToHexString(ReadOnlySpan <byte> .Empty, options); }
protected override string ProcessBytes(byte[] bytes, int expectedLength, HexFormattingOptions options) { var buffer = new char[expectedLength]; var result = Convert.TryToHexChars(bytes.AsSpan(), buffer.AsSpan(), out var written, options); Assert.Equal(expectedLength, written); Assert.True(result); return(new string(buffer)); }
protected override void AcceptOptions(HexFormattingOptions options) { Convert.TryToHexChars(ReadOnlySpan <byte> .Empty, Span <char> .Empty, out _, options); }
protected override void AcceptOptions(HexFormattingOptions options) { Convert.ToHexString(Array.Empty <byte>(), options); }
protected abstract string ProcessBytes(byte[] bytes, int expectedLength, HexFormattingOptions options);
/// <summary> /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with hex characters. /// A parameter specifies whether to insert line breaks in the return value and whether to insert upper- or lowercase hex characters. /// </summary> /// <param name="inArray">An array of 8-bit unsigned integers.</param> /// <param name="options"><see cref="HexFormattingOptions.Lowercase"/> to produce lowercase output. <see cref="HexFormattingOptions.InsertLineBreaks"/> to insert a line break every 72 characters. <see cref="HexFormattingOptions.None"/> to do neither.</param> /// <returns>The string representation in hex of the elements in <paramref name="inArray"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="inArray"/> is <code>null</code>.</exception> /// <exception cref="ArgumentException"><paramref name="options"/> is not a valid <see cref="HexFormattingOptions"/> value.</exception> public static string ToHexString(byte[] inArray, HexFormattingOptions options = default) => ToHexString(inArray, 0, inArray?.Length ?? 0, options);
protected override void AcceptOptions(HexFormattingOptions options) { Convert.ToHexCharArray(Array.Empty <byte>(), 0, 0, Array.Empty <char>(), 0, options); }