haxxoromer
haxxoromer

Reputation: 377

Generic type pointers? and casting byte array to given type of generic?

Okay the basic idea what I'm trying to do is, converting byte array to something like short or int etc. etc.

A simple example might be:

        unsafe
        {
            fixed (byte* byteArray = new byte[5] { 255, 255, 255, 126, 34 })
            {
                short shortSingle = *(short*)byteArray;
                MessageBox.Show((shortSingle).ToString()); // works fine output is -1
            }
        }

Okay, so what I'm really trying to do is, make an extension to Stream class; extended read and write methods. I need help at the following code:

unsafe public static T Read<T>(this Stream stream)
        {
            int bytesToRead = sizeof(T); // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
            byte[] buffer = new byte[bytesToRead];
            if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
            {
                throw new Exception();
            }
            fixed (byte* byteArray = buffer)
            {
                T typeSingle = *(T*)byteArray; // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
                return typeSingle;
            }
        }

        unsafe public static T[] Read<T>(this Stream stream, int count)
        {
             // haven't figured out it yet. This is where I read and return T arrays
        }

I feel like I have to use pointers for speed because I will be working on writting and reading data from streams like NetworkStream classes. Thanks for your help!

EDIT:

And while I try to figure out how may I return T arrays, I've faced with this problem:

unsafe
        {
            fixed (byte* byteArray = new byte[5] { 0, 0, 255, 255, 34 })
            {
                short* shortArray = (short*)byteArray;
                MessageBox.Show((shortArray[0]).ToString()); // works fine output is 0
                MessageBox.Show((shortArray[1]).ToString()); // works fine output is -1

                short[] managedShortArray = new short[2];
                managedShortArray = shortArray; // The problem is, How may I convert pointer to a managed short array? ERROR: Cannot implicitly convert type 'short*' to 'short[]'
            }
        }

THE SUMMARY: I have to convert from byte array to given type of T OR to given type of T array with given length

Upvotes: 9

Views: 3113

Answers (2)

charliefox2
charliefox2

Reputation: 357

Edit: unmanaged constraint added to C# 7.3.

Jumping in a bit late on this one, but with C# 7.3 comes the addition of the unmanaged type constraint.

With the unmanaged type constraint, you can use generic pointers (T*) among other things, provided the type passed in is unmanaged.

I tested the generic method you provided, and it does work now. Additionally, you can extend it to return an array like so:

public static unsafe T[] ReadAsArray<T>(this Stream stream) where T : unmanaged
{
    var length = stream.Length;
    var returnArray = new T[length];

    for (var i = 0; i < length; i++)
    {
        int bytesToRead = sizeof(T); // no longer throws error
        byte[] buffer = new byte[bytesToRead];
        if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
        {
            throw new Exception();
        }
        fixed (byte* byteArray = buffer)
        {
            T typeSingle = *(T*)byteArray; // no longer throws error
            returnArray[i] = typeSingle;
        }
    }

    return returnArray;
}

You can call it with the following code, which will print the contents of a file:

using (var sourceStream = File.Open(filename, FileMode.Open))
{
    var byteArray = sourceStream.ReadAsArray<byte>();
    Console.Write(new string(byteArray.Select(b => (char)b).ToArray()));
}

Upvotes: 1

max
max

Reputation: 34437

You can't make this function generic because of pointer restrictions in C#. Any of the following types may be a pointer type:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Any enum type.
  • Any pointer type.
  • Any user-defined struct type that contains fields of unmanaged types only.

But you can't set a restriction on T where T <can be pointer type>. where T : struct is very close, but not enough, because user-defined structs can contain fields of reference types.

There is a workaround - System.Runtime.InteropServices.Marshal.PtrToStructure() (it simply throws an exception if it is unable to work with specified object type), but it would also kill any achieved performance improvements.

I think the only way to do this is to create non-generic functions for all desired types.

Upvotes: 5

Related Questions