Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
F
flatbuffers
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
flatbuffers
Commits
7bfed4b2
Commit
7bfed4b2
authored
Aug 15, 2015
by
Maor Itzkovitch
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added vector mutators
parent
b062af4c
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
130 additions
and
26 deletions
+130
-26
JavaUsage.md
docs/source/JavaUsage.md
+45
-0
idl_gen_general.cpp
src/idl_gen_general.cpp
+44
-26
FlatBuffersExampleTests.cs
tests/FlatBuffers.Test/FlatBuffersExampleTests.cs
+19
-0
JavaTest.java
tests/JavaTest.java
+18
-0
Monster.cs
tests/MyGame/Example/Monster.cs
+2
-0
Monster.java
tests/MyGame/Example/Monster.java
+2
-0
No files found.
docs/source/JavaUsage.md
View file @
7bfed4b2
...
...
@@ -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.
src/idl_gen_general.cpp
View file @
7bfed4b2
...
...
@@ -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
"
;
}
}
}
...
...
tests/FlatBuffers.Test/FlatBuffersExampleTests.cs
View file @
7bfed4b2
...
...
@@ -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
);
...
...
tests/JavaTest.java
View file @
7bfed4b2
...
...
@@ -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
);
...
...
tests/MyGame/Example/Monster.cs
View file @
7bfed4b2
...
...
@@ -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
;
}
}
...
...
tests/MyGame/Example/Monster.java
View file @
7bfed4b2
...
...
@@ -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
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment