Added fenced code blocks to the C++/Java/Go docs for syntax highlighting.

Change-Id: I504915c6b5367e8c05dc056463158b8420ad8c5e
Tested: on Linux.
parent d38b9af2
...@@ -55,42 +55,52 @@ $(document).ready(function(){initNavTree('md__cpp_usage.html','');}); ...@@ -55,42 +55,52 @@ $(document).ready(function(){initNavTree('md__cpp_usage.html','');});
<div class="contents"> <div class="contents">
<div class="textblock"><p>Assuming you have written a schema using the above language in say <code>mygame.fbs</code> (FlatBuffer Schema, though the extension doesn't matter), you've generated a C++ header called <code>mygame_generated.h</code> using the compiler (e.g. <code>flatc -c mygame.fbs</code>), you can now start using this in your program by including the header. As noted, this header relies on <code>flatbuffers/flatbuffers.h</code>, which should be in your include path.</p> <div class="textblock"><p>Assuming you have written a schema using the above language in say <code>mygame.fbs</code> (FlatBuffer Schema, though the extension doesn't matter), you've generated a C++ header called <code>mygame_generated.h</code> using the compiler (e.g. <code>flatc -c mygame.fbs</code>), you can now start using this in your program by including the header. As noted, this header relies on <code>flatbuffers/flatbuffers.h</code>, which should be in your include path.</p>
<h3>Writing in C++</h3> <h3>Writing in C++</h3>
<p>To start creating a buffer, create an instance of <code>FlatBufferBuilder</code> which will contain the buffer as it grows: </p><pre class="fragment">FlatBufferBuilder fbb; <p>To start creating a buffer, create an instance of <code>FlatBufferBuilder</code> which will contain the buffer as it grows:</p>
</pre><p>Before we serialize a Monster, we need to first serialize any objects that are contained there-in, i.e. we serialize the data tree using depth first, pre-order traversal. This is generally easy to do on any tree structures. For example: </p><pre class="fragment">auto name = fbb.CreateString("MyMonster"); <div class="fragment"><div class="line">FlatBufferBuilder fbb;</div>
</div><!-- fragment --><p>Before we serialize a Monster, we need to first serialize any objects that are contained there-in, i.e. we serialize the data tree using depth first, pre-order traversal. This is generally easy to do on any tree structures. For example:</p>
unsigned char inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; <div class="fragment"><div class="line"><span class="keyword">auto</span> name = fbb.CreateString(<span class="stringliteral">&quot;MyMonster&quot;</span>);</div>
auto inventory = fbb.CreateVector(inv, 10); <div class="line"></div>
</pre><p><code>CreateString</code> and <code>CreateVector</code> serialize these two built-in datatypes, and return offsets into the serialized data indicating where they are stored, such that <code>Monster</code> below can refer to them.</p> <div class="line"><span class="keywordtype">unsigned</span> <span class="keywordtype">char</span> inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };</div>
<div class="line"><span class="keyword">auto</span> inventory = fbb.CreateVector(inv, 10);</div>
</div><!-- fragment --><p><code>CreateString</code> and <code>CreateVector</code> serialize these two built-in datatypes, and return offsets into the serialized data indicating where they are stored, such that <code>Monster</code> below can refer to them.</p>
<p><code>CreateString</code> can also take an <code>std::string</code>, or a <code>const char *</code> with an explicit length, and is suitable for holding UTF-8 and binary data if needed.</p> <p><code>CreateString</code> can also take an <code>std::string</code>, or a <code>const char *</code> with an explicit length, and is suitable for holding UTF-8 and binary data if needed.</p>
<p><code>CreateVector</code> can also take an <code>std::vector</code>. The offset it returns is typed, i.e. can only be used to set fields of the correct type below. To create a vector of struct objects (which will be stored as contiguous memory in the buffer, use <code>CreateVectorOfStructs</code> instead. </p><pre class="fragment">Vec3 vec(1, 2, 3); <p><code>CreateVector</code> can also take an <code>std::vector</code>. The offset it returns is typed, i.e. can only be used to set fields of the correct type below. To create a vector of struct objects (which will be stored as contiguous memory in the buffer, use <code>CreateVectorOfStructs</code> instead.</p>
</pre><p><code>Vec3</code> is the first example of code from our generated header. Structs (unlike tables) translate to simple structs in C++, so we can construct them in a familiar way.</p> <div class="fragment"><div class="line">Vec3 vec(1, 2, 3);</div>
<p>We have now serialized the non-scalar components of of the monster example, so we could create the monster something like this: </p><pre class="fragment">auto mloc = CreateMonster(fbb, &amp;vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE); </div><!-- fragment --><p><code>Vec3</code> is the first example of code from our generated header. Structs (unlike tables) translate to simple structs in C++, so we can construct them in a familiar way.</p>
</pre><p>Note that we're passing <code>150</code> for the <code>mana</code> field, which happens to be the default value: this means the field will not actually be written to the buffer, since we'll get that value anyway when we query it. This is a nice space savings, since it is very common for fields to be at their default. It means we also don't need to be scared to add fields only used in a minority of cases, since they won't bloat up the buffer sizes if they're not actually used.</p> <p>We have now serialized the non-scalar components of of the monster example, so we could create the monster something like this:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> mloc = CreateMonster(fbb, &amp;vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE);</div>
</div><!-- fragment --><p>Note that we're passing <code>150</code> for the <code>mana</code> field, which happens to be the default value: this means the field will not actually be written to the buffer, since we'll get that value anyway when we query it. This is a nice space savings, since it is very common for fields to be at their default. It means we also don't need to be scared to add fields only used in a minority of cases, since they won't bloat up the buffer sizes if they're not actually used.</p>
<p>We do something similarly for the union field <code>test</code> by specifying a <code>0</code> offset and the <code>NONE</code> enum value (part of every union) to indicate we don't actually want to write this field. You can use <code>0</code> also as a default for other non-scalar types, such as strings, vectors and tables.</p> <p>We do something similarly for the union field <code>test</code> by specifying a <code>0</code> offset and the <code>NONE</code> enum value (part of every union) to indicate we don't actually want to write this field. You can use <code>0</code> also as a default for other non-scalar types, such as strings, vectors and tables.</p>
<p>Tables (like <code>Monster</code>) give you full flexibility on what fields you write (unlike <code>Vec3</code>, which always has all fields set because it is a <code>struct</code>). If you want even more control over this (i.e. skip fields even when they are not default), instead of the convenient <code>CreateMonster</code> call we can also build the object field-by-field manually: </p><pre class="fragment">MonsterBuilder mb(fbb); <p>Tables (like <code>Monster</code>) give you full flexibility on what fields you write (unlike <code>Vec3</code>, which always has all fields set because it is a <code>struct</code>). If you want even more control over this (i.e. skip fields even when they are not default), instead of the convenient <code>CreateMonster</code> call we can also build the object field-by-field manually:</p>
mb.add_pos(&amp;vec); <div class="fragment"><div class="line">MonsterBuilder mb(fbb);</div>
mb.add_hp(80); <div class="line">mb.add_pos(&amp;vec);</div>
mb.add_name(name); <div class="line">mb.add_hp(80);</div>
mb.add_inventory(inventory); <div class="line">mb.add_name(name);</div>
auto mloc = mb.Finish(); <div class="line">mb.add_inventory(inventory);</div>
</pre><p>We start with a temporary helper class <code>MonsterBuilder</code> (which is defined in our generated code also), then call the various <code>add_</code> methods to set fields, and <code>Finish</code> to complete the object. This is pretty much the same code as you find inside <code>CreateMonster</code>, except we're leaving out a few fields. Fields may also be added in any order, though orderings with fields of the same size adjacent to each other most efficient in size, due to alignment. You should not nest these Builder classes (serialize your data in pre-order).</p> <div class="line"><span class="keyword">auto</span> mloc = mb.Finish();</div>
<p>Regardless of whether you used <code>CreateMonster</code> or <code>MonsterBuilder</code>, you now have an offset to the root of your data, and you can finish the buffer using: </p><pre class="fragment">FinishMonsterBuffer(fbb, mloc); </div><!-- fragment --><p>We start with a temporary helper class <code>MonsterBuilder</code> (which is defined in our generated code also), then call the various <code>add_</code> methods to set fields, and <code>Finish</code> to complete the object. This is pretty much the same code as you find inside <code>CreateMonster</code>, except we're leaving out a few fields. Fields may also be added in any order, though orderings with fields of the same size adjacent to each other most efficient in size, due to alignment. You should not nest these Builder classes (serialize your data in pre-order).</p>
</pre><p>The buffer is now ready to be stored somewhere, sent over the network, be compressed, or whatever you'd like to do with it. You can access the start of the buffer with <code>fbb.GetBufferPointer()</code>, and it's size from <code>fbb.GetSize()</code>.</p> <p>Regardless of whether you used <code>CreateMonster</code> or <code>MonsterBuilder</code>, you now have an offset to the root of your data, and you can finish the buffer using:</p>
<div class="fragment"><div class="line">FinishMonsterBuffer(fbb, mloc);</div>
</div><!-- fragment --><p>The buffer is now ready to be stored somewhere, sent over the network, be compressed, or whatever you'd like to do with it. You can access the start of the buffer with <code>fbb.GetBufferPointer()</code>, and it's size from <code>fbb.GetSize()</code>.</p>
<p><code>samples/sample_binary.cpp</code> is a complete code sample similar to the code above, that also includes the reading code below.</p> <p><code>samples/sample_binary.cpp</code> is a complete code sample similar to the code above, that also includes the reading code below.</p>
<h3>Reading in C++</h3> <h3>Reading in C++</h3>
<p>If you've received a buffer from somewhere (disk, network, etc.) you can directly start traversing it using: </p><pre class="fragment">auto monster = GetMonster(buffer_pointer); <p>If you've received a buffer from somewhere (disk, network, etc.) you can directly start traversing it using:</p>
</pre><p><code>monster</code> is of type <code>Monster *</code>, and points to somewhere inside your buffer. If you look in your generated header, you'll see it has convenient accessors for all fields, e.g. </p><pre class="fragment">assert(monster-&gt;hp() == 80); <div class="fragment"><div class="line"><span class="keyword">auto</span> monster = GetMonster(buffer_pointer);</div>
assert(monster-&gt;mana() == 150); // default </div><!-- fragment --><p><code>monster</code> is of type <code>Monster *</code>, and points to somewhere inside your buffer. If you look in your generated header, you'll see it has convenient accessors for all fields, e.g.</p>
assert(strcmp(monster-&gt;name()-&gt;c_str(), "MyMonster") == 0); <div class="fragment"><div class="line">assert(monster-&gt;hp() == 80);</div>
</pre><p>These should all be true. Note that we never stored a <code>mana</code> value, so it will return the default.</p> <div class="line">assert(monster-&gt;mana() == 150); <span class="comment">// default</span></div>
<p>To access sub-objects, in this case the <code>Vec3</code>: </p><pre class="fragment">auto pos = monster-&gt;pos(); <div class="line">assert(strcmp(monster-&gt;name()-&gt;c_str(), <span class="stringliteral">&quot;MyMonster&quot;</span>) == 0);</div>
assert(pos); </div><!-- fragment --><p>These should all be true. Note that we never stored a <code>mana</code> value, so it will return the default.</p>
assert(pos-&gt;z() == 3); <p>To access sub-objects, in this case the <code>Vec3</code>:</p>
</pre><p>If we had not set the <code>pos</code> field during serialization, it would be <code>NULL</code>.</p> <div class="fragment"><div class="line"><span class="keyword">auto</span> pos = monster-&gt;pos();</div>
<p>Similarly, we can access elements of the inventory array: </p><pre class="fragment">auto inv = monster-&gt;inventory(); <div class="line">assert(pos);</div>
assert(inv); <div class="line">assert(pos-&gt;z() == 3);</div>
assert(inv-&gt;Get(9) == 9); </div><!-- fragment --><p>If we had not set the <code>pos</code> field during serialization, it would be <code>NULL</code>.</p>
</pre><h3>Direct memory access</h3> <p>Similarly, we can access elements of the inventory array:</p>
<div class="fragment"><div class="line"><span class="keyword">auto</span> inv = monster-&gt;inventory();</div>
<div class="line">assert(inv);</div>
<div class="line">assert(inv-&gt;Get(9) == 9);</div>
</div><!-- fragment --><h3>Direct memory access</h3>
<p>As you can see from the above examples, all elements in a buffer are accessed through generated accessors. This is because everything is stored in little endian format on all platforms (the accessor performs a swap operation on big endian machines), and also because the layout of things is generally not known to the user.</p> <p>As you can see from the above examples, all elements in a buffer are accessed through generated accessors. This is because everything is stored in little endian format on all platforms (the accessor performs a swap operation on big endian machines), and also because the layout of things is generally not known to the user.</p>
<p>For structs, layout is deterministic and guaranteed to be the same accross platforms (scalars are aligned to their own size, and structs themselves to their largest member), and you are allowed to access this memory directly by using <code>sizeof()</code> and <code>memcpy</code> on the pointer to a struct, or even an array of structs.</p> <p>For structs, layout is deterministic and guaranteed to be the same accross platforms (scalars are aligned to their own size, and structs themselves to their largest member), and you are allowed to access this memory directly by using <code>sizeof()</code> and <code>memcpy</code> on the pointer to a struct, or even an array of structs.</p>
<p>To compute offsets to sub-elements of a struct, make sure they are a structs themselves, as then you can use the pointers to figure out the offset without having to hardcode it. This is handy for use of arrays of structs with calls like <code>glVertexAttribPointer</code> in OpenGL or similar APIs.</p> <p>To compute offsets to sub-elements of a struct, make sure they are a structs themselves, as then you can use the pointers to figure out the offset without having to hardcode it. This is handy for use of arrays of structs with calls like <code>glVertexAttribPointer</code> in OpenGL or similar APIs.</p>
...@@ -100,8 +110,8 @@ assert(inv-&gt;Get(9) == 9); ...@@ -100,8 +110,8 @@ assert(inv-&gt;Get(9) == 9);
<p>When you're processing large amounts of data from a source you know (e.g. your own generated data on disk), this is acceptable, but when reading data from the network that can potentially have been modified by an attacker, this is undesirable.</p> <p>When you're processing large amounts of data from a source you know (e.g. your own generated data on disk), this is acceptable, but when reading data from the network that can potentially have been modified by an attacker, this is undesirable.</p>
<p>For this reason, you can optionally use a buffer verifier before you access the data. This verifier will check all offsets, all sizes of fields, and null termination of strings to ensure that when a buffer is accessed, all reads will end up inside the buffer.</p> <p>For this reason, you can optionally use a buffer verifier before you access the data. This verifier will check all offsets, all sizes of fields, and null termination of strings to ensure that when a buffer is accessed, all reads will end up inside the buffer.</p>
<p>Each root type will have a verification function generated for it, e.g. for <code>Monster</code>, you can call:</p> <p>Each root type will have a verification function generated for it, e.g. for <code>Monster</code>, you can call:</p>
<p>bool ok = VerifyMonsterBuffer(Verifier(buf, len));</p> <div class="fragment"><div class="line"><span class="keywordtype">bool</span> ok = VerifyMonsterBuffer(Verifier(buf, len));</div>
<p>if <code>ok</code> is true, the buffer is safe to read.</p> </div><!-- fragment --><p>if <code>ok</code> is true, the buffer is safe to read.</p>
<p>Besides untrusted data, this function may be useful to call in debug mode, as extra insurance against data being corrupted somewhere along the way.</p> <p>Besides untrusted data, this function may be useful to call in debug mode, as extra insurance against data being corrupted somewhere along the way.</p>
<p>While verifying a buffer isn't "free", it is typically faster than a full traversal (since any scalar data is not actually touched), and since it may cause the buffer to be brought into cache before reading, the actual overhead may be even lower than expected.</p> <p>While verifying a buffer isn't "free", it is typically faster than a full traversal (since any scalar data is not actually touched), and since it may cause the buffer to be brought into cache before reading, the actual overhead may be even lower than expected.</p>
<p>In specialized cases where a denial of service attack is possible, the verifier has two additional constructor arguments that allow you to limit the nesting depth and total amount of tables the verifier may encounter before declaring the buffer malformed.</p> <p>In specialized cases where a denial of service attack is possible, the verifier has two additional constructor arguments that allow you to limit the nesting depth and total amount of tables the verifier may encounter before declaring the buffer malformed.</p>
...@@ -117,9 +127,11 @@ assert(inv-&gt;Get(9) == 9); ...@@ -117,9 +127,11 @@ assert(inv-&gt;Get(9) == 9);
<p>This gives you maximum flexibility. You could even opt to support both, i.e. check for both files, and regenerate the binary from text when required, otherwise just load the binary.</p> <p>This gives you maximum flexibility. You could even opt to support both, i.e. check for both files, and regenerate the binary from text when required, otherwise just load the binary.</p>
<p>This option is currently only available for C++, or Java through JNI.</p> <p>This option is currently only available for C++, or Java through JNI.</p>
<p>As mentioned in the section "Building" above, this technique requires you to link a few more files into your program, and you'll want to include <code>flatbuffers/idl.h</code>.</p> <p>As mentioned in the section "Building" above, this technique requires you to link a few more files into your program, and you'll want to include <code>flatbuffers/idl.h</code>.</p>
<p>Load text (either a schema or json) into an in-memory buffer (there is a convenient <code>LoadFile()</code> utility function in <code>flatbuffers/util.h</code> if you wish). Construct a parser: </p><pre class="fragment">flatbuffers::Parser parser; <p>Load text (either a schema or json) into an in-memory buffer (there is a convenient <code>LoadFile()</code> utility function in <code>flatbuffers/util.h</code> if you wish). Construct a parser:</p>
</pre><p>Now you can parse any number of text files in sequence: </p><pre class="fragment">parser.Parse(text_file.c_str()); <div class="fragment"><div class="line">flatbuffers::Parser parser;</div>
</pre><p>This works similarly to how the command-line compiler works: a sequence of files parsed by the same <code>Parser</code> object allow later files to reference definitions in earlier files. Typically this means you first load a schema file (which populates <code>Parser</code> with definitions), followed by one or more JSON files.</p> </div><!-- fragment --><p>Now you can parse any number of text files in sequence:</p>
<div class="fragment"><div class="line">parser.Parse(text_file.c_str());</div>
</div><!-- fragment --><p>This works similarly to how the command-line compiler works: a sequence of files parsed by the same <code>Parser</code> object allow later files to reference definitions in earlier files. Typically this means you first load a schema file (which populates <code>Parser</code> with definitions), followed by one or more JSON files.</p>
<p>As optional argument to <code>Parse</code>, you may specify a null-terminated list of include paths. If not specified, any include statements try to resolve from the current directory.</p> <p>As optional argument to <code>Parse</code>, you may specify a null-terminated list of include paths. If not specified, any include statements try to resolve from the current directory.</p>
<p>If there were any parsing errors, <code>Parse</code> will return <code>false</code>, and <code>Parser::err</code> contains a human readable error string with a line number etc, which you should present to the creator of that file.</p> <p>If there were any parsing errors, <code>Parse</code> will return <code>false</code>, and <code>Parser::err</code> contains a human readable error string with a line number etc, which you should present to the creator of that file.</p>
<p>After each JSON file, the <code>Parser::fbb</code> member variable is the <code>FlatBufferBuilder</code> that contains the binary buffer version of that file, that you can access as described above.</p> <p>After each JSON file, the <code>Parser::fbb</code> member variable is the <code>FlatBufferBuilder</code> that contains the binary buffer version of that file, that you can access as described above.</p>
......
...@@ -54,40 +54,47 @@ $(document).ready(function(){initNavTree('md__go_usage.html','');}); ...@@ -54,40 +54,47 @@ $(document).ready(function(){initNavTree('md__go_usage.html','');});
</div><!--header--> </div><!--header-->
<div class="contents"> <div class="contents">
<div class="textblock"><p>There's experimental support for reading FlatBuffers in Go. Generate code for Go with the <code>-g</code> option to <code>flatc</code>.</p> <div class="textblock"><p>There's experimental support for reading FlatBuffers in Go. Generate code for Go with the <code>-g</code> option to <code>flatc</code>.</p>
<p>See <code>go_test.go</code> for an example. You import the generated code, read a FlatBuffer binary file into a <code>[]byte</code>, which you pass to the <code>GetRootAsMonster</code> function: </p><pre class="fragment">import ( <p>See <code>go_test.go</code> for an example. You import the generated code, read a FlatBuffer binary file into a <code>[]byte</code>, which you pass to the <code>GetRootAsMonster</code> function:</p>
example "MyGame/Example" <div class="fragment"><div class="line"><span class="keyword">import</span> (</div>
flatbuffers "github.com/google/flatbuffers/go" <div class="line"> example <span class="stringliteral">&quot;MyGame/Example&quot;</span></div>
<div class="line"> flatbuffers <span class="stringliteral">&quot;github.com/google/flatbuffers/go&quot;</span></div>
io/ioutil <div class="line"></div>
) <div class="line"> io/ioutil</div>
<div class="line">)</div>
buf, err := ioutil.ReadFile("monster.dat") <div class="line"></div>
// handle err <div class="line">buf, err := ioutil.ReadFile(<span class="stringliteral">&quot;monster.dat&quot;</span>)</div>
monster := example.GetRootAsMonster(buf, 0) <div class="line"><span class="comment">// handle err</span></div>
</pre><p>Now you can access values like this: </p><pre class="fragment">hp := monster.Hp() <div class="line">monster := example.GetRootAsMonster(buf, 0)</div>
pos := monster.Pos(nil) </div><!-- fragment --><p>Now you can access values like this:</p>
</pre><p>Note that whenever you access a new object like in the <code>Pos</code> example above, a new temporary accessor object gets created. If your code is very performance sensitive (you iterate through a lot of objects), you can replace nil with a pointer to a <code>Vec3</code> object you've already created. This allows you to reuse it across many calls and reduce the amount of object allocation (and thus garbage collection) your program does.</p> <div class="fragment"><div class="line">hp := monster.Hp()</div>
<p>To access vectors you pass an extra index to the vector field accessor. Then a second method with the same name suffixed by <code>Length</code> let's you know the number of elements you can access: </p><pre class="fragment">for i := 0; i &lt; monster.InventoryLength(); i++ { <div class="line">pos := monster.Pos(nil)</div>
monster.Inventory(i) // do something here </div><!-- fragment --><p>Note that whenever you access a new object like in the <code>Pos</code> example above, a new temporary accessor object gets created. If your code is very performance sensitive (you iterate through a lot of objects), you can replace nil with a pointer to a <code>Vec3</code> object you've already created. This allows you to reuse it across many calls and reduce the amount of object allocation (and thus garbage collection) your program does.</p>
} <p>To access vectors you pass an extra index to the vector field accessor. Then a second method with the same name suffixed by <code>Length</code> let's you know the number of elements you can access:</p>
</pre><p>You can also construct these buffers in Go using the functions found in the generated code, and the FlatBufferBuilder class: </p><pre class="fragment">builder := flatbuffers.NewBuilder(0) <div class="fragment"><div class="line"><span class="keywordflow">for</span> i := 0; i &lt; monster.InventoryLength(); i++ {</div>
</pre><p>Create strings: </p><pre class="fragment">str := builder.CreateString("MyMonster") <div class="line"> monster.Inventory(i) <span class="comment">// do something here</span></div>
</pre><p>Create a table with a struct contained therein: </p><pre class="fragment">example.MonsterStart(builder) <div class="line">}</div>
example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, 4, 5, 6)) </div><!-- fragment --><p>You can also construct these buffers in Go using the functions found in the generated code, and the FlatBufferBuilder class:</p>
example.MonsterAddHp(builder, 80) <div class="fragment"><div class="line">builder := flatbuffers.NewBuilder(0)</div>
example.MonsterAddName(builder, str) </div><!-- fragment --><p>Create strings:</p>
example.MonsterAddInventory(builder, inv) <div class="fragment"><div class="line">str := builder.CreateString(<span class="stringliteral">&quot;MyMonster&quot;</span>)</div>
example.MonsterAddTest_Type(builder, 1) </div><!-- fragment --><p>Create a table with a struct contained therein:</p>
example.MonsterAddTest(builder, mon2) <div class="fragment"><div class="line">example.MonsterStart(builder)</div>
example.MonsterAddTest4(builder, test4s) <div class="line">example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, 4, 5, 6))</div>
mon := example.MonsterEnd(builder) <div class="line">example.MonsterAddHp(builder, 80)</div>
</pre><p>Unlike C++, Go does not support table creation functions like 'createMonster()'. This is to create the buffer without using temporary object allocation (since the <code>Vec3</code> is an inline component of <code>Monster</code>, it has to be created right where it is added, whereas the name and the inventory are not inline). Structs do have convenient methods that allow you to construct them in one call. These also have arguments for nested structs, e.g. if a struct has a field <code>a</code> and a nested struct field <code>b</code> (which has fields <code>c</code> and <code>d</code>), then the arguments will be <code>a</code>, <code>c</code> and <code>d</code>.</p> <div class="line">example.MonsterAddName(builder, str)</div>
<p>Vectors also use this start/end pattern to allow vectors of both scalar types and structs: </p><pre class="fragment">example.MonsterStartInventoryVector(builder, 5) <div class="line">example.MonsterAddInventory(builder, inv)</div>
for i := 4; i &gt;= 0; i-- { <div class="line">example.MonsterAddTest_Type(builder, 1)</div>
builder.PrependByte(byte(i)) <div class="line">example.MonsterAddTest(builder, mon2)</div>
} <div class="line">example.MonsterAddTest4(builder, test4s)</div>
inv := builder.EndVector(5) <div class="line">mon := example.MonsterEnd(builder)</div>
</pre><p>The generated method 'StartInventoryVector' is provided as a convenience function which calls 'StartVector' with the correct element size of the vector type which in this case is 'ubyte' or 1 byte per vector element. You pass the number of elements you want to write. You write the elements backwards since the buffer is being constructed back to front.</p> </div><!-- fragment --><p>Unlike C++, Go does not support table creation functions like 'createMonster()'. This is to create the buffer without using temporary object allocation (since the <code>Vec3</code> is an inline component of <code>Monster</code>, it has to be created right where it is added, whereas the name and the inventory are not inline). Structs do have convenient methods that allow you to construct them in one call. These also have arguments for nested structs, e.g. if a struct has a field <code>a</code> and a nested struct field <code>b</code> (which has fields <code>c</code> and <code>d</code>), then the arguments will be <code>a</code>, <code>c</code> and <code>d</code>.</p>
<p>Vectors also use this start/end pattern to allow vectors of both scalar types and structs:</p>
<div class="fragment"><div class="line">example.MonsterStartInventoryVector(builder, 5)</div>
<div class="line"><span class="keywordflow">for</span> i := 4; i &gt;= 0; i-- {</div>
<div class="line"> builder.PrependByte(byte(i))</div>
<div class="line">}</div>
<div class="line">inv := builder.EndVector(5)</div>
</div><!-- fragment --><p>The generated method 'StartInventoryVector' is provided as a convenience function which calls 'StartVector' with the correct element size of the vector type which in this case is 'ubyte' or 1 byte per vector element. You pass the number of elements you want to write. You write the elements backwards since the buffer is being constructed back to front.</p>
<p>There are <code>Prepend</code> functions for all the scalar types. You use <code>PrependUOffset</code> for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate <code>create</code> function in-line, as shown above in the <code>Monster</code> example.</p> <p>There are <code>Prepend</code> functions for all the scalar types. You use <code>PrependUOffset</code> for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate <code>create</code> function in-line, as shown above in the <code>Monster</code> example.</p>
<h2>Text Parsing</h2> <h2>Text Parsing</h2>
<p>There currently is no support for parsing text (Schema's and JSON) directly from Go, though you could use the C++ parser through cgo. Please see the C++ documentation for more on text parsing. </p> <p>There currently is no support for parsing text (Schema's and JSON) directly from Go, though you could use the C++ parser through cgo. Please see the C++ documentation for more on text parsing. </p>
......
...@@ -54,41 +54,51 @@ $(document).ready(function(){initNavTree('md__java_usage.html','');}); ...@@ -54,41 +54,51 @@ $(document).ready(function(){initNavTree('md__java_usage.html','');});
</div><!--header--> </div><!--header-->
<div class="contents"> <div class="contents">
<div class="textblock"><p>FlatBuffers supports reading and writing binary FlatBuffers in Java. Generate code for Java with the <code>-j</code> option to <code>flatc</code>.</p> <div class="textblock"><p>FlatBuffers supports reading and writing binary FlatBuffers in Java. Generate code for Java with the <code>-j</code> option to <code>flatc</code>.</p>
<p>See <code>javaTest.java</code> for an example. Essentially, you read a FlatBuffer binary file into a <code>byte[]</code>, which you then turn into a <code>ByteBuffer</code>, which you pass to the <code>getRootAsMyRootType</code> function: </p><pre class="fragment">ByteBuffer bb = ByteBuffer.wrap(data); <p>See <code>javaTest.java</code> for an example. Essentially, you read a FlatBuffer binary file into a <code>byte[]</code>, which you then turn into a <code>ByteBuffer</code>, which you pass to the <code>getRootAsMyRootType</code> function:</p>
Monster monster = Monster.getRootAsMonster(bb); <div class="fragment"><div class="line">ByteBuffer bb = ByteBuffer.wrap(data);</div>
</pre><p>Now you can access values much like C++: </p><pre class="fragment">short hp = monster.hp(); <div class="line">Monster monster = Monster.getRootAsMonster(bb);</div>
Vec3 pos = monster.pos(); </div><!-- fragment --><p>Now you can access values much like C++:</p>
</pre><p>Note that whenever you access a new object like in the <code>pos</code> example above, a new temporary accessor object gets created. If your code is very performance sensitive (you iterate through a lot of objects), there's a second <code>pos()</code> method to which you can pass a <code>Vec3</code> object you've already created. This allows you to reuse it across many calls and reduce the amount of object allocation (and thus garbage collection) your program does.</p> <div class="fragment"><div class="line"><span class="keywordtype">short</span> hp = monster.hp();</div>
<div class="line">Vec3 pos = monster.pos();</div>
</div><!-- fragment --><p>Note that whenever you access a new object like in the <code>pos</code> example above, a new temporary accessor object gets created. If your code is very performance sensitive (you iterate through a lot of objects), there's a second <code>pos()</code> method to which you can pass a <code>Vec3</code> object you've already created. This allows you to reuse it across many calls and reduce the amount of object allocation (and thus garbage collection) your program does.</p>
<p>Java does not support unsigned scalars. This means that any unsigned types you use in your schema will actually be represented as a signed value. This means all bits are still present, but may represent a negative value when used. For example, to read a <code>byte b</code> as an unsigned number, you can do: <code>(short)(b &amp; 0xFF)</code></p> <p>Java does not support unsigned scalars. This means that any unsigned types you use in your schema will actually be represented as a signed value. This means all bits are still present, but may represent a negative value when used. For example, to read a <code>byte b</code> as an unsigned number, you can do: <code>(short)(b &amp; 0xFF)</code></p>
<p>The default string accessor (e.g. <code>monster.name()</code>) currently always create a new Java <code>String</code> when accessed, since FlatBuffer's UTF-8 strings can't be used in-place by <code>String</code>. Alternatively, use <code>monster.nameAsByteBuffer()</code> which returns a <code>ByteBuffer</code> referring to the UTF-8 data in the original <code>ByteBuffer</code>, which is much more efficient. The <code>ByteBuffer</code>'s <code>position</code> points to the first character, and its <code>limit</code> to just after the last.</p> <p>The default string accessor (e.g. <code>monster.name()</code>) currently always create a new Java <code>String</code> when accessed, since FlatBuffer's UTF-8 strings can't be used in-place by <code>String</code>. Alternatively, use <code>monster.nameAsByteBuffer()</code> which returns a <code>ByteBuffer</code> referring to the UTF-8 data in the original <code>ByteBuffer</code>, which is much more efficient. The <code>ByteBuffer</code>'s <code>position</code> points to the first character, and its <code>limit</code> to just after the last.</p>
<p>Vector access is also a bit different from C++: you pass an extra index to the vector field accessor. Then a second method with the same name suffixed by <code>Length</code> let's you know the number of elements you can access: </p><pre class="fragment">for (int i = 0; i &lt; monster.inventoryLength(); i++) <p>Vector access is also a bit different from C++: you pass an extra index to the vector field accessor. Then a second method with the same name suffixed by <code>Length</code> let's you know the number of elements you can access:</p>
monster.inventory(i); // do something here <div class="fragment"><div class="line"><span class="keywordflow">for</span> (<span class="keywordtype">int</span> i = 0; i &lt; monster.inventoryLength(); i++)</div>
</pre><p>Alternatively, much like strings, you can use <code>monster.inventoryAsByteBuffer()</code> to get a <code>ByteBuffer</code> referring to the whole vector. Use <code>ByteBuffer</code> methods like <code>asFloatBuffer</code> to get specific views if needed.</p> <div class="line"> monster.inventory(i); <span class="comment">// do something here</span></div>
<p>If you specified a file_indentifier in the schema, you can query if the buffer is of the desired type before accessing it using: </p><pre class="fragment">if (Monster.MonsterBufferHasIdentifier(bb)) ... </div><!-- fragment --><p>Alternatively, much like strings, you can use <code>monster.inventoryAsByteBuffer()</code> to get a <code>ByteBuffer</code> referring to the whole vector. Use <code>ByteBuffer</code> methods like <code>asFloatBuffer</code> to get specific views if needed.</p>
</pre><h2>Buffer construction in Java</h2> <p>If you specified a file_indentifier in the schema, you can query if the buffer is of the desired type before accessing it using:</p>
<p>You can also construct these buffers in Java using the static methods found in the generated code, and the FlatBufferBuilder class: </p><pre class="fragment">FlatBufferBuilder fbb = new FlatBufferBuilder(); <div class="fragment"><div class="line"><span class="keywordflow">if</span> (Monster.MonsterBufferHasIdentifier(bb)) ...</div>
</pre><p>Create strings: </p><pre class="fragment">int str = fbb.createString("MyMonster"); </div><!-- fragment --><h2>Buffer construction in Java</h2>
</pre><p>Create a table with a struct contained therein: </p><pre class="fragment">Monster.startMonster(fbb); <p>You can also construct these buffers in Java using the static methods found in the generated code, and the FlatBufferBuilder class:</p>
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0, (byte)4, (short)5, (byte)6)); <div class="fragment"><div class="line">FlatBufferBuilder fbb = <span class="keyword">new</span> FlatBufferBuilder();</div>
Monster.addHp(fbb, (short)80); </div><!-- fragment --><p>Create strings:</p>
Monster.addName(fbb, str); <div class="fragment"><div class="line"><span class="keywordtype">int</span> str = fbb.createString(<span class="stringliteral">&quot;MyMonster&quot;</span>);</div>
Monster.addInventory(fbb, inv); </div><!-- fragment --><p>Create a table with a struct contained therein:</p>
Monster.addTest_type(fbb, (byte)1); <div class="fragment"><div class="line">Monster.startMonster(fbb);</div>
Monster.addTest(fbb, mon2); <div class="line">Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0, (byte)4, (<span class="keywordtype">short</span>)5, (byte)6));</div>
Monster.addTest4(fbb, test4s); <div class="line">Monster.addHp(fbb, (short)80);</div>
int mon = Monster.endMonster(fbb); <div class="line">Monster.addName(fbb, str);</div>
</pre><p>For some simpler types, you can use a convenient <code>create</code> function call that allows you to construct tables in one function call. This example definition however contains an inline struct field, so we have to create the table manually. This is to create the buffer without using temporary object allocation.</p> <div class="line">Monster.addInventory(fbb, inv);</div>
<div class="line">Monster.addTest_type(fbb, (byte)1);</div>
<div class="line">Monster.addTest(fbb, mon2);</div>
<div class="line">Monster.addTest4(fbb, test4s);</div>
<div class="line"><span class="keywordtype">int</span> mon = Monster.endMonster(fbb);</div>
</div><!-- fragment --><p>For some simpler types, you can use a convenient <code>create</code> function call that allows you to construct tables in one function call. This example definition however contains an inline struct field, so we have to create the table manually. This is to create the buffer without using temporary object allocation.</p>
<p>It's important to understand that fields that are structs are inline (like <code>Vec3</code> above), and MUST thus be created between the start and end calls of a table. Everything else (other tables, strings, vectors) MUST be created before the start of the table they are referenced in.</p> <p>It's important to understand that fields that are structs are inline (like <code>Vec3</code> above), and MUST thus be created between the start and end calls of a table. Everything else (other tables, strings, vectors) MUST be created before the start of the table they are referenced in.</p>
<p>Structs do have convenient methods that even have arguments for nested structs.</p> <p>Structs do have convenient methods that even have arguments for nested structs.</p>
<p>As you can see, references to other objects (e.g. the string above) are simple ints, and thus do not have the type-safety of the Offset type in C++. Extra case must thus be taken that you set the right offset on the right field.</p> <p>As you can see, references to other objects (e.g. the string above) are simple ints, and thus do not have the type-safety of the Offset type in C++. Extra case must thus be taken that you set the right offset on the right field.</p>
<p>Vectors can be created from the corresponding Java array like so: </p><pre class="fragment">int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 }); <p>Vectors can be created from the corresponding Java array like so:</p>
</pre><p>This works for arrays of scalars and (int) offsets to strings/tables, but not structs. If you want to write structs, or what you want to write does not sit in an array, you can also use the start/end pattern: </p><pre class="fragment">Monster.startInventoryVector(fbb, 5); <div class="fragment"><div class="line"><span class="keywordtype">int</span> inv = Monster.createInventoryVector(fbb, <span class="keyword">new</span> byte[] { 0, 1, 2, 3, 4 });</div>
for (byte i = 4; i &gt;=0; i--) fbb.addByte(i); </div><!-- fragment --><p>This works for arrays of scalars and (int) offsets to strings/tables, but not structs. If you want to write structs, or what you want to write does not sit in an array, you can also use the start/end pattern:</p>
int inv = fbb.endVector(); <div class="fragment"><div class="line">Monster.startInventoryVector(fbb, 5);</div>
</pre><p>You can use the generated method <code>startInventoryVector</code> to conveniently call <code>startVector</code> with the right element size. You pass the number of elements you want to write. Note how you write the elements backwards since the buffer is being constructed back to front.</p> <div class="line"><span class="keywordflow">for</span> (byte i = 4; i &gt;=0; i--) fbb.addByte(i);</div>
<div class="line"><span class="keywordtype">int</span> inv = fbb.endVector();</div>
</div><!-- fragment --><p>You can use the generated method <code>startInventoryVector</code> to conveniently call <code>startVector</code> with the right element size. You pass the number of elements you want to write. Note how you write the elements backwards since the buffer is being constructed back to front.</p>
<p>There are <code>add</code> functions for all the scalar types. You use <code>addOffset</code> for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate <code>create</code> function in-line, as shown above in the <code>Monster</code> example.</p> <p>There are <code>add</code> functions for all the scalar types. You use <code>addOffset</code> for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate <code>create</code> function in-line, as shown above in the <code>Monster</code> example.</p>
<p>To finish the buffer, call: </p><pre class="fragment">Monster.finishMonsterBuffer(fbb, mon); <p>To finish the buffer, call:</p>
</pre><p>The buffer is now ready to be transmitted. It is contained in the <code>ByteBuffer</code> which you can obtain from <code>fbb.dataBuffer()</code>. Importantly, the valid data does not start from offset 0 in this buffer, but from <code>fbb.dataBuffer().position()</code> (this is because the data was built backwards in memory). It ends at <code>fbb.capacity()</code>.</p> <div class="fragment"><div class="line">Monster.finishMonsterBuffer(fbb, mon);</div>
</div><!-- fragment --><p>The buffer is now ready to be transmitted. It is contained in the <code>ByteBuffer</code> which you can obtain from <code>fbb.dataBuffer()</code>. Importantly, the valid data does not start from offset 0 in this buffer, but from <code>fbb.dataBuffer().position()</code> (this is because the data was built backwards in memory). It ends at <code>fbb.capacity()</code>.</p>
<h2>Text Parsing</h2> <h2>Text Parsing</h2>
<p>There currently is no support for parsing text (Schema's and JSON) directly from Java, though you could use the C++ parser through JNI. Please see the C++ documentation for more on text parsing. </p> <p>There currently is no support for parsing text (Schema's and JSON) directly from Java, though you could use the C++ parser through JNI. Please see the C++ documentation for more on text parsing. </p>
</div></div><!-- contents --> </div></div><!-- contents -->
......
...@@ -12,17 +12,21 @@ your program by including the header. As noted, this header relies on ...@@ -12,17 +12,21 @@ your program by including the header. As noted, this header relies on
To start creating a buffer, create an instance of `FlatBufferBuilder` To start creating a buffer, create an instance of `FlatBufferBuilder`
which will contain the buffer as it grows: which will contain the buffer as it grows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
FlatBufferBuilder fbb; FlatBufferBuilder fbb;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before we serialize a Monster, we need to first serialize any objects Before we serialize a Monster, we need to first serialize any objects
that are contained there-in, i.e. we serialize the data tree using that are contained there-in, i.e. we serialize the data tree using
depth first, pre-order traversal. This is generally easy to do on depth first, pre-order traversal. This is generally easy to do on
any tree structures. For example: any tree structures. For example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto name = fbb.CreateString("MyMonster"); auto name = fbb.CreateString("MyMonster");
unsigned char inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; unsigned char inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto inventory = fbb.CreateVector(inv, 10); auto inventory = fbb.CreateVector(inv, 10);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`CreateString` and `CreateVector` serialize these two built-in `CreateString` and `CreateVector` serialize these two built-in
datatypes, and return offsets into the serialized data indicating where datatypes, and return offsets into the serialized data indicating where
...@@ -38,7 +42,9 @@ correct type below. To create a vector of struct objects (which will ...@@ -38,7 +42,9 @@ correct type below. To create a vector of struct objects (which will
be stored as contiguous memory in the buffer, use `CreateVectorOfStructs` be stored as contiguous memory in the buffer, use `CreateVectorOfStructs`
instead. instead.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
Vec3 vec(1, 2, 3); Vec3 vec(1, 2, 3);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`Vec3` is the first example of code from our generated `Vec3` is the first example of code from our generated
header. Structs (unlike tables) translate to simple structs in C++, so header. Structs (unlike tables) translate to simple structs in C++, so
...@@ -47,7 +53,9 @@ we can construct them in a familiar way. ...@@ -47,7 +53,9 @@ we can construct them in a familiar way.
We have now serialized the non-scalar components of of the monster We have now serialized the non-scalar components of of the monster
example, so we could create the monster something like this: example, so we could create the monster something like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE); auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that we're passing `150` for the `mana` field, which happens to be the Note that we're passing `150` for the `mana` field, which happens to be the
default value: this means the field will not actually be written to the buffer, default value: this means the field will not actually be written to the buffer,
...@@ -67,12 +75,14 @@ If you want even more control over this (i.e. skip fields even when they are ...@@ -67,12 +75,14 @@ If you want even more control over this (i.e. skip fields even when they are
not default), instead of the convenient `CreateMonster` call we can also not default), instead of the convenient `CreateMonster` call we can also
build the object field-by-field manually: build the object field-by-field manually:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
MonsterBuilder mb(fbb); MonsterBuilder mb(fbb);
mb.add_pos(&vec); mb.add_pos(&vec);
mb.add_hp(80); mb.add_hp(80);
mb.add_name(name); mb.add_name(name);
mb.add_inventory(inventory); mb.add_inventory(inventory);
auto mloc = mb.Finish(); auto mloc = mb.Finish();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We start with a temporary helper class `MonsterBuilder` (which is We start with a temporary helper class `MonsterBuilder` (which is
defined in our generated code also), then call the various `add_` defined in our generated code also), then call the various `add_`
...@@ -88,7 +98,9 @@ Regardless of whether you used `CreateMonster` or `MonsterBuilder`, you ...@@ -88,7 +98,9 @@ Regardless of whether you used `CreateMonster` or `MonsterBuilder`, you
now have an offset to the root of your data, and you can finish the now have an offset to the root of your data, and you can finish the
buffer using: buffer using:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
FinishMonsterBuffer(fbb, mloc); FinishMonsterBuffer(fbb, mloc);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The buffer is now ready to be stored somewhere, sent over the network, The buffer is now ready to be stored somewhere, sent over the network,
be compressed, or whatever you'd like to do with it. You can access the be compressed, or whatever you'd like to do with it. You can access the
...@@ -103,33 +115,41 @@ the code above, that also includes the reading code below. ...@@ -103,33 +115,41 @@ the code above, that also includes the reading code below.
If you've received a buffer from somewhere (disk, network, etc.) you can If you've received a buffer from somewhere (disk, network, etc.) you can
directly start traversing it using: directly start traversing it using:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto monster = GetMonster(buffer_pointer); auto monster = GetMonster(buffer_pointer);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`monster` is of type `Monster *`, and points to somewhere inside your `monster` is of type `Monster *`, and points to somewhere inside your
buffer. If you look in your generated header, you'll see it has buffer. If you look in your generated header, you'll see it has
convenient accessors for all fields, e.g. convenient accessors for all fields, e.g.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
assert(monster->hp() == 80); assert(monster->hp() == 80);
assert(monster->mana() == 150); // default assert(monster->mana() == 150); // default
assert(strcmp(monster->name()->c_str(), "MyMonster") == 0); assert(strcmp(monster->name()->c_str(), "MyMonster") == 0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These should all be true. Note that we never stored a `mana` value, so These should all be true. Note that we never stored a `mana` value, so
it will return the default. it will return the default.
To access sub-objects, in this case the `Vec3`: To access sub-objects, in this case the `Vec3`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto pos = monster->pos(); auto pos = monster->pos();
assert(pos); assert(pos);
assert(pos->z() == 3); assert(pos->z() == 3);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If we had not set the `pos` field during serialization, it would be If we had not set the `pos` field during serialization, it would be
`NULL`. `NULL`.
Similarly, we can access elements of the inventory array: Similarly, we can access elements of the inventory array:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto inv = monster->inventory(); auto inv = monster->inventory();
assert(inv); assert(inv);
assert(inv->Get(9) == 9); assert(inv->Get(9) == 9);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Direct memory access ### Direct memory access
...@@ -175,7 +195,9 @@ is accessed, all reads will end up inside the buffer. ...@@ -175,7 +195,9 @@ is accessed, all reads will end up inside the buffer.
Each root type will have a verification function generated for it, Each root type will have a verification function generated for it,
e.g. for `Monster`, you can call: e.g. for `Monster`, you can call:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
bool ok = VerifyMonsterBuffer(Verifier(buf, len)); bool ok = VerifyMonsterBuffer(Verifier(buf, len));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if `ok` is true, the buffer is safe to read. if `ok` is true, the buffer is safe to read.
...@@ -237,11 +259,15 @@ Load text (either a schema or json) into an in-memory buffer (there is a ...@@ -237,11 +259,15 @@ Load text (either a schema or json) into an in-memory buffer (there is a
convenient `LoadFile()` utility function in `flatbuffers/util.h` if you convenient `LoadFile()` utility function in `flatbuffers/util.h` if you
wish). Construct a parser: wish). Construct a parser:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
flatbuffers::Parser parser; flatbuffers::Parser parser;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now you can parse any number of text files in sequence: Now you can parse any number of text files in sequence:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
parser.Parse(text_file.c_str()); parser.Parse(text_file.c_str());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This works similarly to how the command-line compiler works: a sequence This works similarly to how the command-line compiler works: a sequence
of files parsed by the same `Parser` object allow later files to of files parsed by the same `Parser` object allow later files to
......
...@@ -7,6 +7,7 @@ See `go_test.go` for an example. You import the generated code, read a ...@@ -7,6 +7,7 @@ See `go_test.go` for an example. You import the generated code, read a
FlatBuffer binary file into a `[]byte`, which you pass to the FlatBuffer binary file into a `[]byte`, which you pass to the
`GetRootAsMonster` function: `GetRootAsMonster` function:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
import ( import (
example "MyGame/Example" example "MyGame/Example"
flatbuffers "github.com/google/flatbuffers/go" flatbuffers "github.com/google/flatbuffers/go"
...@@ -17,11 +18,14 @@ FlatBuffer binary file into a `[]byte`, which you pass to the ...@@ -17,11 +18,14 @@ FlatBuffer binary file into a `[]byte`, which you pass to the
buf, err := ioutil.ReadFile("monster.dat") buf, err := ioutil.ReadFile("monster.dat")
// handle err // handle err
monster := example.GetRootAsMonster(buf, 0) monster := example.GetRootAsMonster(buf, 0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now you can access values like this: Now you can access values like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
hp := monster.Hp() hp := monster.Hp()
pos := monster.Pos(nil) pos := monster.Pos(nil)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that whenever you access a new object like in the `Pos` example above, Note that whenever you access a new object like in the `Pos` example above,
a new temporary accessor object gets created. If your code is very performance a new temporary accessor object gets created. If your code is very performance
...@@ -34,21 +38,28 @@ To access vectors you pass an extra index to the ...@@ -34,21 +38,28 @@ To access vectors you pass an extra index to the
vector field accessor. Then a second method with the same name suffixed vector field accessor. Then a second method with the same name suffixed
by `Length` let's you know the number of elements you can access: by `Length` let's you know the number of elements you can access:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
for i := 0; i < monster.InventoryLength(); i++ { for i := 0; i < monster.InventoryLength(); i++ {
monster.Inventory(i) // do something here monster.Inventory(i) // do something here
} }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also construct these buffers in Go using the functions found in the You can also construct these buffers in Go using the functions found in the
generated code, and the FlatBufferBuilder class: generated code, and the FlatBufferBuilder class:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
builder := flatbuffers.NewBuilder(0) builder := flatbuffers.NewBuilder(0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create strings: Create strings:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
str := builder.CreateString("MyMonster") str := builder.CreateString("MyMonster")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create a table with a struct contained therein: Create a table with a struct contained therein:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
example.MonsterStart(builder) example.MonsterStart(builder)
example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, 4, 5, 6)) example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, 4, 5, 6))
example.MonsterAddHp(builder, 80) example.MonsterAddHp(builder, 80)
...@@ -58,6 +69,7 @@ Create a table with a struct contained therein: ...@@ -58,6 +69,7 @@ Create a table with a struct contained therein:
example.MonsterAddTest(builder, mon2) example.MonsterAddTest(builder, mon2)
example.MonsterAddTest4(builder, test4s) example.MonsterAddTest4(builder, test4s)
mon := example.MonsterEnd(builder) mon := example.MonsterEnd(builder)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unlike C++, Go does not support table creation functions like 'createMonster()'. Unlike C++, Go does not support table creation functions like 'createMonster()'.
This is to create the buffer without This is to create the buffer without
...@@ -72,11 +84,13 @@ will be `a`, `c` and `d`. ...@@ -72,11 +84,13 @@ will be `a`, `c` and `d`.
Vectors also use this start/end pattern to allow vectors of both scalar types Vectors also use this start/end pattern to allow vectors of both scalar types
and structs: and structs:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go}
example.MonsterStartInventoryVector(builder, 5) example.MonsterStartInventoryVector(builder, 5)
for i := 4; i >= 0; i-- { for i := 4; i >= 0; i-- {
builder.PrependByte(byte(i)) builder.PrependByte(byte(i))
} }
inv := builder.EndVector(5) inv := builder.EndVector(5)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The generated method 'StartInventoryVector' is provided as a convenience The generated method 'StartInventoryVector' is provided as a convenience
function which calls 'StartVector' with the correct element size of the vector function which calls 'StartVector' with the correct element size of the vector
......
...@@ -7,13 +7,17 @@ See `javaTest.java` for an example. Essentially, you read a FlatBuffer binary ...@@ -7,13 +7,17 @@ See `javaTest.java` for an example. Essentially, you read a FlatBuffer binary
file into a `byte[]`, which you then turn into a `ByteBuffer`, which you pass to file into a `byte[]`, which you then turn into a `ByteBuffer`, which you pass to
the `getRootAsMyRootType` function: the `getRootAsMyRootType` function:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
ByteBuffer bb = ByteBuffer.wrap(data); ByteBuffer bb = ByteBuffer.wrap(data);
Monster monster = Monster.getRootAsMonster(bb); Monster monster = Monster.getRootAsMonster(bb);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now you can access values much like C++: Now you can access values much like C++:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
short hp = monster.hp(); short hp = monster.hp();
Vec3 pos = monster.pos(); Vec3 pos = monster.pos();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that whenever you access a new object like in the `pos` example above, Note that whenever you access a new object like in the `pos` example above,
a new temporary accessor object gets created. If your code is very performance a new temporary accessor object gets created. If your code is very performance
...@@ -39,8 +43,10 @@ Vector access is also a bit different from C++: you pass an extra index ...@@ -39,8 +43,10 @@ Vector access is also a bit different from C++: you pass an extra index
to the vector field accessor. Then a second method with the same name to the vector field accessor. Then a second method with the same name
suffixed by `Length` let's you know the number of elements you can access: suffixed by `Length` let's you know the number of elements you can access:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
for (int i = 0; i < monster.inventoryLength(); i++) for (int i = 0; i < monster.inventoryLength(); i++)
monster.inventory(i); // do something here monster.inventory(i); // do something here
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alternatively, much like strings, you can use `monster.inventoryAsByteBuffer()` Alternatively, much like strings, you can use `monster.inventoryAsByteBuffer()`
to get a `ByteBuffer` referring to the whole vector. Use `ByteBuffer` methods to get a `ByteBuffer` referring to the whole vector. Use `ByteBuffer` methods
...@@ -49,7 +55,9 @@ like `asFloatBuffer` to get specific views if needed. ...@@ -49,7 +55,9 @@ like `asFloatBuffer` to get specific views if needed.
If you specified a file_indentifier in the schema, you can query if the If you specified a file_indentifier in the schema, you can query if the
buffer is of the desired type before accessing it using: buffer is of the desired type before accessing it using:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
if (Monster.MonsterBufferHasIdentifier(bb)) ... if (Monster.MonsterBufferHasIdentifier(bb)) ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Buffer construction in Java ## Buffer construction in Java
...@@ -57,14 +65,19 @@ buffer is of the desired type before accessing it using: ...@@ -57,14 +65,19 @@ buffer is of the desired type before accessing it using:
You can also construct these buffers in Java using the static methods found You can also construct these buffers in Java using the static methods found
in the generated code, and the FlatBufferBuilder class: in the generated code, and the FlatBufferBuilder class:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
FlatBufferBuilder fbb = new FlatBufferBuilder(); FlatBufferBuilder fbb = new FlatBufferBuilder();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create strings: Create strings:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
int str = fbb.createString("MyMonster"); int str = fbb.createString("MyMonster");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create a table with a struct contained therein: Create a table with a struct contained therein:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
Monster.startMonster(fbb); Monster.startMonster(fbb);
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0, (byte)4, (short)5, (byte)6)); 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.addHp(fbb, (short)80);
...@@ -74,6 +87,7 @@ Create a table with a struct contained therein: ...@@ -74,6 +87,7 @@ Create a table with a struct contained therein:
Monster.addTest(fbb, mon2); Monster.addTest(fbb, mon2);
Monster.addTest4(fbb, test4s); Monster.addTest4(fbb, test4s);
int mon = Monster.endMonster(fbb); int mon = Monster.endMonster(fbb);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For some simpler types, you can use a convenient `create` function call that For some simpler types, you can use a convenient `create` function call that
allows you to construct tables in one function call. This example definition allows you to construct tables in one function call. This example definition
...@@ -94,15 +108,19 @@ case must thus be taken that you set the right offset on the right field. ...@@ -94,15 +108,19 @@ case must thus be taken that you set the right offset on the right field.
Vectors can be created from the corresponding Java array like so: Vectors can be created from the corresponding Java array like so:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 }); int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This works for arrays of scalars and (int) offsets to strings/tables, This works for arrays of scalars and (int) offsets to strings/tables,
but not structs. If you want to write structs, or what you want to write but not structs. If you want to write structs, or what you want to write
does not sit in an array, you can also use the start/end pattern: does not sit in an array, you can also use the start/end pattern:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
Monster.startInventoryVector(fbb, 5); Monster.startInventoryVector(fbb, 5);
for (byte i = 4; i >=0; i--) fbb.addByte(i); for (byte i = 4; i >=0; i--) fbb.addByte(i);
int inv = fbb.endVector(); int inv = fbb.endVector();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use the generated method `startInventoryVector` to conveniently call You can use the generated method `startInventoryVector` to conveniently call
`startVector` with the right element size. You pass the number of `startVector` with the right element size. You pass the number of
...@@ -116,7 +134,9 @@ above in the `Monster` example. ...@@ -116,7 +134,9 @@ above in the `Monster` example.
To finish the buffer, call: To finish the buffer, call:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
Monster.finishMonsterBuffer(fbb, mon); Monster.finishMonsterBuffer(fbb, mon);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The buffer is now ready to be transmitted. It is contained in the `ByteBuffer` The buffer is now ready to be transmitted. It is contained in the `ByteBuffer`
which you can obtain from `fbb.dataBuffer()`. Importantly, the valid data does which you can obtain from `fbb.dataBuffer()`. Importantly, the valid data does
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment