diff --git a/docs/html/md__compiler.html b/docs/html/md__compiler.html
index 1f97fa3b7..d7bf180ec 100644
--- a/docs/html/md__compiler.html
+++ b/docs/html/md__compiler.html
@@ -64,7 +64,8 @@ $(document).ready(function(){initNavTree('md__compiler.html','');});
-b : If data is contained in this file, generate a filename.bin containing the binary flatbuffer.
-t : If data is contained in this file, generate a filename.json representing the data in the flatbuffer.
-o PATH : Output all generated files to PATH (either absolute, or relative to the current directory). If omitted, PATH will be the current directory. PATH should end in your systems path separator, e.g. / or \.
--S : Generate strict JSON (field names are enclosed in quotes). By default, no quotes are generated.
+-S : Generate strict JSON (field names are enclosed in quotes). By default, no quotes are generated.
+-P : Don't prefix enum values in generated C++ by their enum type.
diff --git a/docs/html/md__cpp_usage.html b/docs/html/md__cpp_usage.html
index 9a74910f5..122f53b2e 100644
--- a/docs/html/md__cpp_usage.html
+++ b/docs/html/md__cpp_usage.html
@@ -104,6 +104,7 @@ assert(inv->Get(9) == 9);
if ok is true, the buffer is safe to read.
Besides untrusted data, this function may be useful to call in debug mode, as extra insurance against data being corrupted somewhere along the way.
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.
+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.
Text & schema parsing
Using binary buffers with the generated header provides a super low overhead use of FlatBuffer data. There are, however, times when you want to use text formats, for example because it interacts better with source control, or you want to give your users easy access to data.
Another reason might be that you already have a lot of data in JSON format, or a tool that generates JSON, and if you can write a schema for it, this will provide you an easy way to use that data directly.
diff --git a/docs/html/md__java_usage.html b/docs/html/md__java_usage.html
index 99b923048..b9b9ee023 100644
--- a/docs/html/md__java_usage.html
+++ b/docs/html/md__java_usage.html
@@ -63,7 +63,9 @@ Vec3 pos = monster.pos();
Sadly the string accessors currently always create a new string when accessed, since FlatBuffer's UTF-8 strings can't be read in-place by Java.
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 Length let's you know the number of elements you can access:
for (int i = 0; i < monster.inventoryLength(); i++)
monster.inventory(i); // do something here
-You can also construct these buffers in Java using the static methods found in the generated code, and the FlatBufferBuilder class:
FlatBufferBuilder fbb = new FlatBufferBuilder();
+
If you specified a file_indentifier in the schema, you can query if the buffer is of the desired type before accessing it using:
if (Monster.MonsterBufferHasIdentifier(bb, start)) ...
+
Buffer construction in Java
+You can also construct these buffers in Java using the static methods found in the generated code, and the FlatBufferBuilder class:
FlatBufferBuilder fbb = new FlatBufferBuilder();
Create strings:
int str = fbb.createString("MyMonster");
Create a table with a struct contained therein:
Monster.startMonster(fbb);
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0, (byte)4, (short)5, (byte)6));
@@ -83,6 +85,8 @@ for (byte i = 4; i >=0; i--) fbb.addByte(i);
int inv = fbb.endVector();
You can use the generated method startInventoryVector to conveniently call startVector with the right element size. You pass the number of elements you want to write. You write the elements backwards since the buffer is being constructed back to front.
There are add functions for all the scalar types. You use addOffset for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate create function in-line, as shown above in the Monster example.
+To finish the buffer, call:
Monster.finishMonsterBuffer(fbb, mon);
+
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 not start from offset 0 in this buffer, but from fbb.dataStart() (this is because the data was built backwards in memory). It ends at fbb,capacity().
Text Parsing
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.
diff --git a/docs/source/JavaUsage.md b/docs/source/JavaUsage.md
index 4e2fe8bc8..15bd816dd 100755
--- a/docs/source/JavaUsage.md
+++ b/docs/source/JavaUsage.md
@@ -38,6 +38,14 @@ suffixed by `Length` let's you know the number of elements you can access:
for (int i = 0; i < monster.inventoryLength(); i++)
monster.inventory(i); // do something here
+If you specified a file_indentifier in the schema, you can query if the
+buffer is of the desired type before accessing it using:
+
+ if (Monster.MonsterBufferHasIdentifier(bb, start)) ...
+
+
+## Buffer construction in Java
+
You can also construct these buffers in Java using the static methods found
in the generated code, and the FlatBufferBuilder class:
@@ -91,6 +99,16 @@ any previously constructed objects (such as other tables, strings, vectors).
For structs, you use the appropriate `create` function in-line, as shown
above in the `Monster` example.
+To finish the buffer, call:
+
+ Monster.finishMonsterBuffer(fbb, mon);
+
+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
+not start from offset 0 in this buffer, but from `fbb.dataStart()` (this is
+because the data was built backwards in memory). It ends at `fbb,capacity()`.
+
+
## Text Parsing
There currently is no support for parsing text (Schema's and JSON) directly
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index bab6a4602..0ac4e81f0 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -665,8 +665,8 @@ template const T *GetRoot(const void *buf) {
// Helper to see if the identifier in a buffer has the expected value.
inline bool BufferHasIdentifier(const void *buf, const char *identifier) {
- return strncmp(reinterpret_cast(buf) + 4, identifier,
- FlatBufferBuilder::kFileIdentifierLength) == 0;
+ return strncmp(reinterpret_cast(buf) + sizeof(uoffset_t),
+ identifier, FlatBufferBuilder::kFileIdentifierLength) == 0;
}
// Helper class to verify the integrity of a FlatBuffer
diff --git a/java/flatbuffers/Constants.java b/java/flatbuffers/Constants.java
new file mode 100644
index 000000000..aeb22cc1f
--- /dev/null
+++ b/java/flatbuffers/Constants.java
@@ -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.
+ */
+
+package flatbuffers;
+
+// Class that holds shared constants.
+
+public class Constants {
+ // Java doesn't seem to have these.
+ static final int SIZEOF_SHORT = 2;
+ static final int SIZEOF_INT = 4;
+ static final int FILE_IDENTIFIER_LENGTH = 4;
+}
+
diff --git a/java/flatbuffers/FlatBufferBuilder.java b/java/flatbuffers/FlatBufferBuilder.java
index 50adeb510..a1bc97487 100755
--- a/java/flatbuffers/FlatBufferBuilder.java
+++ b/java/flatbuffers/FlatBufferBuilder.java
@@ -25,7 +25,7 @@ import java.nio.charset.Charset;
// Class that helps you build a FlatBuffer.
// See the section "Use in Java" in the main FlatBuffers documentation.
-public class FlatBufferBuilder {
+public class FlatBufferBuilder extends Constants {
ByteBuffer bb; // Where we construct the FlatBuffer.
int space; // Remaining space in the ByteBuffer.
static final Charset utf8charset = Charset.forName("UTF-8");
@@ -36,10 +36,6 @@ public class FlatBufferBuilder {
int num_vtables = 0; // Number of entries in `vtables` in use.
int vector_num_elems = 0; // For the current vector being built.
- // Java doesn't seem to have these.
- final int SIZEOF_SHORT = 2;
- final int SIZEOF_INT = 4;
-
// Start with a buffer of size `initial_size`, then grow as required.
public FlatBufferBuilder(int initial_size) {
if (initial_size <= 0) initial_size = 1;
@@ -251,6 +247,17 @@ public class FlatBufferBuilder {
addOffset(root_table);
}
+ public void finish(int root_table, String file_identifier) {
+ prep(minalign, SIZEOF_INT + FILE_IDENTIFIER_LENGTH);
+ if (file_identifier.length() != FILE_IDENTIFIER_LENGTH)
+ throw new AssertionError("FlatBuffers: file identifier must be length " +
+ FILE_IDENTIFIER_LENGTH);
+ for (int i = FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) {
+ addByte((byte)file_identifier.charAt(i));
+ }
+ addOffset(root_table);
+ }
+
public ByteBuffer dataBuffer() { return bb; }
// The FlatBuffer data doesn't start at offset 0 in the ByteBuffer:
diff --git a/java/flatbuffers/Table.java b/java/flatbuffers/Table.java
index f25188aef..7740e49fd 100755
--- a/java/flatbuffers/Table.java
+++ b/java/flatbuffers/Table.java
@@ -21,12 +21,10 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
// All tables in the generated code derive from this class, and add their own accessors.
-public class Table {
+public class Table extends Constants {
protected int bb_pos;
protected ByteBuffer bb;
- final int SIZEOF_INT = 4;
-
// 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 vtable_offset) {
@@ -75,4 +73,14 @@ public class Table {
t.bb = bb;
return t;
}
+
+ protected static boolean __has_identifier(ByteBuffer bb, int offset, String ident) {
+ if (ident.length() != FILE_IDENTIFIER_LENGTH)
+ throw new AssertionError("FlatBuffers: file identifier must be length " +
+ FILE_IDENTIFIER_LENGTH);
+ for (int i = 0; i < FILE_IDENTIFIER_LENGTH; i++) {
+ if (ident.charAt(i) != (char)bb.get(offset + SIZEOF_INT + i)) return false;
+ }
+ return true;
+ }
}
diff --git a/src/idl_gen_java.cpp b/src/idl_gen_java.cpp
index 7c57ffbeb..8ee42006d 100755
--- a/src/idl_gen_java.cpp
+++ b/src/idl_gen_java.cpp
@@ -154,7 +154,8 @@ static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
}
}
-static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
+static void GenStruct(const Parser &parser, StructDef &struct_def,
+ std::string *code_ptr) {
if (struct_def.generated) return;
std::string &code = *code_ptr;
@@ -177,12 +178,21 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
code += "_bb.order(ByteOrder.LITTLE_ENDIAN); ";
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 boolean " + 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";
+ 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) {
@@ -321,6 +331,14 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
}
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";
}
@@ -375,7 +393,7 @@ bool GenerateJava(const Parser &parser,
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
std::string declcode;
- GenStruct(**it, &declcode);
+ GenStruct(parser, **it, &declcode);
if (!SaveClass(parser, **it, declcode, path, true))
return false;
}
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index ed4476dba..af44a9324 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -914,7 +914,8 @@ bool Parser::Parse(const char *source, const char *filepath) {
if (builder_.GetSize()) {
Error("cannot have more than one json object in a file");
}
- builder_.Finish(Offset(ParseTable(*root_struct_def)));
+ builder_.Finish(Offset(ParseTable(*root_struct_def)),
+ file_identifier_.length() ? file_identifier_.c_str() : nullptr);
} else if (token_ == kTokenEnum) {
ParseEnum(false);
} else if (token_ == kTokenUnion) {
diff --git a/tests/JavaTest.java b/tests/JavaTest.java
index 862659e81..4747c44f3 100755
--- a/tests/JavaTest.java
+++ b/tests/JavaTest.java
@@ -85,7 +85,7 @@ class JavaTest {
Monster.addTestarrayofstring(fbb, testArrayOfString);
int mon = Monster.endMonster(fbb);
- fbb.finish(mon);
+ Monster.finishMonsterBuffer(fbb, mon);
// Write the result to a file for debugging purposes:
// Note that the binaries are not necessarily identical, since the JSON
@@ -113,6 +113,8 @@ class JavaTest {
}
static void TestBuffer(ByteBuffer bb, int start) {
+ TestEq(Monster.MonsterBufferHasIdentifier(bb, start), true);
+
Monster monster = Monster.getRootAsMonster(bb, start);
TestEq(monster.hp(), (short)80);
diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java
index 68da9d894..76f94760e 100755
--- a/tests/MyGame/Example/Monster.java
+++ b/tests/MyGame/Example/Monster.java
@@ -9,7 +9,9 @@ import flatbuffers.*;
public class Monster extends Table {
public static Monster getRootAsMonster(ByteBuffer _bb, int offset) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (new Monster()).__init(_bb.getInt(offset) + offset, _bb); }
+ public static boolean 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) : 150; }
@@ -57,5 +59,6 @@ public class Monster extends Table {
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.java b/tests/MyGame/Example/Test.java
index 93261f967..fde24e655 100755
--- a/tests/MyGame/Example/Test.java
+++ b/tests/MyGame/Example/Test.java
@@ -9,6 +9,7 @@ import flatbuffers.*;
public class Test extends 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); }
diff --git a/tests/MyGame/Example/Vec3.java b/tests/MyGame/Example/Vec3.java
index 652cba946..be5c66b34 100755
--- a/tests/MyGame/Example/Vec3.java
+++ b/tests/MyGame/Example/Vec3.java
@@ -9,6 +9,7 @@ import flatbuffers.*;
public class Vec3 extends 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); }
diff --git a/tests/monsterdata_test.bin b/tests/monsterdata_test.bin
index 952a96bef..4283d59c9 100644
Binary files a/tests/monsterdata_test.bin and b/tests/monsterdata_test.bin differ
diff --git a/tests/monsterdata_test.json b/tests/monsterdata_test.json
index c905fe4e5..a738caec4 100755
--- a/tests/monsterdata_test.json
+++ b/tests/monsterdata_test.json
@@ -32,5 +32,9 @@
a: 30,
b: 40
}
+ ],
+ testarrayofstring: [
+ "test1",
+ "test2"
]
}