mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-07-08 17:57:02 +00:00
Move some math/numeric byte array extensions
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
using System.Text;
|
||||
using SabreTools.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
@@ -92,121 +90,5 @@ namespace SabreTools.IO.Extensions.Test
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsNumericArray
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Empty_False()
|
||||
{
|
||||
byte[] arr = [];
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_NonNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABCDEF");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_MixedNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABC123");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Numeric_True()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("0123456789");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[0], 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xA4 })]
|
||||
public void Add_NumericInput(byte[] self, uint add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
public void Add_ArrayInput(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RotateLeft
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[] { 0x01 }, 0, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x01 }, 1, new byte[] { 0x02 })]
|
||||
[InlineData(new byte[] { 0x80 }, 1, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 0, new byte[] { 0x00, 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 1, new byte[] { 0x00, 0x02 })]
|
||||
[InlineData(new byte[] { 0x00, 0x80 }, 1, new byte[] { 0x01, 0x00 })]
|
||||
[InlineData(new byte[] { 0x80, 0x00 }, 1, new byte[] { 0x00, 0x01 })]
|
||||
public void RotateLeftTest(byte[] self, int numBits, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.RotateLeft(numBits);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xor
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
public void XorTest(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Xor(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -25,7 +24,7 @@ namespace SabreTools.IO.Extensions
|
||||
// Align the stream position
|
||||
while (offset % alignment != 0 && offset < input.Length)
|
||||
{
|
||||
_ = input.ReadByteValue(ref offset);
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Return if the alignment completed
|
||||
@@ -39,188 +38,5 @@ namespace SabreTools.IO.Extensions
|
||||
{
|
||||
return array is null || array.Length == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if an array contains all ASCII numeric digits
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
#region Math
|
||||
|
||||
/// <summary>
|
||||
/// Add an integer value to a number represented by a byte array
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add two numbers represented by byte arrays
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a rotate left on a byte array
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array value to rotate</param>
|
||||
/// <param name="numBits">Number of bits to rotate</param>
|
||||
/// <returns>Rotated byte array value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XOR two numbers represented by byte arrays
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to XOR to</param>
|
||||
/// <param name="xor">Amount to XOR</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
125
SabreTools.Numerics.Extensions.Test/ByteArrayExtensionsTests.cs
Normal file
125
SabreTools.Numerics.Extensions.Test/ByteArrayExtensionsTests.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Text;
|
||||
using SabreTools.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Numerics.Extensions.Test
|
||||
{
|
||||
public class ByteArrayExtensionsTests
|
||||
{
|
||||
#region IsNumericArray
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Empty_False()
|
||||
{
|
||||
byte[] arr = [];
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_NonNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABCDEF");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_MixedNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABC123");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Numeric_True()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("0123456789");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[0], 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xA4 })]
|
||||
public void Add_NumericInput(byte[] self, uint add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
public void Add_ArrayInput(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RotateLeft
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[] { 0x01 }, 0, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x01 }, 1, new byte[] { 0x02 })]
|
||||
[InlineData(new byte[] { 0x80 }, 1, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 0, new byte[] { 0x00, 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 1, new byte[] { 0x00, 0x02 })]
|
||||
[InlineData(new byte[] { 0x00, 0x80 }, 1, new byte[] { 0x01, 0x00 })]
|
||||
[InlineData(new byte[] { 0x80, 0x00 }, 1, new byte[] { 0x00, 0x01 })]
|
||||
public void RotateLeftTest(byte[] self, int numBits, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.RotateLeft(numBits);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xor
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
public void XorTest(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Xor(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Numerics.Extensions\SabreTools.Numerics.Extensions.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Matching\SabreTools.Matching.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
186
SabreTools.Numerics.Extensions/ByteArrayExtensions.cs
Normal file
186
SabreTools.Numerics.Extensions/ByteArrayExtensions.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Numerics.Extensions
|
||||
{
|
||||
public static class ByteArrayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if an array contains all ASCII numeric digits
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an integer value to a number represented by a byte array
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add two numbers represented by byte arrays
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a rotate left on a byte array
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array value to rotate</param>
|
||||
/// <param name="numBits">Number of bits to rotate</param>
|
||||
/// <returns>Rotated byte array value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XOR two numbers represented by byte arrays
|
||||
/// </summary>
|
||||
/// <param name="self">Byte array to XOR to</param>
|
||||
/// <param name="xor">Amount to XOR</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
/// <remarks>Assumes array values are in big-endian format</remarks>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Security.Cryptography
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<ProjectReference Include="..\SabreTools.IO.Extensions\SabreTools.IO.Extensions.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Matching\SabreTools.Matching.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Numerics\SabreTools.Numerics.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Numerics.Extensions\SabreTools.Numerics.Extensions.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Text.Compare\SabreTools.Text.Compare.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user