diff --git a/.gitignore b/.gitignore index 13a68f835..cdb37b3ca 100755 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,3 @@ CMakeLists.txt.user CMakeScripts/** build/Xcode/FlatBuffers.xcodeproj/project.xcworkspace/** build/Xcode/FlatBuffers.xcodeproj/xcuserdata/** - diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a6323c06..c8ac1e63d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_parser.cpp src/idl_gen_cpp.cpp src/idl_gen_java.cpp + src/idl_gen_csharp.cpp src/idl_gen_go.cpp src/idl_gen_text.cpp src/flatc.cpp diff --git a/build/VS2010/flatc.vcxproj b/build/VS2010/flatc.vcxproj index e9313cfea..1e2b44abd 100755 --- a/build/VS2010/flatc.vcxproj +++ b/build/VS2010/flatc.vcxproj @@ -266,6 +266,7 @@ + Level4 diff --git a/build/VS2010/flatc.vcxproj.user b/build/VS2010/flatc.vcxproj.user index b7bcc9a56..f86bf4820 100755 --- a/build/VS2010/flatc.vcxproj.user +++ b/build/VS2010/flatc.vcxproj.user @@ -3,12 +3,12 @@ ..\..\tests WindowsLocalDebugger - -j -c -g -b -t monster_test.fbs monsterdata_test.golden + -j -c -n -g -b -t monster_test.fbs monsterdata_test.golden ..\.. WindowsLocalDebugger - -j -c -g -b -t monster_test.fbs monsterdata_test.golden + -j -c -n -g -b -t monster_test.fbs monsterdata_test.golden -j -c -g -b -t monster_test.fbs monsterdata_test.golden diff --git a/docs/source/GoUsage.md b/docs/source/GoUsage.md old mode 100755 new mode 100644 diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 53cb13d12..7192ac94e 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -386,6 +386,14 @@ extern bool GenerateJava(const Parser &parser, const std::string &file_name, const GeneratorOptions &opts); +// Generate C# files from the definitions in the Parser object. +// See idl_gen_csharp.cpp. +extern bool GenerateCSharp(const Parser &parser, + const std::string &path, + const std::string &file_name, + const GeneratorOptions &opts); + + } // namespace flatbuffers #endif // FLATBUFFERS_IDL_H_ diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs new file mode 100644 index 000000000..a1c29ab42 --- /dev/null +++ b/net/FlatBuffers/ByteBuffer.cs @@ -0,0 +1,142 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Linq; + +namespace FlatBuffers +{ + /// + /// Class to mimick Java's ByteBuffer which is used heavily in Flatbuffers + /// + public class ByteBuffer + { + private readonly byte[] _buffer; + + public int Length { get { return _buffer.Length; } } + + public byte[] Data { get { return _buffer; } } + + public ByteBuffer(byte[] buffer) + { + _buffer = buffer; + } + + protected void WriteLittleEndian(int offset, byte[] data) + { + if (!BitConverter.IsLittleEndian) + { + data = data.Reverse().ToArray(); + } + Buffer.BlockCopy(data, 0, _buffer, offset, data.Length); + } + + protected byte[] ReadLittleEndian(int offset, int count) + { + AssertOffsetAndLength(offset, count); + var tmp = new byte[count]; + Buffer.BlockCopy(_buffer, offset, tmp, 0, count); + return (BitConverter.IsLittleEndian) + ? tmp + : tmp.Reverse().ToArray(); + } + + private void AssertOffsetAndLength(int offset, int length) + { + if (offset < 0 || + offset >= _buffer.Length || + offset + length > _buffer.Length) + throw new ArgumentOutOfRangeException(); + } + + public void PutByte(int offset, byte value) + { + AssertOffsetAndLength(offset, sizeof(byte)); + _buffer[offset] = value; + } + + public void PutShort(int offset, short value) + { + AssertOffsetAndLength(offset, sizeof(short)); + WriteLittleEndian(offset, BitConverter.GetBytes(value)); + } + + public void PutInt(int offset, int value) + { + AssertOffsetAndLength(offset, sizeof(int)); + WriteLittleEndian(offset, BitConverter.GetBytes(value)); + } + + public void PutLong(int offset, long value) + { + AssertOffsetAndLength(offset, sizeof(long)); + WriteLittleEndian(offset, BitConverter.GetBytes(value)); + } + + public void PutFloat(int offset, float value) + { + AssertOffsetAndLength(offset, sizeof(float)); + WriteLittleEndian(offset, BitConverter.GetBytes(value)); + } + + public void PutDouble(int offset, double value) + { + AssertOffsetAndLength(offset, sizeof(double)); + WriteLittleEndian(offset, BitConverter.GetBytes(value)); + } + + public byte Get(int index) + { + AssertOffsetAndLength(index, sizeof(byte)); + return _buffer[index]; + } + + public short GetShort(int index) + { + var tmp = ReadLittleEndian(index, sizeof(short)); + var value = BitConverter.ToInt16(tmp, 0); + return value; + } + + public int GetInt(int index) + { + var tmp = ReadLittleEndian(index, sizeof(int)); + var value = BitConverter.ToInt32(tmp, 0); + return value; + } + + public long GetLong(int index) + { + var tmp = ReadLittleEndian(index, sizeof(long)); + var value = BitConverter.ToInt64(tmp, 0); + return value; + } + + public float GetFloat(int index) + { + var tmp = ReadLittleEndian(index, sizeof(float)); + var value = BitConverter.ToSingle(tmp, 0); + return value; + } + + public double GetDouble(int index) + { + var tmp = ReadLittleEndian(index, sizeof(double)); + var value = BitConverter.ToDouble(tmp, 0); + return value; + } + } +} diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs new file mode 100644 index 000000000..f48e7a477 --- /dev/null +++ b/net/FlatBuffers/FlatBufferBuilder.cs @@ -0,0 +1,351 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Text; + +namespace FlatBuffers +{ + /// + /// Responsible for building up and accessing a flatbuffer formatted byte + /// array (via ByteBuffer) + /// + public class FlatBufferBuilder + { + private int _space; + private ByteBuffer _bb; + private int _minAlign = 1; + + // The vtable for the current table, null otherwise. + private int[] _vtable; + // Starting offset of the current struct/table. + private int _objectStart; + // List of offsets of all vtables. + private int[] _vtables = new int[16]; + // Number of entries in `vtables` in use. + private int _numVtables = 0; + // For the current vector being built. + private int _vectorNumElems = 0; + + public FlatBufferBuilder(int initialSize) + { + if (initialSize <= 0) + throw new ArgumentOutOfRangeException("initialSize", + initialSize, "Must be greater than zero"); + _space = initialSize; + _bb = new ByteBuffer(new byte[initialSize]); + } + + + public int Offset { get { return _bb.Length - _space; } } + + public void Pad(int size) + { + for (var i = 0; i < size; i++) + { + _bb.PutByte(--_space, 0); + } + } + + // Doubles the size of the ByteBuffer, and copies the old data towards + // the end of the new buffer (since we build the buffer backwards). + void GrowBuffer() + { + var oldBuf = _bb.Data; + var oldBufSize = oldBuf.Length; + if ((oldBufSize & 0xC0000000) != 0) + throw new Exception( + "FlatBuffers: cannot grow buffer beyond 2 gigabytes."); + + var newBufSize = oldBufSize << 1; + var newBuf = new byte[newBufSize]; + + Buffer.BlockCopy(oldBuf, 0, newBuf, newBufSize - oldBufSize, + oldBufSize); + + _bb = new ByteBuffer(newBuf); + } + + // Prepare to write an element of `size` after `additional_bytes` + // have been written, e.g. if you write a string, you need to align + // such the int length field is aligned to SIZEOF_INT, and the string + // data follows it directly. + // If all you need to do is align, `additional_bytes` will be 0. + public void Prep(int size, int additionalBytes) + { + // Track the biggest thing we've ever aligned to. + if (size > _minAlign) + _minAlign = size; + // Find the amount of alignment needed such that `size` is properly + // aligned after `additional_bytes` + var alignSize = + ((~((int)_bb.Length - _space + additionalBytes)) + 1) & + (size - 1); + // Reallocate the buffer if needed. + while (_space < alignSize + size + additionalBytes) + { + var oldBufSize = (int)_bb.Length; + GrowBuffer(); + _space += (int)_bb.Length - oldBufSize; + + } + Pad(alignSize); + } + + public void PutByte(byte x) + { + _bb.PutByte(_space -= sizeof(byte), x); + } + + public void PutShort(short x) + { + _bb.PutShort(_space -= sizeof(short), x); + } + + public void PutInt32(int x) + { + _bb.PutInt(_space -= sizeof(int), x); + } + + public void PutInt64(long x) + { + _bb.PutLong(_space -= sizeof(long), x); + } + + public void PutFloat(float x) + { + _bb.PutFloat(_space -= sizeof(float), x); + } + + public void PutDouble(double x) + { + _bb.PutDouble(_space -= sizeof(double), x); + } + + // Adds a scalar to the buffer, properly aligned, and the buffer grown + // if needed. + public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); } + public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); } + public void AddInt(int x) { Prep(sizeof(int), 0); PutInt32(x); } + public void AddLong(long x) { Prep(sizeof(long), 0); PutInt64(x); } + public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); } + public void AddDouble(double x) { Prep(sizeof(double), 0); + PutDouble(x); } + + + + // Adds on offset, relative to where it will be written. + public void AddOffset(int off) + { + Prep(sizeof(int), 0); // Ensure alignment is already done. + if (off > Offset) + throw new ArgumentException(); + + off = Offset - off + sizeof(int); + PutInt32(off); + } + + public void StartVector(int elemSize, int count, int alignment) + { + NotNested(); + _vectorNumElems = count; + Prep(sizeof(int), elemSize * count); + Prep(alignment, elemSize * count); // Just in case alignment > int. + } + + public int EndVector() + { + PutInt32(_vectorNumElems); + return Offset; + } + + public void Nested(int obj) + { + // Structs are always stored inline, so need to be created right + // where they are used. You'll get this assert if you created it + // elsewhere. + if (obj != Offset) + throw new Exception( + "FlatBuffers: struct must be serialized inline."); + } + + public void NotNested() + { + // You should not be creating any other objects or strings/vectors + // while an object is being constructed + if (_vtable != null) + throw new Exception( + "FlatBuffers: object serialization must not be nested."); + } + + public void StartObject(int numfields) + { + NotNested(); + _vtable = new int[numfields]; + _objectStart = Offset; + } + + + // Set the current vtable at `voffset` to the current location in the + // buffer. + public void Slot(int voffset) + { + _vtable[voffset] = Offset; + } + + // Add a scalar to a table at `o` into its vtable, with value `x` and default `d` + public void AddByte(int o, byte x, int d) { if (x != d) { AddByte(x); Slot(o); } } + public void AddShort(int o, short x, int d) { if (x != d) { AddShort(x); Slot(o); } } + public void AddInt(int o, int x, int d) { if (x != d) { AddInt(x); Slot(o); } } + public void AddLong(int o, long x, long d) { if (x != d) { AddLong(x); Slot(o); } } + public void AddFloat(int o, float x, double d) { if (x != d) { AddFloat(x); Slot(o); } } + public void AddDouble(int o, double x, double d) { if (x != d) { AddDouble(x); Slot(o); } } + public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } } + + public int CreateString(string s) + { + NotNested(); + byte[] utf8 = Encoding.UTF8.GetBytes(s); + AddByte((byte)0); + StartVector(1, utf8.Length, 1); + Buffer.BlockCopy(utf8, 0, _bb.Data, _space -= utf8.Length, + utf8.Length); + return EndVector(); + } + + // Structs are stored inline, so nothing additional is being added. + // `d` is always 0. + public void AddStruct(int voffset, int x, int d) + { + if (x != d) + { + Nested(x); + Slot(voffset); + } + } + + public int EndObject() + { + + if (_vtable == null) + throw new InvalidOperationException( + "Flatbuffers: calling endObject without a startObject"); + + AddInt((int)0); + var vtableloc = Offset; + // Write out the current vtable. + for (int i = _vtable.Length - 1; i >= 0 ; i--) { + // Offset relative to the start of the table. + short off = (short)(_vtable[i] != 0 + ? vtableloc - _vtable[i] + : 0); + AddShort(off); + } + + const int standardFields = 2; // The fields below: + AddShort((short)(vtableloc - _objectStart)); + AddShort((short)((_vtable.Length + standardFields) * + sizeof(short))); + + // Search for an existing vtable that matches the current one. + int existingVtable = 0; + + for (int i = 0; i < _numVtables; i++) { + int vt1 = _bb.Length - _vtables[i]; + int vt2 = _space; + short len = _bb.GetShort(vt1); + if (len == _bb.GetShort(vt2)) { + for (int j = sizeof(short); j < len; j += sizeof(short)) { + if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) { + goto endLoop; + } + } + existingVtable = _vtables[i]; + break; + } + + endLoop: { } + } + + if (existingVtable != 0) { + // Found a match: + // Remove the current vtable. + _space = _bb.Length - vtableloc; + // Point table to existing vtable. + _bb.PutInt(_space, existingVtable - vtableloc); + } else { + // No match: + // Add the location of the current vtable to the list of + // vtables. + if (_numVtables == _vtables.Length) + { + // Arrays.CopyOf(vtables num_vtables * 2); + var newvtables = new int[ _numVtables * 2]; + Array.Copy(_vtables, newvtables, _vtables.Length); + + _vtables = newvtables; + }; + _vtables[_numVtables++] = Offset; + // Point table to current vtable. + _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc); + } + + _vtable = null; + return vtableloc; + } + + public void Finish(int rootTable) + { + Prep(_minAlign, sizeof(int)); + AddOffset(rootTable); + } + + public ByteBuffer Data { get { return _bb; }} + + // The FlatBuffer data doesn't start at offset 0 in the ByteBuffer: + public int DataStart { get { return _space; } } + + + // Utility function for copying a byte array that starts at 0. + public byte[] SizedByteArray() + { + var newArray = new byte[_bb.Data.Length]; + Buffer.BlockCopy(_bb.Data, DataStart, newArray, 0, + _bb.Data.Length); + return newArray; + } + + public void Finish(int rootTable, string fileIdentifier) + { + Prep(_minAlign, sizeof(int) + + FlatBufferConstants.FileIdentifierLength); + if (fileIdentifier.Length != + FlatBufferConstants.FileIdentifierLength) + throw new ArgumentException( + "FlatBuffers: file identifier must be length " + + FlatBufferConstants.FileIdentifierLength, + "fileIdentifier"); + for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0; + i--) + { + AddByte((byte)fileIdentifier[i]); + } + AddOffset(rootTable); + } + + + } +} diff --git a/net/FlatBuffers/FlatBufferConstants.cs b/net/FlatBuffers/FlatBufferConstants.cs new file mode 100644 index 000000000..5c3706e10 --- /dev/null +++ b/net/FlatBuffers/FlatBufferConstants.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FlatBuffers +{ + public static class FlatBufferConstants + { + public const int FileIdentifierLength = 4; + } +} diff --git a/net/FlatBuffers/FlatBuffers.1.0.0.nuspec b/net/FlatBuffers/FlatBuffers.1.0.0.nuspec new file mode 100644 index 000000000..93ddf938b --- /dev/null +++ b/net/FlatBuffers/FlatBuffers.1.0.0.nuspec @@ -0,0 +1,12 @@ + + + + FlatBuffers + 1.0.0-alpha00003 + Google Inc + A .NET port of Google Inc's FlatBuffers project. + en-US + https://github.com/evolutional/flatbuffers + http://www.apache.org/licenses/LICENSE-2.0 + + \ No newline at end of file diff --git a/net/FlatBuffers/FlatBuffers.csproj b/net/FlatBuffers/FlatBuffers.csproj new file mode 100644 index 000000000..a973e6982 --- /dev/null +++ b/net/FlatBuffers/FlatBuffers.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {28C00774-1E73-4A75-AD8F-844CD21A064D} + Library + Properties + FlatBuffers + FlatBuffers + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net/FlatBuffers/Properties/AssemblyInfo.cs b/net/FlatBuffers/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..3d4ea15cc --- /dev/null +++ b/net/FlatBuffers/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FlatBuffers")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FlatBuffers")] +[assembly: AssemblyCopyright("Copyright © 2014 Google Inc")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("91c32e64-ef20-47df-9c9f-cec9207bc6df")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/net/FlatBuffers/Struct.cs b/net/FlatBuffers/Struct.cs new file mode 100644 index 000000000..4cd280124 --- /dev/null +++ b/net/FlatBuffers/Struct.cs @@ -0,0 +1,27 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace FlatBuffers +{ + /// + /// All structs in the generated code derive from this class, and add their own accessors. + /// + public abstract class Struct + { + protected int bb_pos; + protected ByteBuffer bb; + } +} \ No newline at end of file diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs new file mode 100644 index 000000000..f09cb05e0 --- /dev/null +++ b/net/FlatBuffers/Table.cs @@ -0,0 +1,92 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Text; + +namespace FlatBuffers +{ + /// + /// All tables in the generated code derive from this class, and add their own accessors. + /// + public abstract class Table + { + protected int bb_pos; + protected ByteBuffer bb; + + // Look up a field in the vtable, return an offset into the object, or 0 if the field is not + // present. + protected int __offset(int vtableOffset) + { + int vtable = bb_pos - bb.GetInt(bb_pos); + return vtableOffset < bb.GetShort(vtable) ? bb.GetShort(vtable + vtableOffset) : 0; + } + + // Retrieve the relative offset stored at "offset" + protected int __indirect(int offset) + { + return offset + bb.GetInt(offset); + } + + // Create a .NET String from UTF-8 data stored inside the flatbuffer. + protected string __string(int offset) + { + offset += bb.GetInt(offset); + var len = bb.GetInt(offset); + var startPos = offset + sizeof(int); + return Encoding.UTF8.GetString(bb.Data, startPos , len); + } + + // Get the length of a vector whose offset is stored at "offset" in this object. + protected int __vector_len(int offset) + { + offset += bb_pos; + offset += bb.GetInt(offset); + return bb.GetInt(offset); + } + + // Get the start of data of a vector whose offset is stored at "offset" in this object. + protected int __vector(int offset) + { + offset += bb_pos; + return offset + bb.GetInt(offset) + sizeof(int); // data starts after the length + } + + // Initialize any Table-derived type to point to the union at the given offset. + protected Table __union(Table t, int offset) + { + offset += bb_pos; + t.bb_pos = offset + bb.GetInt(offset); + t.bb = bb; + return t; + } + + protected static bool __has_identifier(ByteBuffer bb, int offset, string ident) + { + if (ident.Length != FlatBufferConstants.FileIdentifierLength) + throw new ArgumentException("FlatBuffers: file identifier must be length " + FlatBufferConstants.FileIdentifierLength, "ident"); + + for (var i = 0; i < FlatBufferConstants.FileIdentifierLength; i++) + { + if (ident[i] != (char)bb.Get(offset + sizeof(int) + i)) return false; + } + + return true; + } + + + } +} diff --git a/src/flatc.cpp b/src/flatc.cpp index 2329a5dee..d4a99db1d 100755 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -76,6 +76,8 @@ const Generator generators[] = { "Generate Go files for tables/structs" }, { flatbuffers::GenerateJava, "j", "Java", "Generate Java classes for tables/structs" }, + { flatbuffers::GenerateCSharp, "n", "C#", + "Generate C# classes for tables/structs" } }; const char *program_name = NULL; diff --git a/src/idl_gen_csharp.cpp b/src/idl_gen_csharp.cpp new file mode 100644 index 000000000..03210bc56 --- /dev/null +++ b/src/idl_gen_csharp.cpp @@ -0,0 +1,403 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { +namespace csharp { + +static std::string GenTypeBasic(const Type &type) { + static const char *ctypename[] = { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) #JTYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD + }; + return ctypename[type.base_type]; +} + +static std::string GenTypeGet(const Type &type); + +static std::string GenTypePointer(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_STRING: + return "string"; + case BASE_TYPE_VECTOR: + return GenTypeGet(type.VectorType()); + case BASE_TYPE_STRUCT: + return type.struct_def->name; + case BASE_TYPE_UNION: + // fall through + default: + return "Table"; + } +} + +static std::string GenTypeGet(const Type &type) { + return IsScalar(type.base_type) + ? GenTypeBasic(type) + : GenTypePointer(type); +} + +static void GenComment(const std::string &dc, + std::string *code_ptr, + const char *prefix = "") { + std::string &code = *code_ptr; + if (dc.length()) { + code += std::string(prefix) + "/*" + dc + "*/\n"; + } +} + + +static void GenEnum(EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + if (enum_def.generated) return; + + // Generate enum definitions of the form: + // public static int Name = value; + // We use ints rather than the C# Enum feature, because we want them + // to map directly to how they're used in C/C++ and file formats. + GenComment(enum_def.doc_comment, code_ptr); + code += "public class " + enum_def.name + "\n{\n"; + for (auto it = enum_def.vals.vec.begin(); + it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + GenComment(ev.doc_comment, code_ptr, " "); + code += " public static " + GenTypeBasic(enum_def.underlying_type); + code += " " + ev.name + " = "; + code += NumToString(ev.value) + ";\n"; + } + code += "};\n\n"; +} + +// Returns the function name that is able to read a value of the given type. +static std::string GenGetter(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_STRING: return "__string"; + case BASE_TYPE_STRUCT: return "__struct"; + case BASE_TYPE_UNION: return "__union"; + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); + default: + return "bb.Get" + (SizeOf(type.base_type) > 1 + ? MakeCamel(GenTypeGet(type)) + : ""); + } +} + +// Returns the method name for use with add/put calls. +static std::string GenMethod(const FieldDef &field) { + return IsScalar(field.value.type.base_type) + ? MakeCamel(GenTypeBasic(field.value.type)) + : (IsStruct(field.value.type) ? "Struct" : "Offset"); +} + +// Recursively generate arguments for a constructor, to deal with nested +// structs. +static void GenStructArgs(const StructDef &struct_def, std::string *code_ptr, + const char *nameprefix) { + std::string &code = *code_ptr; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (IsStruct(field.value.type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the struct name. + GenStructArgs(*field.value.type.struct_def, code_ptr, + (field.value.type.struct_def->name + "_").c_str()); + } else { + code += ", " + GenTypeBasic(field.value.type) + " " + nameprefix; + code += MakeCamel(field.name, true); + } + } +} + +// Recusively generate struct construction statements of the form: +// builder.PutType(name); +// and insert manual padding. +static void GenStructBody(const StructDef &struct_def, std::string *code_ptr, + const char *nameprefix) { + std::string &code = *code_ptr; + code += " builder.Prep(" + NumToString(struct_def.minalign) + ", "; + code += NumToString(struct_def.bytesize) + ");\n"; + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); + ++it) { + auto &field = **it; + if (field.padding) + code += " builder.Pad(" + NumToString(field.padding) + ");\n"; + if (IsStruct(field.value.type)) { + GenStructBody(*field.value.type.struct_def, code_ptr, + (field.value.type.struct_def->name + "_").c_str()); + } else { + code += " builder.Put" + GenMethod(field) + "("; + code += nameprefix + MakeCamel(field.name, true) + ");\n"; + } + } +} + +static void GenStruct(const Parser &parser, StructDef &struct_def, + std::string *code_ptr) { + if (struct_def.generated) return; + std::string &code = *code_ptr; + + // Generate a struct accessor class, with methods of the form: + // public type Name() { return bb.GetType(i + offset); } + // or for tables of the form: + // public type Name() { + // int o = __offset(offset); return o != 0 ? bb.GetType(o + i) : default; + // } + GenComment(struct_def.doc_comment, code_ptr); + code += "public class " + struct_def.name + " : "; + code += struct_def.fixed ? "Struct" : "Table"; + code += " {\n"; + if (!struct_def.fixed) { + // Generate a special accessor for the table that when used as the root + // of a FlatBuffer + code += " public static " + struct_def.name + " GetRootAs"; + code += struct_def.name; + code += "(ByteBuffer _bb, int offset) { "; + // Endian handled by .NET ByteBuffer impl + code += "return (new " + struct_def.name; + code += "()).__init(_bb.GetInt(offset) + offset, _bb); }\n"; + if (parser.root_struct_def == &struct_def) { + if (parser.file_identifier_.length()) { + // Check if a buffer has the identifier. + code += " public static bool " + struct_def.name; + code += "BufferHasIdentifier(ByteBuffer _bb, int offset) { return "; + code += "__has_identifier(_bb, offset, \"" + parser.file_identifier_; + code += "\"); }\n"; + } + } + } + // Generate the __init method that sets the field in a pre-existing + // accessor object. This is to allow object reuse. + code += " public " + struct_def.name; + code += " __init(int _i, ByteBuffer _bb) "; + code += "{ bb_pos = _i; bb = _bb; return this; }\n\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (field.deprecated) continue; + GenComment(field.doc_comment, code_ptr, " "); + std::string type_name = GenTypeGet(field.value.type); + std::string method_start = " public " + type_name + " " + + MakeCamel(field.name, true); + // Generate the accessors that don't do object reuse. + if (field.value.type.base_type == BASE_TYPE_STRUCT) { + // Calls the accessor that takes an accessor object with a new object. + code += method_start + "() { return " + MakeCamel(field.name, true); + code += "(new "; + code += type_name + "()); }\n"; + } else if (field.value.type.base_type == BASE_TYPE_VECTOR && + field.value.type.element == BASE_TYPE_STRUCT) { + // Accessors for vectors of structs also take accessor objects, this + // generates a variant without that argument. + code += method_start + "(int j) { return " + MakeCamel(field.name, true); + code += "(new "; + code += type_name + "(), j); }\n"; + } + std::string getter = GenGetter(field.value.type); + code += method_start + "("; + // Most field accessors need to retrieve and test the field offset first, + // this is the prefix code for that: + auto offset_prefix = ") { int o = __offset(" + + NumToString(field.value.offset) + + "); return o != 0 ? "; + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + code += ") { return " + getter; + code += "(bb_pos + " + NumToString(field.value.offset) + ")"; + } else { + code += offset_prefix + getter; + code += "(o + bb_pos) : ("; + code += type_name; + code += ")" + field.value.constant; + } + } else { + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: + code += type_name + " obj"; + if (struct_def.fixed) { + code += ") { return obj.__init(bb_pos + "; + code += NumToString(field.value.offset) + ", bb)"; + } else { + code += offset_prefix; + code += "obj.__init("; + code += field.value.type.struct_def->fixed + ? "o + bb_pos" + : "__indirect(o + bb_pos)"; + code += ", bb) : null"; + } + break; + case BASE_TYPE_STRING: + code += offset_prefix + getter +"(o + bb_pos) : null"; + break; + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += type_name + " obj, "; + getter = "obj.__init"; + } + code += "int j" + offset_prefix + getter +"("; + auto index = "__vector(o) + j * " + + NumToString(InlineSize(vectortype)); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += vectortype.struct_def->fixed + ? index + : "__indirect(" + index + ")"; + code += ", bb"; + } else { + code += index; + } + code += ") : "; + code += IsScalar(field.value.type.element) ? "(" + type_name + ")0" : "null"; + break; + } + case BASE_TYPE_UNION: + code += type_name + " obj" + offset_prefix + getter; + code += "(obj, o) : null"; + break; + default: + assert(0); + } + } + code += "; }\n"; + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += " public int " + MakeCamel(field.name, true) + "Length("; + code += offset_prefix; + code += "__vector_len(o) : 0; }\n"; + } + } + code += "\n"; + if (struct_def.fixed) { + // create a struct constructor function + code += " public static int Create" + struct_def.name; + code += "(FlatBufferBuilder builder"; + GenStructArgs(struct_def, code_ptr, ""); + code += ") {\n"; + GenStructBody(struct_def, code_ptr, ""); + code += " return builder.Offset;\n }\n"; + } else { + // Create a set of static methods that allow table construction, + // of the form: + // public static void AddName(FlatBufferBuilder builder, short name) + // { builder.AddShort(id, name, default); } + code += " public static void Start" + struct_def.name; + code += "(FlatBufferBuilder builder) { builder.StartObject("; + code += NumToString(struct_def.fields.vec.size()) + "); }\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (field.deprecated) continue; + code += " public static void Add" + MakeCamel(field.name); + code += "(FlatBufferBuilder builder, " + GenTypeBasic(field.value.type); + auto argname = MakeCamel(field.name, false); + if (!IsScalar(field.value.type.base_type)) argname += "Offset"; + code += " " + argname + ") { builder.Add"; + code += GenMethod(field) + "("; + code += NumToString(it - struct_def.fields.vec.begin()) + ", "; + code += argname + ", " + field.value.constant; + code += "); }\n"; + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += " public static void Start" + MakeCamel(field.name); + code += "Vector(FlatBufferBuilder builder, int numElems) "; + code += "{ builder.StartVector("; + auto vector_type = field.value.type.VectorType(); + auto alignment = InlineAlignment(vector_type); + auto elem_size = InlineSize(vector_type); + code += NumToString(elem_size); + code += ", numElems, " + NumToString(alignment); + code += "); }\n"; + } + } + code += " public static int End" + struct_def.name; + code += "(FlatBufferBuilder builder) { return builder.EndObject(); }\n"; + if (parser.root_struct_def == &struct_def) { + code += " public static void Finish" + struct_def.name; + code += "Buffer(FlatBufferBuilder builder, int offset) { "; + code += "builder.Finish(offset"; + if (parser.file_identifier_.length()) + code += ", \"" + parser.file_identifier_ + "\""; + code += "); }\n"; + } + } + code += "};\n\n"; +} + +// Save out the generated code for a single Java class while adding +// declaration boilerplate. +static bool SaveClass(const Parser &parser, const Definition &def, + const std::string &classcode, const std::string &path) { + if (!classcode.length()) return true; + + std::string namespace_csharp; + std::string namespace_dir = path; + auto &namespaces = parser.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + if (namespace_csharp.length()) { + namespace_csharp += "."; + namespace_dir += kPathSeparator; + } + namespace_csharp += *it; + namespace_dir += *it; + } + EnsureDirExists(namespace_dir); + + std::string code = "// automatically generated, do not modify\n\n"; + code += "namespace " + namespace_csharp + "\n{\n\n"; +// Other usings + code += "using FlatBuffers;\n\n"; + code += classcode; + code += "\n}\n"; + auto filename = namespace_dir + kPathSeparator + def.name + ".cs"; + return SaveFile(filename.c_str(), code, false); +} + +} // namespace csharp + +bool GenerateCSharp(const Parser &parser, + const std::string &path, + const std::string & /*file_name*/, + const GeneratorOptions & /*opts*/) { + using namespace csharp; + + for (auto it = parser.enums_.vec.begin(); + it != parser.enums_.vec.end(); ++it) { + std::string enumcode; + GenEnum(**it, &enumcode); + if (!SaveClass(parser, **it, enumcode, path)) + return false; + } + + for (auto it = parser.structs_.vec.begin(); + it != parser.structs_.vec.end(); ++it) { + std::string declcode; + GenStruct(parser, **it, &declcode); + if (!SaveClass(parser, **it, declcode, path)) + return false; + } + + return true; +} + +} // namespace flatbuffers + diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp old mode 100755 new mode 100644 diff --git a/tests/FlatBuffers.Test/Assert.cs b/tests/FlatBuffers.Test/Assert.cs new file mode 100644 index 000000000..9e4108239 --- /dev/null +++ b/tests/FlatBuffers.Test/Assert.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FlatBuffers.Test +{ + + public class AssertFailedException : Exception + { + private readonly object _expected; + private readonly object _actual; + + public AssertFailedException(object expected, object actual) + { + _expected = expected; + _actual = actual; + } + + public override string Message + { + get { return string.Format("Expected {0} but saw {1}", _expected, _actual); } + } + } + + public class AssertUnexpectedThrowException : Exception + { + private readonly object _expected; + + public AssertUnexpectedThrowException(object expected) + { + _expected = expected; + } + + public override string Message + { + get { return string.Format("Expected exception of type {0}", _expected); } + } + } + + public static class Assert + { + public static void AreEqual(T expected, T actual) + { + if (!expected.Equals(actual)) + { + throw new AssertFailedException(expected, actual); + } + } + + public static void IsTrue(bool value) + { + if (!value) + { + throw new AssertFailedException(true, value); + } + } + + public static void Throws(Action action) where T : Exception + { + var caught = false; + try + { + action(); + } + catch (T ex) + { + caught = true; + } + + if (!caught) + { + throw new AssertUnexpectedThrowException(typeof (T)); + } + } + } +} diff --git a/tests/FlatBuffers.Test/ByteBufferTests.cs b/tests/FlatBuffers.Test/ByteBufferTests.cs new file mode 100644 index 000000000..b3c1811c9 --- /dev/null +++ b/tests/FlatBuffers.Test/ByteBufferTests.cs @@ -0,0 +1,244 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace FlatBuffers.Test +{ + public class ByteBufferTests + { + + public void ByteBuffer_Length_MatchesBufferLength() + { + var buffer = new byte[1000]; + var uut = new ByteBuffer(buffer); + Assert.AreEqual(buffer.Length, uut.Length); + } + + public void ByteBuffer_PutBytePopulatesBufferAtZeroOffset() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + uut.PutByte(0, (byte)99); + + Assert.AreEqual((byte)99, buffer[0]); + } + + public void ByteBuffer_PutByteCannotPutAtOffsetPastLength() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutByte(1, 99)); + } + + public void ByteBuffer_PutShortPopulatesBufferCorrectly() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + uut.PutShort(0, (short)1); + + // Ensure Endianness was written correctly + Assert.AreEqual((byte)1, buffer[0]); + Assert.AreEqual((byte)0, buffer[1]); + } + + public void ByteBuffer_PutShortCannotPutAtOffsetPastLength() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutShort(2, 99)); + } + + public void ByteBuffer_PutShortChecksLength() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutShort(0, 99)); + } + + public void ByteBuffer_PutShortChecksLengthAndOffset() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutShort(1, 99)); + } + + public void ByteBuffer_PutIntPopulatesBufferCorrectly() + { + var buffer = new byte[4]; + var uut = new ByteBuffer(buffer); + uut.PutInt(0, 0x0A0B0C0D); + + // Ensure Endianness was written correctly + Assert.AreEqual(0x0D, buffer[0]); + Assert.AreEqual(0x0C, buffer[1]); + Assert.AreEqual(0x0B, buffer[2]); + Assert.AreEqual(0x0A, buffer[3]); + } + + public void ByteBuffer_PutIntCannotPutAtOffsetPastLength() + { + var buffer = new byte[4]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutInt(2, 0x0A0B0C0D)); + } + + public void ByteBuffer_PutIntChecksLength() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutInt(0, 0x0A0B0C0D)); + } + + public void ByteBuffer_PutIntChecksLengthAndOffset() + { + var buffer = new byte[4]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutInt(2, 0x0A0B0C0D)); + } + + public void ByteBuffer_PutLongPopulatesBufferCorrectly() + { + var buffer = new byte[8]; + var uut = new ByteBuffer(buffer); + uut.PutLong(0, 0x010203040A0B0C0D); + + // Ensure Endianness was written correctly + Assert.AreEqual(0x0D, buffer[0]); + Assert.AreEqual(0x0C, buffer[1]); + Assert.AreEqual(0x0B, buffer[2]); + Assert.AreEqual(0x0A, buffer[3]); + Assert.AreEqual(0x04, buffer[4]); + Assert.AreEqual(0x03, buffer[5]); + Assert.AreEqual(0x02, buffer[6]); + Assert.AreEqual(0x01, buffer[7]); + } + + public void ByteBuffer_PutLongCannotPutAtOffsetPastLength() + { + var buffer = new byte[8]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutLong(2, 0x010203040A0B0C0D)); + } + + public void ByteBuffer_PutLongChecksLength() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutLong(0, 0x010203040A0B0C0D)); + } + + public void ByteBuffer_PutLongChecksLengthAndOffset() + { + var buffer = new byte[8]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.PutLong(2, 0x010203040A0B0C0D)); + } + + public void ByteBuffer_GetByteReturnsCorrectData() + { + var buffer = new byte[1]; + buffer[0] = 99; + var uut = new ByteBuffer(buffer); + Assert.AreEqual((byte)99, uut.Get(0)); + } + + public void ByteBuffer_GetByteChecksOffset() + { + var buffer = new byte[1]; + var uut = new ByteBuffer(buffer); + Assert.Throws(()=>uut.Get(1)); + } + + public void ByteBuffer_GetShortReturnsCorrectData() + { + var buffer = new byte[2]; + buffer[0] = 1; + buffer[1] = 0; + var uut = new ByteBuffer(buffer); + Assert.AreEqual(1, uut.GetShort(0)); + } + + public void ByteBuffer_GetShortChecksOffset() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetShort(2)); + } + + public void ByteBuffer_GetShortChecksLength() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetShort(1)); + } + + public void ByteBuffer_GetIntReturnsCorrectData() + { + var buffer = new byte[4]; + buffer[0] = 0x0D; + buffer[1] = 0x0C; + buffer[2] = 0x0B; + buffer[3] = 0x0A; + var uut = new ByteBuffer(buffer); + Assert.AreEqual(0x0A0B0C0D, uut.GetInt(0)); + } + + public void ByteBuffer_GetIntChecksOffset() + { + var buffer = new byte[4]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetInt(4)); + } + + public void ByteBuffer_GetIntChecksLength() + { + var buffer = new byte[2]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetInt(0)); + } + + public void ByteBuffer_GetLongReturnsCorrectData() + { + var buffer = new byte[8]; + buffer[0] = 0x0D; + buffer[1] = 0x0C; + buffer[2] = 0x0B; + buffer[3] = 0x0A; + buffer[4] = 0x04; + buffer[5] = 0x03; + buffer[6] = 0x02; + buffer[7] = 0x01; + var uut = new ByteBuffer(buffer); + Assert.AreEqual(0x010203040A0B0C0D, uut.GetLong(0)); + } + + public void ByteBuffer_GetLongChecksOffset() + { + var buffer = new byte[8]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetLong(8)); + } + + public void ByteBuffer_GetLongChecksLength() + { + var buffer = new byte[7]; + var uut = new ByteBuffer(buffer); + Assert.Throws(() => uut.GetLong(0)); + } + + } +} diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj new file mode 100644 index 000000000..6d7a8def1 --- /dev/null +++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj @@ -0,0 +1,82 @@ + + + + Debug + AnyCPU + {9DB0B5E7-757E-4BD1-A5F6-279390331254} + Exe + Properties + FlatBuffers.Test + FlatBuffers.Test + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + 3.5 + + + + + MyGame\Example\Any.cs + + + MyGame\Example\Color.cs + + + MyGame\Example\Monster.cs + + + MyGame\Example\Test.cs + + + MyGame\Example\Vec3.cs + + + + + + + + + + {28C00774-1E73-4A75-AD8F-844CD21A064D} + FlatBuffers + + + + + Resources\monsterdata_test.bin + PreserveNewest + + + + + \ No newline at end of file diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs new file mode 100644 index 000000000..ed946b16a --- /dev/null +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -0,0 +1,145 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.IO; +using MyGame.Example; + +namespace FlatBuffers.Test +{ + public class FlatBuffersExampleTests + { + public void RunTests() + { + CanCreateNewFlatBufferFromScratch(); + CanReadCppGeneratedWireFile(); + } + + public void CanCreateNewFlatBufferFromScratch() + { + // Second, let's create a FlatBuffer from scratch in C#, and test it also. + // We use an initial size of 1 to exercise the reallocation algorithm, + // normally a size larger than the typical FlatBuffer you generate would be + // better for performance. + var fbb = new FlatBufferBuilder(1); + + // We set up the same values as monsterdata.json: + + var str = fbb.CreateString("MyMonster"); + var test1 = fbb.CreateString("test1"); + var test2 = fbb.CreateString("test2"); + + + Monster.StartInventoryVector(fbb, 5); + for (int i = 4; i >= 0; i--) + { + fbb.AddByte((byte)i); + } + var inv = fbb.EndVector(); + + Monster.StartMonster(fbb); + Monster.AddHp(fbb, (short)20); + var mon2 = Monster.EndMonster(fbb); + + Monster.StartTest4Vector(fbb, 2); + MyGame.Example.Test.CreateTest(fbb, (short)10, (byte)20); + MyGame.Example.Test.CreateTest(fbb, (short)30, (byte)40); + var test4 = fbb.EndVector(); + + Monster.StartTestarrayofstringVector(fbb, 2); + fbb.AddOffset(test2); + fbb.AddOffset(test1); + var testArrayOfString = fbb.EndVector(); + + + Monster.StartMonster(fbb); + Monster.AddPos(fbb, Vec3.CreateVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0, + (byte)4, (short)5, (byte)6)); + Monster.AddHp(fbb, (short)80); + Monster.AddName(fbb, str); + Monster.AddInventory(fbb, inv); + Monster.AddTestType(fbb, (byte)1); + Monster.AddTest(fbb, mon2); + Monster.AddTest4(fbb, test4); + Monster.AddTestarrayofstring(fbb, testArrayOfString); + var mon = Monster.EndMonster(fbb); + + fbb.Finish(mon); + + // Dump to output directory so we can inspect later, if needed + using (var ms= new MemoryStream(fbb.Data.Data, fbb.DataStart, fbb.Offset)) + { + var data = ms.ToArray(); + File.WriteAllBytes(@"Resources/monsterdata_cstest.bin",data); + } + + // Now assert the buffer + TestBuffer(fbb.Data, fbb.DataStart); + } + + private void TestBuffer(ByteBuffer bb, int start) + { + var monster = Monster.GetRootAsMonster(bb, start); + + Assert.AreEqual(80, monster.Hp()); + Assert.AreEqual(150, monster.Mana()); + Assert.AreEqual("MyMonster", monster.Name()); + + var pos = monster.Pos(); + Assert.AreEqual(1.0f, pos.X()); + Assert.AreEqual(2.0f, pos.Y()); + Assert.AreEqual(3.0f, pos.Z()); + + Assert.AreEqual(3.0f, pos.Test1()); + Assert.AreEqual((byte)4, pos.Test2()); + var t = pos.Test3(); + Assert.AreEqual((short)5, t.A()); + Assert.AreEqual((byte)6, t.B()); + + Assert.AreEqual((byte)Any.Monster, monster.TestType()); + + var monster2 = new Monster(); + Assert.IsTrue(monster.Test(monster2) != null); + Assert.AreEqual(20, monster2.Hp()); + + + Assert.AreEqual(5, monster.InventoryLength()); + var invsum = 0; + for (var i = 0; i < monster.InventoryLength(); i++) + { + invsum += monster.Inventory(i); + } + Assert.AreEqual(10, invsum); + + var test0 = monster.Test4(0); + var test1 = monster.Test4(1); + Assert.AreEqual(2, monster.Test4Length()); + + Assert.AreEqual(100, test0.A() + test0.B() + test1.A() + test1.B()); + + + Assert.AreEqual(2, monster.TestarrayofstringLength()); + Assert.AreEqual("test1", monster.Testarrayofstring(0)); + Assert.AreEqual("test2", monster.Testarrayofstring(1)); + } + + public void CanReadCppGeneratedWireFile() + { + var data = File.ReadAllBytes(@"Resources/monsterdata_test.bin"); + var bb = new ByteBuffer(data); + TestBuffer(bb, 0); + } + } +} diff --git a/tests/FlatBuffers.Test/Program.cs b/tests/FlatBuffers.Test/Program.cs new file mode 100644 index 000000000..2662b2a3d --- /dev/null +++ b/tests/FlatBuffers.Test/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace FlatBuffers.Test +{ + static class Program + { + public static int Main(string[] args) + { + var tests = new FlatBuffersExampleTests(); + try + { + tests.RunTests(); + } + catch (Exception ex) + { + Console.WriteLine("FlatBuffersExampleTests FAILED - {0}", ex.GetBaseException()); + return -1; + } + + // Run ByteBuffers Tests + var testClass = new ByteBufferTests(); + + var methods = testClass.GetType().GetMethods(BindingFlags.Public | + BindingFlags.Instance) + .Where(m => m.Name.StartsWith("ByteBuffer_")); + foreach (var method in methods) + { + try + { + method.Invoke(testClass, new object[] { }); + } + catch (Exception ex) + { + Console.WriteLine("ByteBufferTests FAILED when invoking {0} with error {1}", + method.Name, ex.GetBaseException()); + return -1; + } + + } + + return 0; + } + } +} diff --git a/tests/FlatBuffers.Test/Properties/AssemblyInfo.cs b/tests/FlatBuffers.Test/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ee3b831c7 --- /dev/null +++ b/tests/FlatBuffers.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FlatBuffers.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FlatBuffers.Test")] +[assembly: AssemblyCopyright("Copyright © 2014 Google Inc")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a1d58a51-3e74-4ae9-aac7-5a399c9eed1a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/GoTest.sh b/tests/GoTest.sh old mode 100755 new mode 100644 diff --git a/tests/MyGame/Example/Any.cs b/tests/MyGame/Example/Any.cs new file mode 100644 index 000000000..73a22487a --- /dev/null +++ b/tests/MyGame/Example/Any.cs @@ -0,0 +1,15 @@ +// automatically generated, do not modify + +namespace MyGame.Example +{ + +using FlatBuffers; + +public class Any +{ + public static byte NONE = 0; + public static byte Monster = 1; +}; + + +} diff --git a/tests/MyGame/Example/Color.cs b/tests/MyGame/Example/Color.cs new file mode 100644 index 000000000..34abc641c --- /dev/null +++ b/tests/MyGame/Example/Color.cs @@ -0,0 +1,16 @@ +// automatically generated, do not modify + +namespace MyGame.Example +{ + +using FlatBuffers; + +public class Color +{ + public static byte Red = 1; + public static byte Green = 2; + public static byte Blue = 8; +}; + + +} diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs new file mode 100644 index 000000000..0e02416bf --- /dev/null +++ b/tests/MyGame/Example/Monster.cs @@ -0,0 +1,64 @@ +// automatically generated, do not modify + +namespace MyGame.Example +{ + +using FlatBuffers; + +public class Monster : Table { + public static Monster GetRootAsMonster(ByteBuffer _bb, int offset) { return (new Monster()).__init(_bb.GetInt(offset) + offset, _bb); } + public static bool MonsterBufferHasIdentifier(ByteBuffer _bb, int offset) { return __has_identifier(_bb, offset, "MONS"); } + public Monster __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + public Vec3 Pos() { return Pos(new Vec3()); } + public Vec3 Pos(Vec3 obj) { int o = __offset(4); return o != 0 ? obj.__init(o + bb_pos, bb) : null; } + public short Mana() { int o = __offset(6); return o != 0 ? bb.GetShort(o + bb_pos) : (short)150; } + public short Hp() { int o = __offset(8); return o != 0 ? bb.GetShort(o + bb_pos) : (short)100; } + public string Name() { int o = __offset(10); return o != 0 ? __string(o + bb_pos) : null; } + public byte Inventory(int j) { int o = __offset(14); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; } + public int InventoryLength() { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; } + public byte Color() { int o = __offset(16); return o != 0 ? bb.Get(o + bb_pos) : (byte)8; } + public byte TestType() { int o = __offset(18); return o != 0 ? bb.Get(o + bb_pos) : (byte)0; } + public Table Test(Table obj) { int o = __offset(20); return o != 0 ? __union(obj, o) : null; } + public Test Test4(int j) { return Test4(new Test(), j); } + public Test Test4(Test obj, int j) { int o = __offset(22); return o != 0 ? obj.__init(__vector(o) + j * 4, bb) : null; } + public int Test4Length() { int o = __offset(22); return o != 0 ? __vector_len(o) : 0; } + public string Testarrayofstring(int j) { int o = __offset(24); return o != 0 ? __string(__vector(o) + j * 4) : null; } + public int TestarrayofstringLength() { int o = __offset(24); return o != 0 ? __vector_len(o) : 0; } + /* an example documentation comment: this will end up in the generated code multiline too */ + public Monster Testarrayoftables(int j) { return Testarrayoftables(new Monster(), j); } + public Monster Testarrayoftables(Monster obj, int j) { int o = __offset(26); return o != 0 ? obj.__init(__indirect(__vector(o) + j * 4), bb) : null; } + public int TestarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; } + public Monster Enemy() { return Enemy(new Monster()); } + public Monster Enemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + public byte Testnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; } + public int TestnestedflatbufferLength() { int o = __offset(30); return o != 0 ? __vector_len(o) : 0; } + public Monster Testempty() { return Testempty(new Monster()); } + public Monster Testempty(Monster obj) { int o = __offset(32); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + + public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(15); } + public static void AddPos(FlatBufferBuilder builder, int posOffset) { builder.AddStruct(0, posOffset, 0); } + public static void AddMana(FlatBufferBuilder builder, short mana) { builder.AddShort(1, mana, 150); } + public static void AddHp(FlatBufferBuilder builder, short hp) { builder.AddShort(2, hp, 100); } + public static void AddName(FlatBufferBuilder builder, int nameOffset) { builder.AddOffset(3, nameOffset, 0); } + public static void AddInventory(FlatBufferBuilder builder, int inventoryOffset) { builder.AddOffset(5, inventoryOffset, 0); } + public static void StartInventoryVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); } + public static void AddColor(FlatBufferBuilder builder, byte color) { builder.AddByte(6, color, 8); } + public static void AddTestType(FlatBufferBuilder builder, byte testType) { builder.AddByte(7, testType, 0); } + public static void AddTest(FlatBufferBuilder builder, int testOffset) { builder.AddOffset(8, testOffset, 0); } + public static void AddTest4(FlatBufferBuilder builder, int test4Offset) { builder.AddOffset(9, test4Offset, 0); } + public static void StartTest4Vector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 2); } + public static void AddTestarrayofstring(FlatBufferBuilder builder, int testarrayofstringOffset) { builder.AddOffset(10, testarrayofstringOffset, 0); } + public static void StartTestarrayofstringVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } + public static void AddTestarrayoftables(FlatBufferBuilder builder, int testarrayoftablesOffset) { builder.AddOffset(11, testarrayoftablesOffset, 0); } + public static void StartTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } + public static void AddEnemy(FlatBufferBuilder builder, int enemyOffset) { builder.AddOffset(12, enemyOffset, 0); } + public static void AddTestnestedflatbuffer(FlatBufferBuilder builder, int testnestedflatbufferOffset) { builder.AddOffset(13, testnestedflatbufferOffset, 0); } + public static void StartTestnestedflatbufferVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); } + public static void AddTestempty(FlatBufferBuilder builder, int testemptyOffset) { builder.AddOffset(14, testemptyOffset, 0); } + public static int EndMonster(FlatBufferBuilder builder) { return builder.EndObject(); } + public static void FinishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.Finish(offset, "MONS"); } +}; + + +} diff --git a/tests/MyGame/Example/Test.cs b/tests/MyGame/Example/Test.cs new file mode 100644 index 000000000..20d8fe6f6 --- /dev/null +++ b/tests/MyGame/Example/Test.cs @@ -0,0 +1,24 @@ +// automatically generated, do not modify + +namespace MyGame.Example +{ + +using FlatBuffers; + +public class Test : Struct { + public Test __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + public short A() { return bb.GetShort(bb_pos + 0); } + public byte B() { return bb.Get(bb_pos + 2); } + + public static int CreateTest(FlatBufferBuilder builder, short A, byte B) { + builder.Prep(2, 4); + builder.Pad(1); + builder.PutByte(B); + builder.PutShort(A); + return builder.Offset; + } +}; + + +} diff --git a/tests/MyGame/Example/Vec3.cs b/tests/MyGame/Example/Vec3.cs new file mode 100644 index 000000000..169a94435 --- /dev/null +++ b/tests/MyGame/Example/Vec3.cs @@ -0,0 +1,38 @@ +// automatically generated, do not modify + +namespace MyGame.Example +{ + +using FlatBuffers; + +public class Vec3 : Struct { + public Vec3 __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + public float X() { return bb.GetFloat(bb_pos + 0); } + public float Y() { return bb.GetFloat(bb_pos + 4); } + public float Z() { return bb.GetFloat(bb_pos + 8); } + public double Test1() { return bb.GetDouble(bb_pos + 16); } + public byte Test2() { return bb.Get(bb_pos + 24); } + public Test Test3() { return Test3(new Test()); } + public Test Test3(Test obj) { return obj.__init(bb_pos + 26, bb); } + + public static int CreateVec3(FlatBufferBuilder builder, float X, float Y, float Z, double Test1, byte Test2, short Test_A, byte Test_B) { + builder.Prep(16, 32); + builder.Pad(2); + builder.Prep(2, 4); + builder.Pad(1); + builder.PutByte(Test_B); + builder.PutShort(Test_A); + builder.Pad(1); + builder.PutByte(Test2); + builder.PutDouble(Test1); + builder.Pad(4); + builder.PutFloat(Z); + builder.PutFloat(Y); + builder.PutFloat(X); + return builder.Offset; + } +}; + + +}