From d8f49e18d70613dec608987ef897c94b95942d75 Mon Sep 17 00:00:00 2001 From: Derek Bailey Date: Mon, 20 Aug 2018 16:31:44 -0700 Subject: Mono Fix for Unsafe Mode (#4887) * Added preprocessor define for C++ if Template Aliases are supported by the compiler * Revert "Revert "Performance Increase of Vector of Structures using .NET BlockCopy (#4830)"" This reverts commit 1f5eae5d6a135ff6811724f6c57f911d1f46bb15. * Put method was inside #if UNSAFE_BYTEBUFFER which caused compilation failure when building in unsafe mode * Revert "Added preprocessor define for C++ if Template Aliases are supported by the compiler" This reverts commit a75af7352127c261baf0b6cca5cb823e13e78f11. --- net/FlatBuffers/ByteBuffer.cs | 120 +++++++++++++++++++++++++++++++++-- net/FlatBuffers/FlatBufferBuilder.cs | 43 +++++++++++++ net/FlatBuffers/Table.cs | 23 +++++++ 3 files changed, 182 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs index 878e740b..7b170c17 100644 --- a/net/FlatBuffers/ByteBuffer.cs +++ b/net/FlatBuffers/ByteBuffer.cs @@ -30,6 +30,7 @@ // using System; +using System.Collections.Generic; using System.IO; using System.Text; @@ -91,19 +92,78 @@ namespace FlatBuffers public byte[] ToArray(int pos, int len) { - byte[] arr = new byte[len]; - Buffer.BlockCopy(_buffer, pos, arr, 0, len); + return ToArray(pos, len); + } + + /// + /// A lookup of type sizes. Used instead of Marshal.SizeOf() which has additional + /// overhead, but also is compatible with generic functions for simplified code. + /// + private static Dictionary genericSizes = new Dictionary() + { + { typeof(bool), sizeof(bool) }, + { typeof(float), sizeof(float) }, + { typeof(double), sizeof(double) }, + { typeof(sbyte), sizeof(sbyte) }, + { typeof(byte), sizeof(byte) }, + { typeof(short), sizeof(short) }, + { typeof(ushort), sizeof(ushort) }, + { typeof(int), sizeof(int) }, + { typeof(uint), sizeof(uint) }, + { typeof(ulong), sizeof(ulong) }, + { typeof(long), sizeof(long) }, + }; + + /// + /// Get the wire-size (in bytes) of a type supported by flatbuffers. + /// + /// The type to get the wire size of + /// + public static int SizeOf() + { + return genericSizes[typeof(T)]; + } + + /// + /// Checks if the Type provided is supported as scalar value + /// + /// The Type to check + /// True if the type is a scalar type that is supported, falsed otherwise + public static bool IsSupportedType() + { + return genericSizes.ContainsKey(typeof(T)); + } + + /// + /// Get the wire-size (in bytes) of an typed array + /// + /// The type of the array + /// The array to get the size of + /// The number of bytes the array takes on wire + public static int ArraySize(T[] x) + { + return SizeOf() * x.Length; + } + + // Get a portion of the buffer casted into an array of type T, given + // the buffer position and length. + public T[] ToArray(int pos, int len) + where T: struct + { + AssertOffsetAndLength(pos, len); + T[] arr = new T[len]; + Buffer.BlockCopy(_buffer, pos, arr, 0, ArraySize(arr)); return arr; } public byte[] ToSizedArray() { - return ToArray(Position, Length - Position); + return ToArray(Position, Length - Position); } public byte[] ToFullArray() { - return ToArray(0, Length); + return ToArray(0, Length); } public ArraySegment ToArraySegment(int pos, int len) @@ -370,6 +430,58 @@ namespace FlatBuffers #endif // UNSAFE_BYTEBUFFER + /// + /// Copies an array of type T into this buffer, ending at the given + /// offset into this buffer. The starting offset is calculated based on the length + /// of the array and is the value returned. + /// + /// The type of the input data (must be a struct) + /// The offset into this buffer where the copy will end + /// The array to copy data from + /// The 'start' location of this buffer now, after the copy completed + public int Put(int offset, T[] x) + where T : struct + { + if(x == null) + { + throw new ArgumentNullException("Cannot put a null array"); + } + + if(x.Length == 0) + { + throw new ArgumentException("Cannot put an empty array"); + } + + if(!IsSupportedType()) + { + throw new ArgumentException("Cannot put an array of type " + + typeof(T) + " into this buffer"); + } + + if (BitConverter.IsLittleEndian) + { + int numBytes = ByteBuffer.ArraySize(x); + offset -= numBytes; + AssertOffsetAndLength(offset, numBytes); + // if we are LE, just do a block copy + Buffer.BlockCopy(x, 0, _buffer, offset, numBytes); + } + else + { + throw new NotImplementedException("Big Endian Support not implemented yet " + + "for putting typed arrays"); + // if we are BE, we have to swap each element by itself + //for(int i = x.Length - 1; i >= 0; i--) + //{ + // todo: low priority, but need to genericize the Put() functions + //} + } + return offset; + } + + + + public sbyte GetSbyte(int index) { AssertOffsetAndLength(index, sizeof(sbyte)); diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs index a2224498..33bba969 100644 --- a/net/FlatBuffers/FlatBufferBuilder.cs +++ b/net/FlatBuffers/FlatBufferBuilder.cs @@ -179,6 +179,18 @@ namespace FlatBuffers _bb.PutFloat(_space -= sizeof(float), x); } + /// + /// Puts an array of type T into this builder at the + /// current offset + /// + /// The type of the input data + /// The array to copy data from + public void Put(T[] x) + where T : struct + { + _space = _bb.Put(_space, x); + } + public void PutDouble(double x) { _bb.PutDouble(_space -= sizeof(double), x); @@ -245,6 +257,37 @@ namespace FlatBuffers /// The `float` to add to the buffer. public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); } + /// + /// Add an array of type T to the buffer (aligns the data and grows if necessary). + /// + /// The type of the input data + /// The array to copy data from + public void Add(T[] x) + where T : struct + { + if (x == null) + { + throw new ArgumentNullException("Cannot add a null array"); + } + + if( x.Length == 0) + { + // don't do anything if the array is empty + return; + } + + if(!ByteBuffer.IsSupportedType()) + { + throw new ArgumentException("Cannot add this Type array to the builder"); + } + + int size = ByteBuffer.SizeOf(); + // Need to prep on size (for data alignment) and then we pass the + // rest of the length (minus 1) as additional bytes + Prep(size, size * (x.Length - 1)); + Put(x); + } + /// /// Add a `double` to the buffer (aligns the data and grows if necessary). /// diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index 4a188ff8..07db5f42 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -94,6 +94,29 @@ namespace FlatBuffers return bb.ToArraySegment(pos, len); } + // Get the data of a vector whoses offset is stored at "offset" in this object as an + // T[]. If the vector is not present in the ByteBuffer, then a null value will be + // returned. + public T[] __vector_as_array(int offset) + where T : struct + { + if(!BitConverter.IsLittleEndian) + { + throw new NotSupportedException("Getting typed arrays on a Big Endian " + + "system is not support"); + } + + var o = this.__offset(offset); + if (0 == o) + { + return null; + } + + var pos = this.__vector(o); + var len = this.__vector_len(o); + return bb.ToArray(pos, len); + } + // Initialize any Table-derived type to point to the union at the given offset. public T __union(int offset) where T : struct, IFlatbufferObject { -- cgit v1.2.3