0

I am trying to do this operation:

Sending a byte[] inputBuffer into a function

In that function I need to parse the array for e.g.

inputBuffer = {1A,2B,3C,4D,5F,6E,2A,3B,4C,6D}

I want to read 1A ,2B and 3C first

Then 4D,5F,6E,2A,3B,4C,6D second

I need to use these two subarrays to do some operations (Hash and Encrypt them)

Then I need to put them back into a new array with some extra bytes attach to it (length bytes)

I don't want to use blockcopy since I don't think it's really efficient

jy824212
  • 37
  • 8
  • 4
    have a look at `Span` – Daniel A. White Mar 13 '21 at 16:14
  • How long are your arrays? Can they have hundreds or thousands of elements? – PMF Mar 13 '21 at 16:26
  • you can use `oldArray[1..^1]` for Ignore first and last item of array. – Amirhossein Azhdari Mar 13 '21 at 16:26
  • @DanielA.White but how I can add them back to a new big array after – jy824212 Mar 13 '21 at 16:34
  • @PMF less than 100 bytes – jy824212 Mar 13 '21 at 16:35
  • 1
    @jy824212 its a view of the array, not a copy – Daniel A. White Mar 13 '21 at 16:39
  • I don't understand why you aren't just indexing the array. If you want to read bytes 2, 3 and 4 that's `someArray[2]`, `someArray[3]` and `someArray[4]` (if your numbering starts at 0, subtract one if it doesn't – Caius Jard Mar 13 '21 at 17:19
  • 1
    @jy824212 What makes you think that `Buffer.BlockCopy` is not the most efficient way to copy bytes? Now, if you just need to use those bytes without copying them anywhere, then use `Span` as suggested earlier. – Kit Mar 13 '21 at 17:20
  • @Kit I need to copy them out then do some operations then copy back into a new byte array, is Span still applicable? – jy824212 Mar 13 '21 at 17:40
  • What are your precise performance requirements here, and how close is the simplest code (e.g. using `Buffer.BlockCopy`) to achieving those requirements? If you don't have *concrete* requirements, or you aren't measuring the current performance, then taking those first steps is much more important than any specific suggestion. – Jon Skeet Mar 13 '21 at 19:53

3 Answers3

1

Let's break down your steps, and try to make each step as efficient as possible (by efficient, we want to not copy data if we don't have to, and when we do, we copy it using the most efficient method).

Sending a byte[] inputBuffer into a function

That part is simply passing in the array, which does not do a copy of the array, just a copy of the array reference (and that won't even happen if .NET has inlined the method call).

void DoStuff(byte[] inputBuffer)
{
    ...

In that function I need to parse the array for e.g.

inputBuffer = {1A,2B,3C,4D,5F,6E,2A,3B,4C,6D}

Let's say your inputBuffer looks like {00, 00, 1A,2B,3C,4D,5F,6E,2A,3B,4C,6D, 00, 00} then your parse/find would get the start and end index of the above array of bytes as 2 and 11, for a length of 10.

I want to read 1A ,2B and 3C first

Then 4D,5F,6E,2A,3B,4C,6D second

You can slice the array twice, creating two ReadOnlySpan<byte>, which use the underlying memory directly (no copying). (see this reference for more info).

var firstSlice = new ReadOnlySpan<byte>(inputBuffer, 2, 3); // index 2, for 3 bytes
var secondSlice = new ReadOnlySpan<byte> inputBuffer, 5, 7); // index 5 for 7 bytes

I need to use these two subarrays to do some operations (Hash and Encrypt them)

Do your hashing and encryption on these spans; this doesn't change the spans, so you're still using the in-place memory. If your hash/encryption routines can accept arrays with offsets, then you won't even need to give them the span treatment.

Then I need to put them back into a new array with some extra bytes attach to it (length bytes)

I don't want to use blockcopy since I don't think it's really efficient

You simply need to create the array,

var result = new byte[10 + size-of-extra-data];

and copy the two slices and the extra data into that array using either Buffer.BlockCopy, Array.Copy a loop over bytes, or an unrolled loop. (Note: if it's thread-safe, and you're working with same-size data each time, you can avoid the above allocation by reusing a preallocated result array).

Anyway, for your specific case (and by the way, benchmark it if it's in critical code), it seems like a loop or unrolled loop (which .NET will probably unroll anyway when optimizing the code) is your best bet.

// this could be a loop, but I'm unrolling it here...
result[0] = firstSlice[0];
result[1] = firstSlice[1];
result[2] = firstSlice[2];
result[3] = secondSlice[0];
result[4] = secondSlice[1];
result[5] = secondSlice[2];
result[6] = secondSlice[3];
result[7] = secondSlice[4];
result[8] = secondSlice[5];
result[9] = secondSlice[6];
... // copy extra data from wherever it comes from

With your next best option being Array.Copy

// just one copy since we are using the original source; it would be two if we used the spans
Array.Copy(inputBuffer, 2, result, 0, 10);
// now copy in the extra data
Array.Copy(extraData, 0, result, 10, extraData.Length);

or, with a similar signature, the Buffer.BlockCopy:

// just one copy since we are using the original source; it would be two if we used the spans
Buffer.BlockCopy(inputBuffer, 2, result, 0, 10);
// now copy in the extra data
Buffer.BlockCopy(extraData, 0, result, 10, extraData.Length);

The reference for the above assertions of efficiency comes from this highly upvoted SO answer.

Kit
  • 20,354
  • 4
  • 60
  • 103
  • thanks for the reply, I have one comment regarding var firstSlice = new ReadOnlySpan(inputBuffer, 2, 3); what is the difference between this method and inputBuffer.Skip(x).Take(y).ToArray(); 2. To reallocate this buffer can I use Array.Resize(ref inputBuffer, inputBuffer.Length + length I want to add); – jy824212 Mar 14 '21 at 15:14
  • There are several differences. 1) `Skip()` means you'd have to scan your input buffer, which doesn't make sense if your buffer has random access (if your buffer is a stream... that would be OK), but from your question you imply it's an array. 2) `ToArray()` and `Resize()` both make allocations. It's hard to know for sure if I've fully helped you as it seems like you might be doing much more than this question indicates... – Kit Mar 14 '21 at 15:47
  • Thank your for your feedback. This is really helpful – jy824212 Mar 14 '21 at 22:03
  • is it also possible to change the value of the inputBuffer at a specific index let's say 2-4 using Span as well without copying ? – jy824212 Mar 15 '21 at 13:12
  • @jy824212 Yes you can. – Kit Mar 16 '21 at 13:15
0

You don't need BlockCopy here, just do it in one loop.

List<int> inputValues = new List<int>{1, 2, 3, 4, 5};
List<int> indicesToPick = new List<int>{3, 4};

var result = new int[indicesToPick.Count];
int idx = 0;
foreach(var e in indicesToPick)
{
    result[idx++] = inputValues[e];
}

Since you're picking individual elements only, you can't use BlockCopy (that can be used for copying a large section of an array). The one allocation is required, as it will contain your result (unless it would be ok to overwrite the input).

PMF
  • 14,535
  • 3
  • 23
  • 49
0

You may use Array.Copy() instead of Buffer.BlockCopy() and hope that following approach would work for you:

byte[5] oldArray = {1,2,3,4,5};
byte[3] newArray = new byte[3];
Array.Copy(oldArray, 1, newArray, 0, 3);
Salahuddin Ahmed
  • 4,854
  • 4
  • 14
  • 35