using System; using System.Globalization; using System.IO; using System.Text; using System.Security.Cryptography; namespace BizHawk.Common.BufferExtensions { public static class BufferExtensions { [Obsolete] // do we know of any situation where SaveAsHexFast doesn't work? public static void SaveAsHex(this byte[] buffer, TextWriter writer) { foreach (var b in buffer) { writer.Write("{0:X2}", b); } writer.WriteLine(); } private static readonly char[] HexConvArr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public static unsafe void SaveAsHexFast(this byte[] buffer, TextWriter writer) { fixed (char* table = HexConvArr) { if (buffer.Length > 0) { int len = buffer.Length; fixed (byte* src = buffer) for (int i = 0; i < len; i++) { writer.Write(table[src[i] >> 4]); writer.Write(table[src[i] & 15]); } } } writer.WriteLine(); } [Obsolete] // do we know of any situation where SaveAsHexFast doesn't work? public static void SaveAsHex(this byte[] buffer, TextWriter writer, int length) { for (int i = 0; i < length; i++) { writer.Write("{0:X2}", buffer[i]); } writer.WriteLine(); } public static void SaveAsHex(this short[] buffer, TextWriter writer) { foreach (var b in buffer) { writer.Write("{0:X4}", b); } writer.WriteLine(); } public static void SaveAsHex(this ushort[] buffer, TextWriter writer) { foreach (var b in buffer) { writer.Write("{0:X4}", b); } writer.WriteLine(); } public static void SaveAsHex(this int[] buffer, TextWriter writer) { foreach (int b in buffer) { writer.Write("{0:X8}", b); } writer.WriteLine(); } public static void SaveAsHex(this uint[] buffer, TextWriter writer) { foreach (var b in buffer) { writer.Write("{0:X8}", b); } writer.WriteLine(); } public static void ReadFromHex(this byte[] buffer, string hex) { if (hex.Length % 2 != 0) { throw new Exception("Hex value string does not appear to be properly formatted."); } for (int i = 0; i < buffer.Length && i * 2 < hex.Length; i++) { var bytehex = "" + hex[i * 2] + hex[(i * 2) + 1]; buffer[i] = byte.Parse(bytehex, NumberStyles.HexNumber); } } public static unsafe void ReadFromHexFast(this byte[] buffer, string hex) { if (buffer.Length * 2 != hex.Length) { throw new Exception("Data size mismatch"); } int count = buffer.Length; fixed (byte* _dst = buffer) fixed (char* _src = hex) { byte* dst = _dst; char* src = _src; while (count > 0) { // in my tests, replacing Hex2Int() with a 256 entry LUT slowed things down slightly *dst++ = (byte)(Hex2Int(*src++) << 4 | Hex2Int(*src++)); count--; } } } public static void ReadFromHex(this short[] buffer, string hex) { if (hex.Length % 4 != 0) { throw new Exception("Hex value string does not appear to be properly formatted."); } for (int i = 0; i < buffer.Length && i * 4 < hex.Length; i++) { var shorthex = "" + hex[i * 4] + hex[(i * 4) + 1] + hex[(i * 4) + 2] + hex[(i * 4) + 3]; buffer[i] = short.Parse(shorthex, NumberStyles.HexNumber); } } public static void ReadFromHex(this ushort[] buffer, string hex) { if (hex.Length % 4 != 0) { throw new Exception("Hex value string does not appear to be properly formatted."); } for (int i = 0; i < buffer.Length && i * 4 < hex.Length; i++) { var ushorthex = "" + hex[i * 4] + hex[(i * 4) + 1] + hex[(i * 4) + 2] + hex[(i * 4) + 3]; buffer[i] = ushort.Parse(ushorthex, NumberStyles.HexNumber); } } public static void ReadFromHex(this int[] buffer, string hex) { if (hex.Length % 8 != 0) { throw new Exception("Hex value string does not appear to be properly formatted."); } for (int i = 0; i < buffer.Length && i * 8 < hex.Length; i++) { var inthex = hex.Substring(i * 8, 8); buffer[i] = int.Parse(inthex, NumberStyles.HexNumber); } } /// /// Converts bytes to an uppercase string of hex numbers in upper case without any spacing or anything /// public static string BytesToHexString(this byte[] bytes) { var sb = new StringBuilder(); foreach (var b in bytes) { sb.AppendFormat("{0:X2}", b); } return sb.ToString(); } public static bool FindBytes(this byte[] array, byte[] pattern) { var fidx = 0; int result = Array.FindIndex(array, 0, array.Length, (byte b) => { fidx = (b == pattern[fidx]) ? fidx + 1 : 0; return fidx == pattern.Length; }); return result >= pattern.Length - 1; } public static string HashMD5(this byte[] data, int offset, int len) { using (var md5 = MD5.Create()) { md5.ComputeHash(data, offset, len); return md5.Hash.BytesToHexString(); } } public static string HashMD5(this byte[] data) { return HashMD5(data, 0, data.Length); } public static string HashSHA1(this byte[] data, int offset, int len) { using (var sha1 = SHA1.Create()) { sha1.ComputeHash(data, offset, len); return sha1.Hash.BytesToHexString(); } } public static string HashSHA1(this byte[] data) { return HashSHA1(data, 0, data.Length); } private static int Hex2Int(char c) { if (c <= '9') { return c - '0'; } if (c <= 'F') { return c - '7'; } return c - 'W'; } } }