tags:

views:

514

answers:

2

Hello,

I have a Read generic function that does its job very well. It reads from a buffer of bytes and returns the specific type

public static T Read<T>()
{
    // An T[] would be a reference type, and a lot easier to work with.
    T[] t = new T[1];

    // Marshal.SizeOf will fail with types of unknown size. Try and see...
    int s = Marshal.SizeOf(typeof(T));
    if (index + s > size)
        // Should throw something more specific.
        throw new Exception("Error 101 Celebrity");

    // Grab a handle of the array we just created, pin it to avoid the gc
    // from moving it, then copy bytes from our stream into the address
    // of our array.
    GCHandle handle = GCHandle.Alloc(t, GCHandleType.Pinned);
    Marshal.Copy(dataRead, index, handle.AddrOfPinnedObject(), s);

    index += s;

    // Return the first (and only) element in the array.
    return t[0];
}

Problem: How to do the Write function?

public static T Write<T>()
{
    // An T[] would be a reference type, and a lot easier to work with.
    T[] t = new T[1];

    // Marshal.SizeOf will fail with types of unknown size. Try and see...
    int s = Marshal.SizeOf(typeof(T));
    if (index + s > size)
        // Should throw something more specific.
        throw new Exception("Error 101 Celebrity");

    // Grab a handle of the array we just created, pin it to avoid the gc
    // from moving it, then copy bytes from our stream into the address
    // of our array.
    GCHandle handle = GCHandle.Alloc(dataWrite, GCHandleType.Pinned);
    Marshal.Copy(t, index, handle.AddrOfPinnedObject(), s); // ?? Problem

    index += s;
}

"t" should be byte[] array. How do I accomplish that?

+1  A: 

It's a lot of code to put on SO, but here y'go. There are a few overloads for Marshal.Copy that suit your needs.

class Program
{
    static void Main(string[] args)
    {
        TestStruct test = new TestStruct();
        test.Bar = 100;
        test.Foo = 200;

        using (MemoryStream ms = new MemoryStream())
        {
            using (ObjectStream os = new ObjectStream(ms, false))
            {
                os.Write(test);
            }
            ms.Seek(0, SeekOrigin.Begin);
            Console.WriteLine(BitConverter.ToString(ms.ToArray()));
            using (ObjectStream os = new ObjectStream(ms, false))
            {
                TestStruct result = os.Read<TestStruct>();
                Console.WriteLine(result.Bar);
                Console.WriteLine(result.Foo);
            }
        }

        Console.ReadLine();
    }
}

struct TestStruct
{
    public int Foo;
    public int Bar;
}

class ObjectStream : Stream
{
    private Stream _backing;
    private bool _ownsStream = true;

    public ObjectStream(Stream source)
        :this(source, true)
    {
    }

    public ObjectStream(Stream source, bool ownsStream)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        _backing = source;
        _ownsStream = ownsStream;
    }

    public override bool CanRead
    {
        get
        {
            return _backing.CanRead;
        }
    }

    public override bool CanSeek
    {
        get
        {
            return _backing.CanSeek;
        }
    }

    public override bool CanWrite
    {
        get
        {
            return _backing.CanWrite;
        }
    }

    public override void Flush()
    {
        _backing.Flush();
    }

    public override long Length
    {
        get
        {
            return _backing.Length;
        }
    }

    public override long Position
    {
        get
        {
            return _backing.Position;
        }
        set
        {
            _backing.Position = value;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _backing.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _backing.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _backing.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _backing.Write(buffer, offset, count);
    }

    /// <summary>
    /// Writes a value type to the stream.
    /// </summary>
    /// <typeparam name="T">The type of value.</typeparam>
    /// <param name="value">The value.</param>
    /// <returns>The number of bytes written to the stream.</returns>
    public int Write<T>(T value)
    {
        // An T[] would be a reference type, and alot easier to work with.
        T[] t = new T[1];
        t[0] = value;

        // Marshal.SizeOf will fail with types of unknown size. Try and see...
        int s = Marshal.SizeOf(typeof(T));
        // Create a temp array.
        byte[] target = new byte[s];

        // Grab a handle of the array we just created, pin it to avoid the gc
        // from moving it, then copy bytes from our stream into the address
        // of our array.
        GCHandle handle = GCHandle.Alloc(t, GCHandleType.Pinned);
        Marshal.Copy(handle.AddrOfPinnedObject(), target, 0, s); // ?? Problem

        // Write to the stream.
        Write(target, 0, s);

        return s;
    }

    /// <summary>
    /// Reads a value type from the stream.
    /// </summary>
    /// <typeparam name="T">The type to read.</typeparam>
    /// <returns>The data read from the stream.</returns>
    public T Read<T>()
    {
        // An T[] would be a reference type, and alot easier to work with.
        T[] t = new T[1];

        // Marshal.SizeOf will fail with types of unknown size. Try and see...
        int s = Marshal.SizeOf(typeof(T));
        byte[] target = new byte[s];

        // Make sure there is enough data.
        if (Read(target, 0, s) != s)
            throw new InvalidDataException("Not enough data.");

        // Grab a handle of the array we just created, pin it to avoid the gc
        // from moving it, then copy bytes from our stream into the address
        // of our array.
        GCHandle handle = GCHandle.Alloc(t, GCHandleType.Pinned);
        Marshal.Copy(target, 0, handle.AddrOfPinnedObject(), s);

        // Return the first (and only) element in the array.
        return t[0];
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && _ownsStream)
            _backing.Dispose();
        base.Dispose(disposing);
    }
}
Jonathan C Dickinson
A: 

Same stuff as last time, you probably want to use the BinaryWriter class.

public void Write<T>(T item) {
    // An T[] would be a reference type, and alot easier to work with.
    T[] t = new T[] { item };

    // Marshal.SizeOf will fail with types of unknown size. Try and see...
    int s = Marshal.SizeOf(typeof(T));
    if (_index + s > _size)
        // Should throw something more specific.
        throw new Exception("Error 101 Celebrity");

    // Grab a handle of the array we just created, pin it to avoid the gc
    // from moving it, then copy bytes from our array into the stream.
    GCHandle handle = GCHandle.Alloc(t, GCHandleType.Pinned);
    Marshal.Copy(handle.AddrOfPinnedObject(), _stream, _index, s);

    _index += s;
}
Simon Svensson