Byte access to structure data

I need to read a binary file of records which can be defined by a structure. For example…
struct Record1Struct {
int dataInt1;
int dataInt2’
float dataFloat1;
char dataText1[10];
};

In C/C++ one could union this structure with a character array to get byte access. Read a record into the memory using the byte array and then access the structure items by name after.

union Record1Union{
struct Record1Struct fields;
char bytes[sizeof(Record1Struct)];
};

Record1Union dataRecord;

read(datafile,dataRecord,sizeof(Record1Struct));
if (dataRecord.fields.dataInt2==0) do something…

(note the above is sudo code)

Another option would be to create a buffer to hold the file bytes, then cast the buffer to a pointer of the structure type to access the data.

How would I do similar on Fez? Fez has no union, I do not how how to create a basic byte or char storage rather than an array object, and there does not seem to be a malloc type method.

I would prefer to allocate a buffer of bytes to read data into, then cast this buffer to a pointer of the structure type, use indirect access via the structure pointer to access the data.

I saw some mention here in the forum about a util function that might convert an object into a byte array, as a means around a lack of serialization support but I do not find a util function that seems to support this either.

David, you want to Marshal the pointer over however this isn’t available on netMF :frowning:

This is it on full size .net: Marshal.PtrToStructure Method (System.Runtime.InteropServices) | Microsoft Learn

Marshalling uses external unmanaged code to do what it does, so there is no chance of implementing that in netmf.

However, you can use BitConverter to rebuild the struct manually…

Unfortunately even BitConverter isnt provided on netmf. BitConverter Class (System) | Microsoft Learn

But, it is managed code. So you can decompile the System.BitConverter class to get it’s code. Once re-written to get rid of the ThrowHelper mentions…


static Program()
        {
            IsLittleEndian = true;
        }

        public static readonly bool IsLittleEndian;

        public static unsafe int ToInt32(byte[] value, int startIndex)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value", "cannot be null");
            }
            if (startIndex >= value.Length)
            {
                throw new ArgumentOutOfRangeException("startIndex", "Argument is out of range");
            }
            if (startIndex > (value.Length - 4))
            {
                throw new ArgumentException("value", "Arg_ArrayPlusOffTooSmall");
            }
            fixed (byte* numRef = &(value[startIndex]))
            {
                if ((startIndex % 4) == 0)
                {
                    return *(((int*)numRef));
                }
                if (IsLittleEndian)
                {
                    return (((numRef[0] | (numRef[1] << 8)) | (numRef[2] << 0x10)) | (numRef[3] << 0x18));
                }
                return ((((numRef[0] << 0x18) | (numRef[1] << 0x10)) | (numRef[2] << 8)) | numRef[3]);
            }
        }

Binary serialization may help you!

http://www.netmf.com/Discussion/Forums/SingleForum/SingleThread.aspx?mode=singleThread&thread=5156aae0-1fc9-4d1b-88cd-30ad224037bb

Unfortunately no unsafe code is allowed on NETMF, here is my implementation of the BitConverter class (we sure could use that Code Exchange now :stuck_out_tongue: ).

public static class BitConverter
    {
        public static byte[] GetBytes(byte val)
        {
            return new byte[1] { val };
        }
        public static byte[] GetBytes(char val)
        {
            return new byte[2] { (byte)(val & 0xFF), (byte)((val >> 8) & 0xFF) };
        }
        public static byte[] GetBytes(short val)
        {
            return new byte[2] { (byte)(val & 0xFF), (byte)((val >> 8) & 0xFF) };
        }
        public static byte[] GetBytes(ushort val)
        {
            return new byte[2] { (byte)(val & 0xFF), (byte)((val >> 8) & 0xFF) };
        }
        public static byte[] GetBytes(int val)
        {
            return new byte[4] { 
                    (byte)(val & 0xFF), 
                    (byte)((val >> 8) & 0xFF), 
                    (byte)((val >> 16) & 0xFF), 
                    (byte)((val >> 24) & 0xFF) };
        }
        public static byte[] GetBytes(uint val)
        {
            return new byte[4] { 
                    (byte)(val & 0xFF), 
                    (byte)((val >> 8) & 0xFF), 
                    (byte)((val >> 16) & 0xFF), 
                    (byte)((val >> 24) & 0xFF) };
        }
        public static byte[] GetBytes(long val)
        {
            return new byte[8] { 
                    (byte)(val & 0xFF), 
                    (byte)((val >> 8) & 0xFF), 
                    (byte)((val >> 16) & 0xFF), 
                    (byte)((val >> 24) & 0xFF),
                    (byte)((val >> 32) & 0xFF),
                    (byte)((val >> 40) & 0xFF),
                    (byte)((val >> 48) & 0xFF),
                    (byte)((val >> 56) & 0xFF)};
        }
        public static byte[] GetBytes(ulong val)
        {
            return new byte[8] { 
                    (byte)(val & 0xFF), 
                    (byte)((val >> 8) & 0xFF), 
                    (byte)((val >> 16) & 0xFF), 
                    (byte)((val >> 24) & 0xFF),
                    (byte)((val >> 32) & 0xFF),
                    (byte)((val >> 40) & 0xFF),
                    (byte)((val >> 48) & 0xFF),
                    (byte)((val >> 56) & 0xFF)};
        }

        public static byte[] GetBytes(float val)
        {
            byte[] buffer = new byte[4];
            GHIElectronics.NETMF.System.Util.InsertValueIntoArray(val, buffer, 0);
            return buffer;
        }
        public static byte[] GetBytes(double val)
        {
            byte[] buffer = new byte[4];
            GHIElectronics.NETMF.System.Util.InsertValueIntoArray((float)val, buffer, 0);
            return buffer;
        }

        public static char ToChar(byte[] val, int index)
        {
            return (char)(val[0 + index] << 0 | val[1 + index] << 8);
        }
        public static short ToInt16(byte[] val, int index)
        {
            return (short)(
                val[0 + index] << 0 |
                val[1 + index] << 8);
        }
        public static int ToInt32(byte[] val, int index)
        {
            return (
                val[0 + index] << 0 |
                val[1 + index] << 8 |
                val[2 + index] << 16 |
                val[3 + index] << 24);
        }
        public static long ToInt64(byte[] val, int index)
        {
            return (
                val[0 + index] << 0 |
                val[1 + index] << 8 |
                val[2 + index] << 16 |
                val[3 + index] << 24 |
                val[4 + index] << 32 |
                val[5 + index] << 40 |
                val[6 + index] << 48 |
                val[7 + index] << 56);
        }
        public static ushort ToUInt16(byte[] val, int index)
        {
            return (ushort)(
                val[0 + index] << 0 |
                val[1 + index] << 8);
        }
        public static uint ToUInt32(byte[] val,int index)
        {
            return (uint)(
                val[0 + index] << 0 |
                val[1 + index] << 8 |
                val[2 + index] << 16 |
                val[3 + index] << 24);
        }
        public static ulong ToUInt64(byte[] val,int index)
        {
            return (ulong)(
                val[0 + index] << 0 |
                val[1 + index] << 8 |
                val[2 + index] << 16 |
                val[3 + index] << 24 |
                val[4 + index] << 32 |
                val[5 + index] << 40 |
                val[6 + index] << 48 |
                val[7 + index] << 56);
        }

        public static float ToSingle(byte[] val,int index)
        {
            float ret;
            GHIElectronics.NETMF.System.Util.ExtractValueFromArray(out ret, val, index);
            return ret;
        }
        public static double ToDouble(byte[] val,int index)
        {
            float ret;
            GHIElectronics.NETMF.System.Util.ExtractValueFromArray(out ret, val, index);
            return (double)ret;
        }

        public static string ToString(byte[] val)
        {
            return new string(System.Text.Encoding.UTF8.GetChars(val));
        }
    }

Ah thanks Ravenheart, i don’t have my FEZ yet so i wasn’t sure if unsafe was allowed :wink:

You do not have to have one, use the emulator :slight_smile:

You will need the methods in ghi’s until class and Microsoft utilities class to read int and float

Netmf is made to be very rugged so unsafe code is not allowed. Serialization is too slow and bulky so we do not include on usbizi

I was at work and don’t have NetMF on there, only .net 4.0/VS 2010 Ultimate :wink:

Wow, so its freeking hard to do binary structure work with a Fez. This is such a trivial thing to do with C/C++.

Yes correct, this is how “managed” C# code works. It takes away some of the very dangerous features in C++. Pointers are great but any little mistake and the whole system goes down. This is a feature not a limitation on NETMF and C#. There is nothing you can do to crash the system :slight_smile: I find this very important.

Remember how this is an embedded system, your PC, with its huge operating system, can recover from a bad pointer but an embedded system will completely crash.

I found this limiting at the beginning since I am used to pointers but then after a short time I started to think “managed” instead of “native” and all flows just fine for me

David, you just need to adjust your thinking to the managed world where the CLR is doing all the memory stuff for you. If you’re dealing with binary data it can be a bit of a pain at first - but with a bitconverter class it’s not so bad - and no buffer overruns/affecting other memory segments :smiley: