1

Currently I code client-server junk and deal a lot with C++ structs passed over network. I know about ways provided here Reading a C/C++ data structure in C# from a byte array, but they all about making a copy.

I want to have something like that:

struct/*or class*/ SomeStruct
{
    public uint F1;
    public uint F2;
    public uint F3;
}

Later in my code I want to have something like that:

byte[] Data; //16 bytes that I got from network
SomeStruct PartOfDataAsSomeStruct { get { return /*make SomeStruct instance based on this.Data starting from index 4, without copying it. So when I do PartOfDataAsSomeStruct.F1 = 132465; it also changes bytes 4, 5, 6 and 7 in this.Data.*/; } }

If this is possible, please, tell how?

2 Answers 2

5

Like so?

byte[] data = new byte[16];
// 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct typed = ref Unsafe.As<byte, SomeStruct>(ref data[4]);
typed.F1 = 42;
typed.F2 = 3;
typed.F3 = 9;
// 00-00-00-00-2A-00-00-00-03-00-00-00-09-00-00-00
Console.WriteLine(BitConverter.ToString(data));

This coerces the data from the middle of the byte-array using a ref-local that is an "interior managed pointer" to the data. Zero copies.

If you need multiple items (like how a vector would work), you can do the same thing with spans and MemoryMarshal.Cast

Note that it uses CPU-endian rules for the elements - little endian in my case.

For spans:

byte[] data = new byte[256];
// create a span of some of it
var span = new Span<byte>(data, 4, 128);
// now coerce the span
var typed = MemoryMarshal.Cast<byte, SomeStruct>(span);
Console.WriteLine(typed.Length); // 10 of them fit
typed[3].F1 = 3; // etc
Sign up to request clarification or add additional context in comments.

3 Comments

@Kosmos you can also use unsafe and unmanaged pointers if you're using down-level compiler etc, but: the above is a lot easier!
Whooohooo, it's working! I wanted to avoid unsafe thing... at least until I run into problems that I can't solve without it. This time problem is solved.
@Kosmos just note that even though you didn't use unsafe, it is still "unsafe", because you might have got the size wrong (not enough bytes in the array), meaning you're overwriting someone else's memory. So just: don't get the size wrong :)
0

Thank you for the correction, Marc Gravell. And thank you for the example.

Here is a way using Class and Bitwise Operators, without pointers, to do the samething:

    class SomeClass
    {
        public byte[] Data;

        public SomeClass()
        {
            Data = new byte[16];
        }

            public uint F1
        {
            get
            {
                uint ret = (uint)(Data[4] << 24 | Data[5] << 16 | Data[6] << 8 | Data[7]);
                return ret;
            }
            set
            {
                Data[4] = (byte)(value >> 24);
                Data[5] = (byte)(value >> 16);
                Data[6] = (byte)(value >> 8);
                Data[7] = (byte)value;
            }
        }        
    }

Testing:

            SomeClass sc = new SomeClass();
            sc.F1 = 0b_00000001_00000010_00000011_00000100;
            Console.WriteLine(sc.Data[3].ToString() + " " + sc.Data[4].ToString() + " " + sc.Data[5].ToString() + " " + sc.Data[6].ToString());
            Console.WriteLine(sc.F1.ToString());
//Output:
//1 2 3 4
//16909060

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.