handling Handling hexadecimal strings &and byte arrays
Kittoes0124
- 2k
- 2
- 18
- 24
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private const string ALPHABET = "0123456789ABCDEFabcdef";
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(ai => (((uint)(GetChar((byte)(ai & 0x0000000F0b1111)) << 16)) | GetChar((byte)(ai >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 103)
.Select(ai => ("0123456789ABCDEFabcdef"ALPHABET.Contains((char)ai) ? GetNybble((char)ai) : ((byte)0xFF255)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High { get => m_high; }
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low { get => m_low; }
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (prefixLength < 0) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero", paramName: nameof(prefixLength));
}
if (value == null.IsNull()) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if (stringLength < 1) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
return Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 0x67103) && (low < 0x67103)) && ((m[high] < 0xFF255) && (m[low] < 0xFF255))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value == null.IsNull()) {
return null;
}
prefix = (prefix ?? string.Empty);
if (value.Length == 0IsEmpty()) {
return prefix;
}
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)unchecked((55 + value) + (((value - 10) >> 31) & 0xFFFFFFF90b11111111_11111001)));
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
if (ALPHABET.Contains(value)) {
return ((byte)unchecked(((value - 48) + (((57 - value) >> 31) & 0xFFFFFFF90b1001)) & 0x0000000F0b1111));
}
else {
throw new ArgumentOutOfRangeException(message: ("invalid hexadecimal character encountered: " + value), paramName: nameof(value));
}
}
}
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(a => (((uint)(GetChar((byte)(a & 0x0000000F)) << 16)) | GetChar((byte)(a >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 103)
.Select(a => ("0123456789ABCDEFabcdef".Contains((char)a) ? GetNybble((char)a) : ((byte)0xFF)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High => m_high;
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low => m_low;
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (prefixLength < 0) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero", paramName: nameof(prefixLength));
}
if (value == null) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if (stringLength < 1) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
return Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 0x67) && (low < 0x67)) && ((m[high] < 0xFF) && (m[low] < 0xFF))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value == null) {
return null;
}
prefix = (prefix ?? string.Empty);
if (value.Length == 0) {
return prefix;
}
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)((55 + value) + (((value - 10) >> 31) & 0xFFFFFFF9)));
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
return ((byte)(((value - 48) + (((57 - value) >> 31) & 0xFFFFFFF9)) & 0x0000000F));
}
}
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private const string ALPHABET = "0123456789ABCDEFabcdef";
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(i => (((uint)(GetChar((byte)(i & 0b1111)) << 16)) | GetChar((byte)(i >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 103)
.Select(i => (ALPHABET.Contains((char)i) ? GetNybble((char)i) : ((byte)255)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High { get => m_high; }
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low { get => m_low; }
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (prefixLength < 0) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero", paramName: nameof(prefixLength));
}
if (value.IsNull()) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if (stringLength < 1) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
return Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 103) && (low < 103)) && ((m[high] < 255) && (m[low] < 255))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value.IsNull()) {
return null;
}
prefix = (prefix ?? string.Empty);
if (value.IsEmpty()) {
return prefix;
}
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)unchecked((55 + value) + (((value - 10) >> 31) & 0b11111111_11111001)));
}
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
if (ALPHABET.Contains(value)) {
return ((byte)unchecked(((value - 48) + (((57 - value) >> 31) & 0b1001)) & 0b1111));
}
else {
throw new ArgumentOutOfRangeException(message: ("invalid hexadecimal character encountered: " + value), paramName: nameof(value));
}
}
}
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(ia => (((uint)(GetChar((byte)(ia & 0x0000000F)) << 16)) | GetChar((byte)(ia >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 256103)
.Select(ia => ("0123456789ABCDEFabcdef".Contains((char)ia) ? GetNybble((char)ia) : ((byte)0xFF)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High => m_high;
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low => m_low;
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (prefixLength < 0) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero", paramName: nameof(prefixLength));
}
if (value == null) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if ((prefixLength < 0) || (prefixLength > stringLength)) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero or greater than hexadecimal string length", paramName: nameof(prefixLength));
}
if (stringLength ==< 01) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
return Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 0xFF0x67) && (low < 0xFF0x67)) && ((m[high] < 0xFF) && (m[low] < 0xFF))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value == null) {
return null;
}
prefix = (prefix ?? string.Empty);
if (value.Length == 0) {
return string.Empty;prefix;
}
prefix = (prefix ?? string.Empty);
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)((55 + value) + (((value - 10) >> 31) & 0xFFFFFFF9)));
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
return ((byte)(((value - 48) + (((57 - value) >> 31) & 0xFFFFFFF9)) & 0x0000000F));
}
}
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(i => (((uint)(GetChar((byte)(i & 0x0000000F)) << 16)) | GetChar((byte)(i >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 256)
.Select(i => ("0123456789ABCDEFabcdef".Contains((char)i) ? GetNybble((char)i) : ((byte)0xFF)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High => m_high;
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low => m_low;
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (value == null) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if ((prefixLength < 0) || (prefixLength > stringLength)) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero or greater than hexadecimal string length", paramName: nameof(prefixLength));
}
if (stringLength == 0) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 0xFF) && (low < 0xFF)) && ((m[high] < 0xFF) && (m[low] < 0xFF))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value == null) {
return null;
}
if (value.Length == 0) {
return string.Empty;
}
prefix = (prefix ?? string.Empty);
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)((55 + value) + (((value - 10) >> 31) & 0xFFFFFFF9)));
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
return ((byte)(((value - 48) + (((57 - value) >> 31) & 0xFFFFFFF9)) & 0x0000000F));
}
}
/// <summary>
/// Represents a single byte in hexadecimal notation.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 4)]
public struct Hexadecimal
{
private static readonly uint[] m_byteToHexMap = Enumerable
.Range(0, 256)
.Select(a => (((uint)(GetChar((byte)(a & 0x0000000F)) << 16)) | GetChar((byte)(a >> 4))))
.ToArray();
private static readonly unsafe Hexadecimal* m_byteToHexMapPointer = ((Hexadecimal*)GCHandle.Alloc(m_byteToHexMap, GCHandleType.Pinned).AddrOfPinnedObject());
private static readonly byte[] m_hexToByteMap = Enumerable
.Range(0, 103)
.Select(a => ("0123456789ABCDEFabcdef".Contains((char)a) ? GetNybble((char)a) : ((byte)0xFF)))
.ToArray();
private static readonly unsafe byte* m_hexToByteMapPointer = ((byte*)GCHandle.Alloc(m_hexToByteMap, GCHandleType.Pinned).AddrOfPinnedObject());
[FieldOffset((sizeof(char) * 0))]
private readonly char m_high;
[FieldOffset((sizeof(char) * 1))]
private readonly char m_low;
/// <summary>
/// Returns the left symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char High => m_high;
/// <summary>
/// Returns the right symbol of this <see cref="Hexadecimal"/> value.
/// </summary>
public char Low => m_low;
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
/// <param name="prefixLength">The length of the prefix to be skipped over.</param>
public static unsafe byte[] GetBytes(string value, int prefixLength) {
if (prefixLength < 0) {
throw new ArgumentOutOfRangeException(message: "prefix length cannot be less than zero", paramName: nameof(prefixLength));
}
if (value == null) {
return null;
}
var stringLength = checked(value.Length - prefixLength);
if (stringLength < 1) {
#if (NET40 || NET45 || NET451 || NET452)
return new byte[0];
#else
return Array.Empty<byte>();
#endif
}
if (stringLength.IsOdd()) {
throw new ArgumentOutOfRangeException(message: "hexadecimal string cannot have an odd length", paramName: nameof(value));
}
var count = (stringLength >> 1);
var result = new byte[count];
fixed (char* source = value)
fixed (byte* target = &result[0]) {
var m = m_hexToByteMapPointer;
var s = ((Hexadecimal*)(source + prefixLength));
var t = target;
while (0 < count--) {
var high = (*s).High;
var low = (*s).Low;
if (((high < 0x67) && (low < 0x67)) && ((m[high] < 0xFF) && (m[low] < 0xFF))) {
*t = ((byte)(((uint)(m[high] << 4)) | m[low]));
}
else {
throw new IndexOutOfRangeException(message: ("invalid hexadecimal string encountered: " + high + low));
}
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts a hexadecimal string into an array of bytes.
/// </summary>
/// <param name="value">The hexadecimal string that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static byte[] GetBytes(string value) {
return GetBytes(value, 0);
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
/// <param name="prefix">The prefix that will be appended to the hexadecimal string.</param>
public static unsafe string GetString(byte[] value, string prefix) {
if (value == null) {
return null;
}
prefix = (prefix ?? string.Empty);
if (value.Length == 0) {
return prefix;
}
var count = value.Length;
var result = new string('☠', ((count << 1) + prefix.Length));
fixed (byte* source = &value[0])
fixed (char* target = result) {
for (var i = 0; i < prefix.Length; i++) {
*(target + i) = prefix[i];
}
var m = m_byteToHexMapPointer;
var s = source;
var t = ((Hexadecimal*)(target + prefix.Length));
while (0 < count--) {
*t = m[*s];
s++;
t++;
}
}
return result;
}
/// <summary>
/// Converts an array of bytes into a hexadecimal string.
/// </summary>
/// <param name="value">The array of bytes that will be converted.</param>
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static unsafe string GetString(byte[] value) {
return GetString(value, string.Empty);
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static char GetChar(byte value) { // https://stackoverflow.com/a/14333437
return ((char)((55 + value) + (((value - 10) >> 31) & 0xFFFFFFF9)));
}
#if !(NET40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static byte GetNybble(char value) { // https://stackoverflow.com/a/20695932
return ((byte)(((value - 48) + (((57 - value) >> 31) & 0xFFFFFFF9)) & 0x0000000F));
}
}
Loading
lang-cs