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:

Create Byte Array Example
 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]:

Trim Big-Endian Leading Zero Bytes
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)
Trim Little-Endian Leading Zero Bytes
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)
Pad Big-Endian Most Significant Bytes Example
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)
Pad Little-Endian Most Significant Bytes Example
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)
Append Bytes Example
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)
Append Shortest Example
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)
Prepend Bytes Example
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)
Prepend Shortest Example
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)
Reverse Bytes Example
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)
Big-Endian Effective Length Example
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)
Little-Endian Effective Length Example
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