General Byte Array Operations
There exist a number of operations one may want to do on an array of bytes that don’t directly relate to explicit higher order mathematical operations or typical bitwise operations, baring any other name space we’re considering these general operations. Typically their about building, transforming, mutating, or gathering meta data.
Unless otherwise stated the following methods are statically defined in the ByteArrayUtils class and do not modify their input.
Byte Array Creation and Population
It is usually easy enough to new up a new byte array, however sometimes something a little more exotic than an array of 0x00 bytes are desired.
Create
It can be necessary to create a byte array filled with a known value. In this case ByteArrayUtils.CreateByteArray can be used to create a byte array of a given length filled with an optional element value.
public static byte[] ByteArrayUtils.CreateByteArray(int length, byte element = 0x00)
In the following example a byte array of length 10 is filled with the the repeated byte value of 0x42:
public static void CreateByteArrayExample()
{
// Setup
const int length = 10;
const byte element = 0x42; // optional, defaults to 0x00
// Act
// creates a byte array of length 10, filled with bytes of 0x42
var result = ByteArrayUtils.CreateByteArray(length, element);
// Conclusion
Console.WriteLine("CreateByteArray example");
Console.WriteLine($"length:\t{length}");
Console.WriteLine($"element:\t{element}");
Console.WriteLine($"result:\t{result.ToString(\"H\")}");
}
CreateByteArray example
length: 10
element: 66
result: 42 42 42 42 42 42 42 42 42 42
Creates a byte array resultBytes with a value of [0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42].
Byte Array Mutation
Byte arrays often need to be altered in some way to process them, the addition of needing to be concerned with endiness can make this a bit less straightforward.
Trimming
Leading zero byte trimming works similarly for both big and little endian byte arrays. In both cases leading, or most significant, zero value bytes are removed. For big endian those bytes starting at the 0th index are removed, whereas for little endian zero bytes are removed from the tail of the array.
If a byte array has no most significant zero valued bytes then a copy of the original will be returned.
Big Endian
To trim all 0x00 bytes starting at the 0th index of the byte array
public static byte[] ByteArrayUtils.TrimBigEndianLeadingZeroBytes(this byte[] input)
The following example trims the array [0x00, 0x00, 0x2A, 0x00] returning [0x2A, 0x00]:
public static void TrimBigEndianLeadingZeroBytes()
{
// Setup
var input = new byte[] { 0x00, 0x00, 0x2A, 0x00 };
// Act
var result = input.TrimBigEndianLeadingZeroBytes();
// Conclusion
Console.WriteLine("TrimBigEndianLeadingZeroBytes example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
}
TrimBigEndianLeadingZeroBytes example
input: 00 00 2A 00
result: 2A 00
Note that the final 0x00 value was not removed as we were only trimming most significant zero values.
Little Endian
To trim all 0x00 bytes starting at the end of the byte array
public static byte[] ByteArrayUtils.TrimLittleEndianLeadingZeroBytes(this byte[] input)
public static void TrimLittleEndianLeadingZeroBytes()
{
// Setup
var input = new byte[] { 0x2A, 0xFF, 0x2A, 0x00 };
// Act
var result = input.TrimLittleEndianLeadingZeroBytes();
// Conclusion
Console.WriteLine("TrimLittleEndianLeadingZeroBytes");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
}
TrimLittleEndianLeadingZeroBytes
input: 2A FF 2A 00
result: 2A FF 2A
Padding
When padding a byte array, if the the given array length is equal to or larger than finalLength a copy of the original array will be returned. Otherwise bytes with the value of element will be padded in the most significant value place.
Big Endian
public static byte[] ByteArrayUtils.PadBigEndianMostSignificantBytes(this byte[] source, int finalLength, byte element = 0x00)
public static void PadBigEndianMostSignificantBytesExample()
{
// Setup
var bytes = new byte[] { 0xDE, 0xFA, 0xCE, 0xC0, 0xDE };
const int finalLength = 6;
// Act
var result = bytes.PadBigEndianMostSignificantBytes(finalLength);
// Conclusion
Console.WriteLine("PadBigEndianMostSignificantBytes Short Example");
Console.WriteLine($"input:\t{bytes.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
Console.WriteLine(string.Empty);
}
PadBigEndianMostSignificantBytes Short Example
input: DE FA CE C0 DE
result: 00 DE FA CE C0 DE
Little Endian
public static byte[] ByteArrayUtils.PadLittleEndianMostSignificantBytes(this byte[] source, int finalLength, byte element = 0x00)
public static void PadLittleEndianMostSignificantBytesExample()
{
// Setup
var input = new byte[] { 0xDE, 0xFA, 0xCE, 0xC0, 0xDE };
const int finalLength = 6;
// Act
var result = input.PadLittleEndianMostSignificantBytes(finalLength);
// Conclusion
Console.WriteLine("PadLittleEndianMostSignificantBytes Example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
Console.WriteLine(string.Empty);
}
PadLittleEndianMostSignificantBytes Example
input: DE FA CE C0 DE
result: DE FA CE C0 DE 00
Appending
Appending operations are endian agnostic, new byte values will appear after the highest order index of the input array.
Append Bytes
The ByteArrayUtils.AppendBytes operation simply adds count bytes to the end of the value provided by the source array. The optional element parameter may be provided to use a byte value other than the default 0x00.
public static byte[] ByteArrayUtils.AppendBytes(this byte[] source, int count, byte element = 0x00)
public static void AppendBytesExample()
{
// Setup
var input = new byte[] { 0xC0, 0xC0, 0xCA, 0xFE };
const int count = 4;
// Act
var result = input.AppendBytes(count);
// Conclusion
Console.WriteLine("AppendBytes Example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
Console.WriteLine(string.Empty);
}
AppendBytes Example
input: C0 C0 CA FE
result: C0 C0 CA FE 00 00 00 00
Append Shortest
ByteArrayUtils.AppendShortest works much like ByteArrayUtils.AppendBytes, except instead of providing a desired byte count, the two input arrays lengths are compared and the shortest array is returned, along with the the longest array, with enough 0x00 bytes such that both byte arrays are now the same length.
Effectively this adds most significant 0x00 bytes to the shortest little endian byte array, but may be useful for big endian arrays as well.
public static (byte[] left, byte[] right) ByteArrayUtils.AppendShortest(byte[] left, byte[] right)
public static void AppendShortestExample()
{
// Setup
var lhs = new byte[] { 0xDE, 0xCA, 0xF0 };
var rhs = new byte[] { 0xCA, 0xFE, 0xC0, 0xFF, 0xEE };
// Act
var (lhsResult, rhsResult) = ByteArrayUtils.AppendShortest(lhs, rhs);
// Conclusion
Console.WriteLine("AppendShortest Example");
Console.WriteLine($"lhs:\t{lhs.ToString("H")}");
Console.WriteLine($"rhs:\t{rhs.ToString("H")}");
Console.WriteLine($"lhsResult:\t{lhsResult.ToString("H")}");
Console.WriteLine($"lhsResult:\t{rhsResult.ToString("H")}");
Console.WriteLine(string.Empty);
}
AppendShortest Example
lhs: DE CA F0
rhs: CA FE C0 FF EE
lhsResult: DE CA F0 00 00
lhsResult: CA FE C0 FF EE
Prepend
Prepending operations are endian agnostic, new byte values will appear after the lowest order index of the input array.
Prepend Bytes
The ByteArrayUtils.PrependBytes operation simply adds count bytes to the start of the value provided by the source array. The optional element parameter may be provided to use a byte value other than the default 0x00. This is essentially the inverse of ByteArrayUtils.AppendBytes operation.
public static byte[] ByteArrayUtils.PrependBytes(this byte[] source, int count, byte element = 0x00)
public static void PrependBytesExample()
{
// Setup
var input = new byte[] { 0xC0, 0xC0, 0xCA, 0xFE };
const int count = 4;
// Act
var result = input.PrependBytes(count);
// Conclusion
Console.WriteLine("PrependBytes Example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
Console.WriteLine(string.Empty);
}
PrependBytes Example
input: C0 C0 CA FE
result: 00 00 00 00 C0 C0 CA FE
Prepend Shortest
ByteArrayUtils.PrependShortest works much like ByteArrayUtils.PrependBytes, except instead of providing a desired byte count, the two input arrays lengths are compared and the shortest array is returned, along with the the longest array, with enough 0x00 bytes such that both byte arrays are now the same length.
Effectively this adds most significant 0x00 bytes to the shortest big endian byte array, but may be useful for little endian arrays as well.
public static (byte[] left, byte[] right) ByteArrayUtils.PrependShortest(byte[] left, byte[] right)
public static void PrependShortestExample()
{
// Setup
var lhs = new byte[] { 0xDE, 0xCA, 0xF0 };
var rhs = new byte[] { 0xCA, 0xFE, 0xC0, 0xFF, 0xEE };
// Act
var (lhsResult, rhsResult) = ByteArrayUtils.PrependShortest(lhs, rhs);
// Conclusion
Console.WriteLine("PrependShortest Example");
Console.WriteLine($"lhs:\t{lhs.ToString("H")}");
Console.WriteLine($"rhs:\t{rhs.ToString("H")}");
Console.WriteLine($"lhsResult:\t{lhsResult.ToString("H")}");
Console.WriteLine($"lhsResult:\t{rhsResult.ToString("H")}");
Console.WriteLine(string.Empty);
}
PrependShortest Example
lhs: DE CA F0
rhs: CA FE C0 FF EE
lhsResult: 00 00 DE CA F0
lhsResult: CA FE C0 FF EE
Reversing
Unsurprisingly, hopefully, ByteArrayUtils.ReverseBytes returns the reverse of the provided bytes byte array.
Note
The ReverseBytes operation is endian agnostic.
public static byte[] ByteArrayUtils.ReverseBytes(this byte[] bytes)
public static void ReverseBytesExample()
{
// Setup
var input = new byte[] { 0xC0, 0x1D, 0xC0, 0xFF, 0xEE };
// Act
var result = input.ReverseBytes();
// Conclusion
Console.WriteLine("ReverseBytes example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result.ToString("H")}");
Console.WriteLine(string.Empty);
}
ReverseBytes example
input: C0 1D C0 FF EE
result: EE FF C0 1D C0
Effective Length
Effective length provides the ability to count the number of non-most significant bytes within a byte array. Eg. the length of meaningful bytes within the array.
Big Endian
ByteArrayUtils.BigEndianEffectiveLength returns an int representing the byte length of the given input disregarding the 0x00 bytes at the beginning of the array.
public static int ByteArrayUtils.BigEndianEffectiveLength(this byte[] input)
public static void BigEndianEffectiveLengthExample()
{
// Setup
var input = new byte[] { 0x00, 0x00, 0x00, 0xDA, 0xBD, 0xAD };
// Act
var result = input.BigEndianEffectiveLength();
// Conclusion
Console.WriteLine("BigEndianEffectiveLength Example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result}");
Console.WriteLine(string.Empty);
}
BigEndianEffectiveLength Example
input: 00 00 00 DA BD AD
result: 3
Little Endian
ByteArrayUtils.LittleEndianEffectiveLength returns an int representing the byte length of the given input disregarding the 0x00 bytes at the end of the array.
public static int ByteArrayUtils.LittleEndianEffectiveLength(this byte[] input)
public static void LittleEndianEffectiveLengthExample()
{
// Setup
var input = new byte[] { 0xDA, 0xB0, 0x00, 0x00, 0x00, 0x00 };
// Act
var result = input.LittleEndianEffectiveLength();
// Conclusion
Console.WriteLine("LittleEndianEffectiveLength Example");
Console.WriteLine($"input:\t{input.ToString("H")}");
Console.WriteLine($"result:\t{result}");
Console.WriteLine(string.Empty);
}
LittleEndianEffectiveLength Example
input: DA B0 00 00 00 00
result: 2