mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-09 06:30:54 +00:00
* Documentation updates for Optional Scalars * Updated Support * Reword stuff * s/NULL/null Co-authored-by: Casper Neo <cneo@google.com>
This commit is contained in:
@@ -39,16 +39,16 @@ first:
|
||||
|
||||
### Tables
|
||||
|
||||
Tables are the main way of defining objects in FlatBuffers, and consist
|
||||
of a name (here `Monster`) and a list of fields. Each field has a name,
|
||||
a type, and optionally a default value (if omitted, it defaults to `0` /
|
||||
`NULL`).
|
||||
Tables are the main way of defining objects in FlatBuffers, and consist of a
|
||||
name (here `Monster`) and a list of fields. Each field has a name, a type, and
|
||||
optionally a default value. If the default value is not specified in the schema,
|
||||
it will be `0` for scalar types, or `null` for other types. Some languages
|
||||
support setting a scalar's default to `null`. This makes the scalar optional.
|
||||
|
||||
Each field is optional: It does not have to appear in the wire
|
||||
representation, and you can choose to omit fields for each individual
|
||||
object. As a result, you have the flexibility to add fields without fear of
|
||||
bloating your data. This design is also FlatBuffer's mechanism for forward
|
||||
and backwards compatibility. Note that:
|
||||
Fields do not have to appear in the wire representation, and you can choose
|
||||
to omit fields when constructing an object. You have the flexibility to add
|
||||
fields without fear of bloating your data. This design is also FlatBuffer's
|
||||
mechanism for forward and backwards compatibility. Note that:
|
||||
|
||||
- You can add new fields in the schema ONLY at the end of a table
|
||||
definition. Older data will still
|
||||
@@ -135,24 +135,35 @@ Both representations are binary equivalent.
|
||||
|
||||
Arrays are currently only supported in a `struct`.
|
||||
|
||||
### (Default) Values
|
||||
### Default, Optional and Required Values
|
||||
|
||||
Values are a sequence of digits. Values may be optionally followed by a decimal
|
||||
point (`.`) and more digits, for float constants, or optionally prefixed by
|
||||
a `-`. Floats may also be in scientific notation; optionally ending with an `e`
|
||||
or `E`, followed by a `+` or `-` and more digits.
|
||||
There are three, mutually exclusive, reactions to the non-presence of a table's
|
||||
field in the binary data:
|
||||
|
||||
1. Default valued fields will return the default value (as defined in the schema).
|
||||
2. Optional valued fields will return some form of `null` depending on the
|
||||
local language. (In a sense, `null` is the default value).
|
||||
3. Required fields will cause an error. Flatbuffer verifiers would
|
||||
consider the whole buffer invalid. See the `required` tag below.
|
||||
|
||||
When writing a schema, values are a sequence of digits. Values may be optionally
|
||||
followed by a decimal point (`.`) and more digits, for float constants, or
|
||||
optionally prefixed by a `-`. Floats may also be in scientific notation;
|
||||
optionally ending with an `e` or `E`, followed by a `+` or `-` and more digits.
|
||||
Values can also be the keyword `null`.
|
||||
|
||||
Only scalar values can have defaults, non-scalar (string/vector/table) fields
|
||||
default to `NULL` when not present.
|
||||
default to `null` when not present.
|
||||
|
||||
You generally do not want to change default values after they're initially
|
||||
defined. Fields that have the default value are not actually stored in the
|
||||
serialized data (see also Gotchas below) but are generated in code,
|
||||
so when you change the default, you'd
|
||||
now get a different value than from code generated from an older version of
|
||||
the schema. There are situations, however, where this may be
|
||||
desirable, especially if you can ensure a simultaneous rebuild of
|
||||
all code.
|
||||
serialized data (see also Gotchas below). Values explicitly written by code
|
||||
generated by the old schema old version, if they happen to be the default, will
|
||||
be read as a different value by code generated with the new schema. This is
|
||||
slightly less bad when converting an optional scalar into a default valued
|
||||
scalar since non-presence would not be overloaded with a previous default value.
|
||||
There are situations, however, where this may be desirable, especially if you
|
||||
can ensure a simultaneous rebuild of all code.
|
||||
|
||||
### Enums
|
||||
|
||||
@@ -325,18 +336,16 @@ Current understood attributes:
|
||||
deprecate a field that was previous required, old code may fail to validate
|
||||
new data (when using the optional verifier).
|
||||
- `required` (on a non-scalar table field): this field must always be set.
|
||||
By default, all fields are optional, i.e. may be left out. This is
|
||||
By default, fields do not need to be present in the binary. This is
|
||||
desirable, as it helps with forwards/backwards compatibility, and
|
||||
flexibility of data structures. It is also a burden on the reading code,
|
||||
since for non-scalar fields it requires you to check against NULL and
|
||||
take appropriate action. By specifying this field, you force code that
|
||||
constructs FlatBuffers to ensure this field is initialized, so the reading
|
||||
code may access it directly, without checking for NULL. If the constructing
|
||||
code does not initialize this field, they will get an assert, and also
|
||||
the verifier will fail on buffers that have missing required fields. Note
|
||||
that if you add this attribute to an existing field, this will only be
|
||||
valid if existing data always contains this field / existing code always
|
||||
writes this field.
|
||||
flexibility of data structures. By specifying this attribute, you make non-
|
||||
presence in an error for both reader and writer. The reading code may access
|
||||
the field directly, without checking for null. If the constructing code does
|
||||
not initialize this field, they will get an assert, and also the verifier
|
||||
will fail on buffers that have missing required fields. Both adding and
|
||||
removing this attribute may be forwards/backwards incompatible as readers
|
||||
will be unable read old or new data, respectively, unless the data happens to
|
||||
always have the field set.
|
||||
- `force_align: size` (on a struct): force the alignment of this struct
|
||||
to be something higher than what it is naturally aligned to. Causes
|
||||
these structs to be aligned to that amount inside a buffer, IF that
|
||||
@@ -486,7 +495,7 @@ of related data structures is a union. Unions do have a cost however,
|
||||
so an alternative to a union is to have a single table that has
|
||||
all the fields of all the data structures you are trying to
|
||||
represent, if they are relatively similar / share many fields.
|
||||
Again, this is efficient because optional fields are cheap.
|
||||
Again, this is efficient because non-present fields are cheap.
|
||||
|
||||
FlatBuffers supports the full range of integer sizes, so try to pick
|
||||
the smallest size needed, rather than defaulting to int/long.
|
||||
@@ -611,22 +620,25 @@ Most serialization formats (e.g. JSON or Protocol Buffers) make it very
|
||||
explicit in the format whether a field is present in an object or not,
|
||||
allowing you to use this as "extra" information.
|
||||
|
||||
In FlatBuffers, this also holds for everything except scalar values.
|
||||
FlatBuffers will not write fields that are equal to their default value,
|
||||
sometimes resulting in significant space savings. However, this also means we
|
||||
cannot disambiguate the meaning of non-presence as "written default value" or
|
||||
"not written at all". This only applies to scalar fields since only they support
|
||||
default values. Unless otherwise specified, their default is 0.
|
||||
|
||||
FlatBuffers by default will not write fields that are equal to the default
|
||||
value (for scalars), sometimes resulting in a significant space savings.
|
||||
|
||||
However, this also means testing whether a field is "present" is somewhat
|
||||
meaningless, since it does not tell you if the field was actually written by
|
||||
calling `add_field` style calls, unless you're only interested in this
|
||||
information for non-default values.
|
||||
If you care about the presence of scalars, most languages support "optional
|
||||
scalars." You can set `null` as the default value in the schema. `null` is a
|
||||
value that's outside of all types, so we will always write if `add_field` is
|
||||
called. The generated field accessor should use the local language's canonical
|
||||
optional type.
|
||||
|
||||
Some `FlatBufferBuilder` implementations have an option called `force_defaults`
|
||||
that circumvents this behavior, and writes fields even if they are equal to
|
||||
the default. You can then use `IsFieldPresent` to query this.
|
||||
that circumvents this "not writing defaults" behavior you can then use
|
||||
`IsFieldPresent` to query presence.
|
||||
|
||||
Another option that works in all languages is to wrap a scalar field in a
|
||||
struct. This way it will return null if it is not present. The cool thing
|
||||
is that structs don't take up any more space than the scalar they represent.
|
||||
struct. This way it will return null if it is not present. This will be slightly
|
||||
less ergonomic but structs don't take up any more space than the scalar they
|
||||
represent.
|
||||
|
||||
[Interface Definition Language]: https://en.wikipedia.org/wiki/Interface_description_language
|
||||
|
||||
Reference in New Issue
Block a user