Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable<T> ToArray usage - Is it a copy or a pointer?

I am parsing an arbitrary length byte array that is going to be passed around to a few different layers of parsing. Each parser creates a Header and a Packet payload just like any ordinary encapsulation.

My problem lies in how the encapsulation holds its packet byte array payload. Say I have a 100 byte array with three levels of encapsulation. Three packet objects will be created and I want to set the payload of these packets to the corresponding position in the byte array of the packet.

For example, let's say the payload size is 20 for all levels, then imagine it has a public byte[] Payload on each object. However, the problem is that this byte[] Payload is a copy of the original 100 bytes, so I'm going to end up with 160 bytes in memory instead of 100.

If it were in C++, I could just easily use a pointer - however, I'm writing this in C#.

So I created the following class:

public class PayloadSegment<T> : IEnumerable<T>
{
    public readonly T[] Array;
    public readonly int Offset;
    public readonly int Count;

    public PayloadSegment(T[] array, int offset, int count)
    {
        this.Array = array;
        this.Offset = offset;
        this.Count = count;
    }

    public T this[int index]
    {
        get
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                return Array[Offset + index];
        }
        set
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                Array[Offset + index] = value;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = Offset; i < Offset + Count; i++)
            yield return Array[i];
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        IEnumerator<T> enumerator = this.GetEnumerator();
        while (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
    }
}

This way I can simply reference a position inside the original byte array but use positional indexing. However, if I do something like:

 PayloadSegment<byte> something = new PayloadSegment<byte>(someArray, 5, 10);
 byte[] somethingArray = something.ToArray();

Will the somethingArray be a copy of the bytes, or a reference to the original PayloadSegment (which in turn is a reference to the original byte array)?

EDIT: Actually after rethinking this, can't I simply use a new MemoryStream(array, offset, length)?

like image 975
Daniel Avatar asked Apr 21 '10 04:04

Daniel


1 Answers

The documentation for the Enumerable.ToArray extension method doesn't specifically mention what it does when it's passed a sequence that happens to already be an array. But a simple check with .NET Reflector reveals that it does indeed create a copy of the array.

It is worth noting however that when given a sequence that implements ICollection<T> (which Array does) the copy can be done much faster because the number of elements is known up front so it does not have to do dynamic resizing of the buffer such as List<T> does.

like image 51
Josh Avatar answered Oct 07 '22 22:10

Josh