mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 04:04:19 +00:00
added vector mutators
This commit is contained in:
@@ -177,3 +177,48 @@ There currently is no support for parsing text (Schema's and JSON) directly
|
||||
from Java or C#, though you could use the C++ parser through native call
|
||||
interfaces available to each language. Please see the
|
||||
C++ documentation for more on text parsing.
|
||||
|
||||
### Mutating FlatBuffers
|
||||
|
||||
As you saw above, typically once you have created a FlatBuffer, it is
|
||||
read-only from that moment on. There are however cases where you have just
|
||||
received a FlatBuffer, and you'd like to modify something about it before
|
||||
sending it on to another recipient. With the above functionality, you'd have
|
||||
to generate an entirely new FlatBuffer, while tracking what you modify in your
|
||||
own data structures. This is inconvenient.
|
||||
|
||||
For this reason FlatBuffers can also be mutated in-place. While this is great
|
||||
for making small fixes to an existing buffer, you generally want to create
|
||||
buffers from scratch whenever possible, since it is much more efficient and
|
||||
the API is much more general purpose.
|
||||
|
||||
To get non-const accessors, invoke `flatc` with `--gen-mutable`.
|
||||
|
||||
You now can:
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java}
|
||||
Monster monster = Monster.getRootAsMonster(bb);
|
||||
monster.mutateHp(10); // Set table field.
|
||||
monster.pos().mutateZ(4); // Set struct field.
|
||||
monster.mutateInventory(0, 1); // Set vector element.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We use the somewhat verbose term `mutate` instead of `set` to indicate that
|
||||
this is a special use case, not to be confused with the default way of
|
||||
constructing FlatBuffer data.
|
||||
|
||||
After the above mutations, you can send on the FlatBuffer to a new recipient
|
||||
without any further work!
|
||||
|
||||
Note that any `mutate` functions on tables return a boolean, which is false
|
||||
if the field we're trying to set isn't present in the buffer. Fields are not
|
||||
present if they weren't set, or even if they happen to be equal to the
|
||||
default value. For example, in the creation code above we set the `mana` field
|
||||
to `150`, which is the default value, so it was never stored in the buffer.
|
||||
Trying to call mutateMana() on such data will return false, and the value won't
|
||||
actually be modified!
|
||||
|
||||
One way to solve this is to call `forceDefaults()` on a
|
||||
`FlatBufferBuilder` to force all fields you set to actually be written. This
|
||||
of course increases the size of the buffer somewhat, but this may be
|
||||
acceptable for a mutable buffer.
|
||||
|
||||
@@ -252,7 +252,7 @@ static Type DestinationType(const LanguageParameters &lang, const Type &type,
|
||||
bool vectorelem) {
|
||||
if (lang.language != GeneratorOptions::kJava) return type;
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT);
|
||||
case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT); //intentionally returns int to avoid unnecessary casting in Java
|
||||
case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT);
|
||||
case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG);
|
||||
case BASE_TYPE_VECTOR:
|
||||
@@ -367,21 +367,25 @@ static std::string DestinationValue(const LanguageParameters &lang,
|
||||
// In C#, one cast directly cast an Enum to its underlying type, which is essential before putting it onto the buffer.
|
||||
static std::string SourceCast(const LanguageParameters &lang,
|
||||
const Type &type) {
|
||||
switch (lang.language) {
|
||||
case GeneratorOptions::kJava:
|
||||
if (type.base_type == BASE_TYPE_UINT) return "(int)";
|
||||
else if (type.base_type == BASE_TYPE_USHORT) return "(short)";
|
||||
else if (type.base_type == BASE_TYPE_UCHAR) return "(byte)";
|
||||
break;
|
||||
case GeneratorOptions::kCSharp:
|
||||
if (type.enum_def != nullptr &&
|
||||
type.base_type != BASE_TYPE_UNION)
|
||||
return "(" + GenTypeGet(lang, type) + ")";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (type.base_type == BASE_TYPE_VECTOR) {
|
||||
return SourceCast(lang, type.VectorType());
|
||||
} else {
|
||||
switch (lang.language) {
|
||||
case GeneratorOptions::kJava:
|
||||
if (type.base_type == BASE_TYPE_UINT) return "(int)";
|
||||
else if (type.base_type == BASE_TYPE_USHORT) return "(short)";
|
||||
else if (type.base_type == BASE_TYPE_UCHAR) return "(byte)";
|
||||
break;
|
||||
case GeneratorOptions::kCSharp:
|
||||
if (type.enum_def != nullptr &&
|
||||
type.base_type != BASE_TYPE_UNION)
|
||||
return "(" + GenTypeGet(lang, type) + ")";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string GenDefaultValue(const LanguageParameters &lang, const Value &value, bool for_buffer) {
|
||||
@@ -640,6 +644,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
|
||||
std::string src_cast = SourceCast(lang, field.value.type);
|
||||
std::string method_start = " public " + type_name_dest + " " +
|
||||
MakeCamel(field.name, lang.first_camel_upper);
|
||||
|
||||
// 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(" +
|
||||
@@ -784,24 +789,37 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
|
||||
code += "); }\n";
|
||||
}
|
||||
|
||||
// generate mutators for scalar fields
|
||||
// generate mutators for scalar fields or vectors of scalars
|
||||
if (opts.mutable_buffer) {
|
||||
auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR
|
||||
? field.value.type.VectorType()
|
||||
: field.value.type;
|
||||
// boolean parameters have to be explicitly converted to byte representation
|
||||
std::string setter_parameter = field.value.type.base_type == BASE_TYPE_BOOL ? "(byte)(" + field.name + " ? 1 : 0)" : field.name;
|
||||
std::string mutator_prefix = MakeCamel("mutate", lang.first_camel_upper);
|
||||
if (IsScalar(field.value.type.base_type)) {
|
||||
auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL ? "(byte)(" + field.name + " ? 1 : 0)" : field.name;
|
||||
auto mutator_prefix = MakeCamel("mutate", lang.first_camel_upper);
|
||||
//a vector mutator also needs the index of the vector element it should mutate
|
||||
auto mutator_params = (field.value.type.base_type == BASE_TYPE_VECTOR ? "(int j, " : "(") +
|
||||
GenTypeNameDest(lang, underlying_type) + " " +
|
||||
field.name + ") { ";
|
||||
auto setter_index = field.value.type.base_type == BASE_TYPE_VECTOR
|
||||
? "__vector(o) + j * " + NumToString(InlineSize(underlying_type))
|
||||
: (struct_def.fixed ? "bb_pos + " + NumToString(field.value.offset) : "o + bb_pos");
|
||||
|
||||
|
||||
if (IsScalar(field.value.type.base_type) ||
|
||||
(field.value.type.base_type == BASE_TYPE_VECTOR &&
|
||||
IsScalar(field.value.type.VectorType().base_type))) {
|
||||
code += " public ";
|
||||
code += struct_def.fixed ? "void " : lang.bool_type;
|
||||
code += mutator_prefix + MakeCamel(field.name, true) + "(";
|
||||
code += GenTypeNameDest(lang, field.value.type);
|
||||
code += " " + field.name + ") { ";
|
||||
code += mutator_prefix + MakeCamel(field.name, true);
|
||||
code += mutator_params;
|
||||
if (struct_def.fixed) {
|
||||
code += GenSetter(lang, field.value.type) + "(bb_pos + ";
|
||||
code += NumToString(field.value.offset) + ", " + src_cast + setter_parameter + "); }\n";
|
||||
code += GenSetter(lang, underlying_type) + "(" + setter_index + ", ";
|
||||
code += src_cast + setter_parameter + "); }\n";
|
||||
} else {
|
||||
code += "int o = __offset(" + NumToString(field.value.offset) + ");";
|
||||
code += " if (o != 0) { " + GenSetter(lang, field.value.type);
|
||||
code += "(o + bb_pos, " + src_cast + setter_parameter + "); return true; } else { return false; } }\n";
|
||||
code += " if (o != 0) { " + GenSetter(lang, underlying_type);
|
||||
code += "(" + setter_index + ", " + src_cast + setter_parameter + "); return true; } else { return false; } }\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,25 @@ namespace FlatBuffers.Test
|
||||
Assert.AreEqual(monster.MutateTestType(Any.Monster), true);
|
||||
Assert.AreEqual(monster.TestType, Any.Monster);
|
||||
|
||||
//mutate the inventory vector
|
||||
Assert.AreEqual(monster.MutateInventory(0, 1), true);
|
||||
Assert.AreEqual(monster.MutateInventory(1, 2), true);
|
||||
Assert.AreEqual(monster.MutateInventory(2, 3), true);
|
||||
Assert.AreEqual(monster.MutateInventory(3, 4), true);
|
||||
Assert.AreEqual(monster.MutateInventory(4, 5), true);
|
||||
|
||||
for (int i = 0; i < monster.InventoryLength; i++)
|
||||
{
|
||||
Assert.AreEqual(monster.GetInventory(i), i + 1);
|
||||
}
|
||||
|
||||
//reverse mutation
|
||||
Assert.AreEqual(monster.MutateInventory(0, 0), true);
|
||||
Assert.AreEqual(monster.MutateInventory(1, 1), true);
|
||||
Assert.AreEqual(monster.MutateInventory(2, 2), true);
|
||||
Assert.AreEqual(monster.MutateInventory(3, 3), true);
|
||||
Assert.AreEqual(monster.MutateInventory(4, 4), true);
|
||||
|
||||
// get a struct field and edit one of its fields
|
||||
Vec3 pos = monster.Pos;
|
||||
Assert.AreEqual(pos.X, 1.0f);
|
||||
|
||||
@@ -127,6 +127,24 @@ class JavaTest {
|
||||
TestEq(monster.mutateTestType(Any.Monster), true);
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
|
||||
//mutate the inventory vector
|
||||
TestEq(monster.mutateInventory(0, 1), true);
|
||||
TestEq(monster.mutateInventory(1, 2), true);
|
||||
TestEq(monster.mutateInventory(2, 3), true);
|
||||
TestEq(monster.mutateInventory(3, 4), true);
|
||||
TestEq(monster.mutateInventory(4, 5), true);
|
||||
|
||||
for (int i = 0; i < monster.inventoryLength(); i++) {
|
||||
TestEq(monster.inventory(i), i + 1);
|
||||
}
|
||||
|
||||
//reverse mutation
|
||||
TestEq(monster.mutateInventory(0, 0), true);
|
||||
TestEq(monster.mutateInventory(1, 1), true);
|
||||
TestEq(monster.mutateInventory(2, 2), true);
|
||||
TestEq(monster.mutateInventory(3, 3), true);
|
||||
TestEq(monster.mutateInventory(4, 4), true);
|
||||
|
||||
// get a struct field and edit one of its fields
|
||||
Vec3 pos = monster.pos();
|
||||
TestEq(pos.x(), 1.0f);
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed class Monster : Table {
|
||||
public string Name { get { int o = __offset(10); return o != 0 ? __string(o + bb_pos) : null; } }
|
||||
public byte GetInventory(int j) { int o = __offset(14); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; }
|
||||
public int InventoryLength { get { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; } }
|
||||
public bool MutateInventory(int j, byte inventory) { int o = __offset(14); if (o != 0) { bb.Put(__vector(o) + j * 1, inventory); return true; } else { return false; } }
|
||||
public Color Color { get { int o = __offset(16); return o != 0 ? (Color)bb.GetSbyte(o + bb_pos) : (Color)8; } }
|
||||
public bool MutateColor(Color color) { int o = __offset(16); if (o != 0) { bb.PutSbyte(o + bb_pos, (sbyte)color); return true; } else { return false; } }
|
||||
public Any TestType { get { int o = __offset(18); return o != 0 ? (Any)bb.Get(o + bb_pos) : (Any)0; } }
|
||||
@@ -39,6 +40,7 @@ public sealed class Monster : Table {
|
||||
public Monster GetEnemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; }
|
||||
public byte GetTestnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; }
|
||||
public int TestnestedflatbufferLength { get { int o = __offset(30); return o != 0 ? __vector_len(o) : 0; } }
|
||||
public bool MutateTestnestedflatbuffer(int j, byte testnestedflatbuffer) { int o = __offset(30); if (o != 0) { bb.Put(__vector(o) + j * 1, testnestedflatbuffer); return true; } else { return false; } }
|
||||
public Stat Testempty { get { return GetTestempty(new Stat()); } }
|
||||
public Stat GetTestempty(Stat obj) { int o = __offset(32); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; }
|
||||
public bool Testbool { get { int o = __offset(34); return o != 0 ? 0!=bb.Get(o + bb_pos) : (bool)false; } }
|
||||
|
||||
@@ -24,6 +24,7 @@ public final class Monster extends Table {
|
||||
public int inventory(int j) { int o = __offset(14); return o != 0 ? bb.get(__vector(o) + j * 1) & 0xFF : 0; }
|
||||
public int inventoryLength() { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; }
|
||||
public ByteBuffer inventoryAsByteBuffer() { return __vector_as_bytebuffer(14, 1); }
|
||||
public boolean mutateInventory(int j, int inventory) { int o = __offset(14); if (o != 0) { bb.put(__vector(o) + j * 1, (byte)inventory); return true; } else { return false; } }
|
||||
public byte color() { int o = __offset(16); return o != 0 ? bb.get(o + bb_pos) : 8; }
|
||||
public boolean mutateColor(byte color) { int o = __offset(16); if (o != 0) { bb.put(o + bb_pos, color); return true; } else { return false; } }
|
||||
public byte testType() { int o = __offset(18); return o != 0 ? bb.get(o + bb_pos) : 0; }
|
||||
@@ -46,6 +47,7 @@ public final class Monster extends Table {
|
||||
public int testnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.get(__vector(o) + j * 1) & 0xFF : 0; }
|
||||
public int testnestedflatbufferLength() { int o = __offset(30); return o != 0 ? __vector_len(o) : 0; }
|
||||
public ByteBuffer testnestedflatbufferAsByteBuffer() { return __vector_as_bytebuffer(30, 1); }
|
||||
public boolean mutateTestnestedflatbuffer(int j, int testnestedflatbuffer) { int o = __offset(30); if (o != 0) { bb.put(__vector(o) + j * 1, (byte)testnestedflatbuffer); return true; } else { return false; } }
|
||||
public Stat testempty() { return testempty(new Stat()); }
|
||||
public Stat testempty(Stat obj) { int o = __offset(32); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; }
|
||||
public boolean testbool() { int o = __offset(34); return o != 0 ? 0!=bb.get(o + bb_pos) : false; }
|
||||
|
||||
Reference in New Issue
Block a user