[libcamera-devel] [PATCH v6 2/9] utils: ipc: add templates for code generation for IPC mechanism
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Tue Feb 2 02:25:29 CET 2021
Hi Paul,
On Fri, Jan 15, 2021 at 02:11:41PM +0900, paul.elder at ideasonboard.com wrote:
> Hi Kieran,
>
> <snip>
>
> > > diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > > new file mode 100644
> > > index 00000000..af4800bf
> > > --- /dev/null
> > > +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > > @@ -0,0 +1,313 @@
> > > +{#-
> > > + # SPDX-License-Identifier: LGPL-2.1-or-later
> > > + # Copyright (C) 2020, Google Inc.
> > > +-#}
> > > +{# Turn this into a C macro? #}
> >
> > Is this a todo?
>
> It was more of a question,
>
> > > +{#
> > > + # \brief Verify that there is enough bytes to deserialize
> > > + #
> > > + # Generate code that verifies that \a size is not greater than \a dataSize.
> > > + # Otherwise log an error with \a name and \a typename.
> > > + #}
> > > +{%- macro check_data_size(size, dataSize, name, typename) %}
> > > + if ({{dataSize}} < {{size}}) {
> > > + LOG(IPADataSerializer, Error)
> > > + << "Failed to deserialize " << "{{name}}"
> > > + << ": not enough {{typename}}, expected "
> > > + << ({{size}}) << ", got " << ({{dataSize}});
> > > + return ret;
> > > + }
> > > +{%- endmacro %}
>
> because this block is kind of big, so I wasn't sure if this shold be
> generated at every check, or if this should be a macro and put the macro
> at every generated check. In either case it's generated.
>
> > > +
> > > +
> > > +{#
> > > + # \brief Serialize some field into return vector
> >
> > 'some' field?
> > a field?
>
> Mathematics vocabulary :p
>
> Yeah, I'll change it.
>
> > > + #
> > > + # Generate code to serialize \a field into retData, including size of the
> > > + # field and fds (where appropriate).
> > > + # This code is meant to be used by the IPADataSerializer specialization.
> > > + #
> > > + # \todo Avoid intermediate vectors
> > > + #}
> > > +{%- macro serializer_field(field, namespace, loop) %}
> > > +{%- if field|is_pod or field|is_enum %}
> > > + std::vector<uint8_t> {{field.mojom_name}};
> > > + std::tie({{field.mojom_name}}, std::ignore) =
> > > + {%- if field|is_pod %}
> > > + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
> > > + {%- elif field|is_enum %}
> > > + IPADataSerializer<uint{{field|bit_width}}_t>::serialize(data.{{field.mojom_name}});
> > > + {%- endif %}
> > > + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > > +{%- elif field|is_fd %}
> > > + std::vector<uint8_t> {{field.mojom_name}};
> > > + std::vector<int32_t> {{field.mojom_name}}Fds;
> > > + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> > > + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
> > > + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > > + retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
> > > +{%- elif field|is_controls %}
> > > + if (data.{{field.mojom_name}}.size() > 0) {
> > > + std::vector<uint8_t> {{field.mojom_name}};
> > > + std::tie({{field.mojom_name}}, std::ignore) =
> > > + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}, cs);
> > > + appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
> > > + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > > + } else {
> > > + appendPOD<uint32_t>(retData, 0);
> > > + }
> > > +{%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> > > + std::vector<uint8_t> {{field.mojom_name}};
> > > + {%- if field|has_fd %}
> > > + std::vector<int32_t> {{field.mojom_name}}Fds;
> > > + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> > > + {%- else %}
> > > + std::tie({{field.mojom_name}}, std::ignore) =
> > > + {%- endif %}
> > > + {%- if field|is_array or field|is_map %}
> > > + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}, cs);
> > > + {%- elif field|is_str %}
> > > + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
> > > + {%- else %}
> > > + IPADataSerializer<{{field|name_full(namespace)}}>::serialize(data.{{field.mojom_name}}, cs);
> > > + {%- endif %}
> > > + appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
> > > + {%- if field|has_fd %}
> > > + appendPOD<uint32_t>(retData, {{field.mojom_name}}Fds.size());
> > > + {%- endif %}
> > > + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > > + {%- if field|has_fd %}
> > > + retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
> > > + {%- endif %}
> > > +{%- else %}
> > > + /* Unknown serialization for {{field.mojom_name}}. */
> > > +{%- endif %}
> > > +{%- endmacro %}
> > > +
> > > +
> > > +{#
> > > + # \brief Deserialize some field into return struct
> >
> > Again, 'some field' sounds like a random field.
> > Presumably this is going to handle a quite specific one each time...
> >
> >
> > > + #
> > > + # Generate code to deserialize \a field into object ret.
> > > + # This code is meant to be used by the IPADataSerializer specialization.
> > > + #}
> > > +{%- macro deserializer_field(field, namespace, loop) %}
> > > +{% if field|is_pod or field|is_enum %}
> > > + {%- set field_size = (field|bit_width|int / 8)|int %}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> > > + {%- if field|is_pod %}
> > > + ret.{{field.mojom_name}} = IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field_size}});
> > > + {%- else %}
> > > + ret.{{field.mojom_name}} = static_cast<{{field|name_full(namespace)}}>(IPADataSerializer<uint{{field|bit_width}}_t>::deserialize(m, m + {{field_size}}));
> > > + {%- endif %}
> > > + {%- if not loop.last %}
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {%- endif %}
> > > +{% elif field|is_fd %}
> > > + {%- set field_size = 1 %}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> > > + ret.{{field.mojom_name}} = IPADataSerializer<{{field|name}}>::deserialize(m, m + 1, n, n + 1, cs);
> > > + {%- if not loop.last %}
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + n += ret.{{field.mojom_name}}.isValid() ? 1 : 0;
> > > + fdsSize -= ret.{{field.mojom_name}}.isValid() ? 1 : 0;
> > > + {%- endif %}
> > > +{% elif field|is_controls %}
> > > + {%- set field_size = 4 %}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
> > > + const size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, dataEnd);
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {%- set field_size = field.mojom_name + 'Size' -%}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> > > + if ({{field.mojom_name}}Size > 0)
> > > + ret.{{field.mojom_name}} =
> > > + IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
> > > + {%- if not loop.last %}
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {%- endif %}
> > > +{% elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> > > + {%- set field_size = 4 %}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
> > > + const size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, dataEnd);
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {%- if field|has_fd %}
> > > + {%- set field_size = 4 %}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'FdsSize', 'data')}}
> > > + const size_t {{field.mojom_name}}FdsSize = readPOD<uint32_t>(m, 0, dataEnd);
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {{- check_data_size(field.mojom_name + 'FdsSize', 'fdsSize', field.mojom_name, 'fds')}}
> > > + {%- endif %}
> > > + {%- set field_size = field.mojom_name + 'Size' -%}
> > > + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> > > + ret.{{field.mojom_name}} =
> > > + {%- if field|is_str %}
> > > + IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size);
> > > + {%- elif field|has_fd and (field|is_array or field|is_map) %}
> > > + IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
> > > + {%- elif field|has_fd and (not (field|is_array or field|is_map)) %}
> > > + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m, m + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
> > > + {%- elif (not field|has_fd) and (field|is_array or field|is_map) %}
> > > + IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
> > > + {%- else %}
> > > + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
> > > + {%- endif %}
> > > + {%- if not loop.last %}
> > > + m += {{field_size}};
> > > + dataSize -= {{field_size}};
> > > + {%- if field|has_fd %}
> > > + n += {{field.mojom_name}}FdsSize;
> > > + fdsSize -= {{field.mojom_name}}FdsSize;
> > > + {%- endif %}
> > > + {%- endif %}
> > > +{% else %}
> > > + /* Unknown deserialization for {{field.mojom_name}}. */
> > > +{%- endif %}
> > > +{%- endmacro %}
> > > +
> > > +
> > > +{#
> > > + # \brief Serialize a struct
> > > + #
> > > + # Generate code for IPADataSerializer specialization, for serializing
> > > + # \a struct.
> > > + #}
> > > +{%- macro serializer(struct, namespace) %}
> > > + static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > + serialize(const {{struct|name_full(namespace)}} &data,
> > > +{%- if struct|needs_control_serializer %}
> > > + ControlSerializer *cs)
> > > +{%- else %}
> > > + [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +{%- endif %}
> > > + {
> > > + std::vector<uint8_t> retData;
> > > +{%- if struct|has_fd %}
> > > + std::vector<int32_t> retFds;
> > > +{%- endif %}
> > > +{%- for field in struct.fields %}
> > > +{{serializer_field(field, namespace, loop)}}
> > > +{%- endfor %}
> > > +{% if struct|has_fd %}
> > > + return {retData, retFds};
> > > +{%- else %}
> > > + return {retData, {}};
> > > +{%- endif %}
> > > + }
> > > +{%- endmacro %}
> > > +
> > > +
> > > +{#
> > > + # \brief Deserialize a struct that has fds
> > > + #
> > > + # Generate code for IPADataSerializer specialization, for deserializing
> > > + # \a struct, in the case that \a struct has file descriptors.
> > > + # fd parameters
> > > + #}
> > > +{%- macro deserializer_fd(struct, namespace) %}
> > > + static {{struct|name_full(namespace)}}
> > > + deserialize(std::vector<uint8_t> &data,
> > > + std::vector<int32_t> &fds,
> > > +{%- if struct|needs_control_serializer %}
> > > + ControlSerializer *cs)
> > > +{%- else %}
> > > + [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +{%- endif %}
> > > + {
> > > + return IPADataSerializer<{{struct|name_full(namespace)}}>::deserialize(data.cbegin(), data.cend(), fds.cbegin(), fds.cend(), cs);
> > > + }
> > > +
> > > + static {{struct|name_full(namespace)}}
> > > + deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > > + std::vector<uint8_t>::const_iterator dataEnd,
> > > + std::vector<int32_t>::const_iterator fdsBegin,
> > > + std::vector<int32_t>::const_iterator fdsEnd,
> > > +{%- if struct|needs_control_serializer %}
> > > + ControlSerializer *cs)
> > > +{%- else %}
> > > + [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +{%- endif %}
> > > + {
> > > + {{struct|name_full(namespace)}} ret;
> > > + std::vector<uint8_t>::const_iterator m = dataBegin;
> > > + std::vector<int32_t>::const_iterator n = fdsBegin;
> > > +
> > > + size_t dataSize = std::distance(dataBegin, dataEnd);
> > > + [[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
> > > +{%- for field in struct.fields -%}
> > > +{{deserializer_field(field, namespace, loop)}}
> > > +{%- endfor %}
> > > + return ret;
> > > + }
> > > +{%- endmacro %}
> > > +
> > > +{#
> > > + # \brief Deserialize a struct that has fds, using non-fd
> > > + #
> > > + # Generate code for IPADataSerializer specialization, for deserializing
> > > + # \a struct, in the case that \a struct has no file descriptors but requires
> > > + # deserializers with file descriptors.
> >
> > If the struct has no file descriptors, why does it need a deserialiszer
> > with file descriptors?
>
> It's for structs that are generated from core.mojom, to allow them to be
> embedded in arrays/maps. The array/map (de)serializer has no way of
> knowing if the struct it's (de)serializing has fds or not, so any struct
> that will be contained in an array/map must have (de)serializers defined
> for fds.
>
> That's why the hand-written (de)serializer implementations for
> arithmetic types and ControlList and ControlInfoMap also have them.
>
> It's actually also required for structs that are generated in the
> per-pipeline mojom files.
Would it make sense to pass the fds to all (de)serialization functions,
unconditionally, to lower the complexity ? Up to you.
> > Other than the documentation comments, I don't think I can hope to
> > provide much more parsing of all this.
> >
> > Acked-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list