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?

Kosmo零
  • 4,001
  • 9
  • 45
  • 88

2 Answers2

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
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @Kosmos you can also use `unsafe` and unmanaged pointers if you're using down-level compiler etc, but: the above is a lot easier! – Marc Gravell Jul 11 '20 at 17:25
  • 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. – Kosmo零 Jul 11 '20 at 17:28
  • @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 :) – Marc Gravell Jul 11 '20 at 17:49
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
Raphael
  • 114
  • 6