using System; namespace SabreTools.Numerics.Extensions { public static class ByteArrayExtensions { /// /// Indicates if an array contains all ASCII numeric digits /// public static bool IsNumericArray(this byte[] arr) { // Empty arrays cannot be numeric if (arr.Length == 0) return false; // '0' to '9' return Array.TrueForAll(arr, b => b >= 0x30 && b <= 0x39); } /// /// Add an integer value to a number represented by a byte array /// /// Byte array to add to /// Amount to add /// Byte array representing the new value /// Assumes array values are in big-endian format public static byte[] Add(this byte[] self, uint add) { // If nothing is being added, just return if (add == 0) return self; // Get the big-endian representation of the value byte[] addBytes = BitConverter.GetBytes(add); Array.Reverse(addBytes); // Pad the array out to 16 bytes byte[] paddedBytes = new byte[16]; Array.Copy(addBytes, 0, paddedBytes, 12, 4); // If the input is empty, just return the added value if (self.Length == 0) return paddedBytes; return self.Add(paddedBytes); } /// /// Add two numbers represented by byte arrays /// /// Byte array to add to /// Amount to add /// Byte array representing the new value /// Assumes array values are in big-endian format public static byte[] Add(this byte[] self, byte[] add) { // If either input is empty if (self.Length == 0 && add.Length == 0) return []; else if (self.Length > 0 && add.Length == 0) return self; else if (self.Length == 0 && add.Length > 0) return add; // Setup the output array int outLength = Math.Max(self.Length, add.Length); byte[] output = new byte[outLength]; // Loop adding with carry uint carry = 0; for (int i = 0; i < outLength; i++) { int selfIndex = self.Length - i - 1; uint selfValue = selfIndex >= 0 ? self[selfIndex] : 0u; int addIndex = add.Length - i - 1; uint addValue = addIndex >= 0 ? add[addIndex] : 0u; uint next = selfValue + addValue + carry; carry = next >> 8; int outputIndex = output.Length - i - 1; output[outputIndex] = (byte)(next & 0xFF); } return output; } /// /// Perform a rotate left on a byte array /// /// Byte array value to rotate /// Number of bits to rotate /// Rotated byte array value /// Assumes array values are in big-endian format public static byte[] RotateLeft(this byte[] self, int numBits) { // If either input is empty if (self.Length == 0) return []; else if (numBits == 0) return self; byte[] output = new byte[self.Length]; Array.Copy(self, output, output.Length); // Shift by bytes while (numBits >= 8) { byte temp = output[0]; for (int i = 0; i < output.Length - 1; i++) { output[i] = output[i + 1]; } output[output.Length - 1] = temp; numBits -= 8; } // Shift by bits if (numBits > 0) { byte bitMask = (byte)(8 - numBits), carry, wrap = 0; for (int i = 0; i < output.Length; i++) { carry = (byte)(((255 << bitMask) & output[i]) >> bitMask); // Make sure the first byte carries to the end if (i == 0) wrap = carry; // Otherwise, move to the last byte else output[i - 1] |= carry; // Shift the current bits output[i] <<= numBits; } // Make sure the wrap happens output[output.Length - 1] |= wrap; } return output; } /// /// XOR two numbers represented by byte arrays /// /// Byte array to XOR to /// Amount to XOR /// Byte array representing the new value /// Assumes array values are in big-endian format public static byte[] Xor(this byte[] self, byte[] xor) { // If either input is empty if (self.Length == 0 && xor.Length == 0) return []; else if (self.Length > 0 && xor.Length == 0) return self; else if (self.Length == 0 && xor.Length > 0) return xor; // Setup the output array int outLength = Math.Max(self.Length, xor.Length); byte[] output = new byte[outLength]; // Loop XOR for (int i = 0; i < outLength; i++) { int selfIndex = self.Length - i - 1; uint selfValue = selfIndex >= 0 ? self[selfIndex] : 0u; int xorIndex = xor.Length - i - 1; uint xorValue = xorIndex >= 0 ? xor[xorIndex] : 0u; uint next = selfValue ^ xorValue; int outputIndex = output.Length - i - 1; output[outputIndex] = (byte)(next & 0xFF); } return output; } } }