5

If someone passes me an ArraySegment<byte> foo and this segment points into a larger buffer, what's the idiomatic way to copy this segment into a fresh new byte[] ?

I tried accessing at foo.Array but this seems to point to the beginning of the larger buffer, not the beginning of the segment.

e.g. the larger buffer could be "blahfoobar" and the ArraySegment points to "foo". I want to get a byte[] with just "foo".

I'm sure it's dead simple, but coming from C++, I can't figure the lingo used in c#.

Sudharshann D
  • 935
  • 9
  • 9
Jeffrey
  • 11,063
  • 1
  • 21
  • 42
  • I see ArraySegment implements the ICollection, so you have to have the ToArray method – PiGi78 Dec 08 '20 at 21:55
  • @PiGi78 that's only true with .Net 4.5 and above and .Net Core and it implements heaps more iterative types than ICollection: https://stackoverflow.com/a/7907024/495455 - OP looks like you need iterate through the ArraySegments converting ToArray and joining the arrays together. – Jeremy Thompson Dec 08 '20 at 21:57

2 Answers2

4

Creating a new array would be entirely to miss the point of the API, which is to represent a pre-existing segment. In more recent .NET version, Span<T> and ReadOnlySpan<T> would be better choices - they allow you to create an abstraction over contiguous memory without needing the consumer to worry about the Offset etc, as that can be imposed externally. There are constructors on Span<T> and ReadOnlySpan<T> that allow you to deal with aspects so that the consumer never needs to know about them, with the knowledge that on recent runtimes: the JIT will elide bounds checks on spans.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • awesome. I'll look at my version see if span would fit. I'm familiar with C++ gsl::span and like them – Jeffrey Dec 09 '20 at 02:06
  • 1
    @Jeffrey note: if you need to store the range as a field on the heap, you'll need [ReadOnly]Memory-T; memory is basically "I can give you a span when you need it" - it could just as easily have been called "SpanSource" or similar. You can create a memory over an array segment (and a range of other things, including custom allocators) – Marc Gravell Dec 09 '20 at 02:13
1
using System;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {
        private static readonly byte[] _initArray = Encoding.ASCII.GetBytes("blahfoobar");

        //private static byte[] ArraySegmentToArray(ArraySegment<byte> segment)
        //{
        //    var result = new byte[segment.Count];

        //    for (int i = 0; i < segment.Count; i++)
        //    {
        //        result[i] = segment.Array[i + segment.Offset];
        //    }

        //    return result;
        //}

        private static byte[] ArraySegmentToArray(ArraySegment<byte> segment) =>
            segment.ToArray();

        static void Main(string[] args)
        {
            var segFoo = new ArraySegment<byte>(_initArray, 4, 3);

            var test = ArraySegmentToArray(segFoo);
        }
    }
}

But of course it is bad practice. Because if you turn segment to array you allocate memory, and if you use ArraySegment stuff as pointers , you don't allocate memory, actually, that is the main idea of using ArraySegment, because if not the array could be provided as parameters :) P.S. Commented code just for understanding the idea.

Ivan Khorin
  • 827
  • 1
  • 5
  • 17
  • That helps. I like the fact that it shows `segment.Offset` being explicitly needed from `.Array[]`. But there has to be a way to directly get an offset'ed array, no? – Jeffrey Dec 08 '20 at 22:27
  • What's the runtime cost of `ToArray` ? Please confirm it's returning the existing memory, not making a copy – Jeffrey Dec 08 '20 at 22:30
  • 2
    @Jeffrey OP requested "into a fresh new byte[] ?" which indeed this code does. Not sure why you think it should do something else... How you can create new array without newly allocated space for it? It does not work that way even in C++ which you claim to know... – Alexei Levenkov Dec 08 '20 at 22:31
  • Runtime cost is allocation = bytes x number of requests + code execution:) If you use ArraySegment properly then only code execution. – Ivan Khorin Dec 08 '20 at 22:40
  • @AlexeiLevenkov what OP (and myself) meant by "fresh new byte[]" was something that behave like an array at the offset position. As in `char* fresh = original + offset;` this doesn't allocate memory. `ToArray()` works for me, because the rest of the code expect a byte[]. It's unfortunate that it allocate a copy. – Jeffrey Dec 09 '20 at 02:12