2

Given byte[] buffer I'd like to create a new array from a sub-array. A method signature might look like byte[] SubArray(byte[] buffer, int start, int length) but I'd probably rather just have a neat 1-liner than a method.

Here's a nice neat LINQ version but it feels like this mightn't be very efficient. Is there a standard library method that does this or would I have to create a new array then copy to it?

        var subArray = buffer.Skip(start).Take(length).ToArray();
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • 2
    [`ArraySegment(T[], int, int)`](https://learn.microsoft.com/en-us/dotnet/api/system.arraysegment-1.-ctor?view=netcore-3.1) ? – Fildor Jul 08 '20 at 13:40
  • https://stackoverflow.com/questions/943635 – Robert Harvey Jul 08 '20 at 13:41
  • 6
    Why not use the new `AsSpan(start, length).ToArray()` method from `System.Memory` package? It should avoid extra allocations as well... – haim770 Jul 08 '20 at 13:43
  • Is having a one-liner the main requirement? – Emond Jul 08 '20 at 13:45
  • Do you _really_ need an array, or is a sequence/IEnumerable good enough? – Joel Coehoorn Jul 08 '20 at 13:50
  • 1
    @haim770 if you're happy to work with spans, then *work with spans* (or memories) - don't call `ToArray()` at the end – Marc Gravell Jul 08 '20 at 14:05
  • @MarcGravell, I assumed the SO isn't going to change the method signatures for the sake of this purpose. Yes, it does create a new array, but it seems more efficient than its LINQ counterpart. – haim770 Jul 08 '20 at 14:51
  • Many APIs including .Net require arrays as parameters. Aside from that, spans and segments, etc, as a _view_ onto an underlying array work great - as long as you can be sure the underlying array isn't going to change. Sometimes, that's not the case. – Mr. Boy Jul 08 '20 at 15:05
  • 1
    Yes, sometimes you need an array to appease older APIs. But... even then, wait as long as possible to call ToArray(), and start using IEnumerable internally as much as you can. – Joel Coehoorn Jul 13 '20 at 22:48

2 Answers2

3

There is no efficient way that involves creating a new array, however, there are other ways you can look at a portion of an array:

  • Memory<byte> / ReadOnlyMemory<byte> - effectively this is a "span provider"
  • Span<byte> / ReadOnlySpan<byte> - abstract view over any kind of raw memory (including arrays, interior pointers, and unmanaged memory), but can only be held on the stack - if you need to store it, you need the "memory", not the "span")
  • ArraySegment<byte> - explicitly just describes a vector/offset/count

All of the above have constructors that take a byte[] and an offset/count pair.

None of these require allocating / copying.

If you really really want a byte[], then:

var newArray = new byte[length];
Buffer.BlockCopy(oldArray, start, newArray, 0, length);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2
  • ~3000 Milliseconds (for running 10000000 times and after trying several times):
subArray = buffer.Skip(2).Take(3).ToArray();
  • ~2600 Milliseconds:
subArray = buffer.ToList().GetRange(2, 3).ToArray();
  • ~2300 Milliseconds:
subArray = Enumerable.Range(2, 3).Select(x => buffer[x]).ToArray();
  • ~800 Milliseconds:
subArray = buffer[2..5];

This is a new feature in c# 8. I couldn't find any specific name but you'll find it by googling something like "c# range feature" or "c# range operator" or "c# binary infix operator with two expressions" or "Range Syntax c#". See Docs.

  • ~650 Milliseconds:
subArray = new byte[3];
Array.ConstrainedCopy(buffer, 2, subArray, 0, 3);
  • ~650 Milliseconds:
subArray = new byte[3];
Array.Copy(buffer, 2, subArray, 0, 3);
  • 1
    Is `buffer[2..5]` a new feature in latest C#? What is this feature called so I can research it (maybe edit that into the answer) – Mr. Boy Jul 09 '20 at 09:42
  • 1
    Yes It's a new feature in c# 8. I couldn't find any specific name but you'll find it by googling something like "c# range feature" or "c# range operator" or "c# binary infix operator with two expressions" or "Range Syntax c#". See [Range Struct](https://learn.microsoft.com/en-us/dotnet/api/system.range?view=netcore-3.1). –  Jul 09 '20 at 13:04