blob: 390cdf1ba3c63a972677af5031ee3de12f5fb4dd [file] [log] [blame]
#include <utility>
#include <variant>
{{/*
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
*/}}
{{- define "UnionForwardDeclaration" }}
{{ EnsureNamespace . }}
class {{ .Name }};
{{- end }}
{{- define "UnionDeclaration" }}
{{ EnsureNamespace . }}
{{ if .IsResourceType }}
{{- IfdefFuchsia -}}
{{- end }}
{{- .Docs }}
class {{ .Name }} final {
public:
static const fidl_type_t* FidlType;
{{ .Name }}();
~{{ .Name }}();
{{ .Name }}({{ .Name }}&&);
{{ .Name }}& operator=({{ .Name }}&&);
{{ range .Members }}
static {{ $.Name }} With{{ .UpperCamelCaseName }}({{ .Type }}&&);
{{- end }}
{{/* There are two different tag types here:
* fidl_xunion_tag_t: This is an "open" enum that encompasses all possible ordinal values
(including zero). Ordinal() returns a fidl_xunion_tag_t.
* An inner ::Tag enum: This only contains valid ordinals for this xunion. Which() returns a
::Tag.
The two types generally carry the same value. However:
* If the ordinal is zero, which is only ever the case when the xunion is first constructed and
not yet set:
* tag_, which is a fidl_xunion_tag_t, will be 0.
* Ordinal() will return tag_,
* Which() will return Tag::kUnknown.
* if the xunion is non-strict (flexible) and has been de-serialized from a xunion with an
ordinal that's unknown to the client's schema:
* tag_ will be the raw ordinal from the serialized xunion,
* Ordinal() will return tag_,
* Which() will return Tag::kUnknown.
* UnknownBytes() will return a pointer to a valid std::vector<uint8_t> with the unknown bytes.
* if the xunion is a resource type:
* UnknownHandles() will return a pointer to a valid std::vector<zx::handle> with the unknown handles.
*/ -}}
enum __attribute__((enum_extensibility(closed))) {{ .TagEnum.Self }} : fidl_xunion_tag_t {
{{ if .IsFlexible -}}
{{ .TagUnknown.Self }} = 0,
{{ end -}}
{{- range .Members }}
{{ .TagName.Self }} = {{ .Ordinal }}, // {{ .Ordinal | printf "%#x" }}
{{- end }}
Invalid = ::std::numeric_limits<::fidl_union_tag_t>::max(),
};
static inline ::std::unique_ptr<{{ .Name }}> New() { return ::std::make_unique<{{ .Name }}>(); }
void Encode(::fidl::Encoder* encoder, size_t offset,
cpp17::optional<::fidl::HandleInformation> maybe_handle_info = cpp17::nullopt);
static void Decode(::fidl::Decoder* _decoder, {{ .Name }}* value, size_t offset);
zx_status_t Clone({{ .Name }}* result) const;
bool has_invalid_tag() const {
return tag_ == Invalid;
}
{{- range .Members }}
bool is_{{ .Name }}() const { return tag_ == {{ .TagName }}; }
{{ .Docs }}
{{ .Type }}& {{ .Name }}() {
EnsureStorageInitialized({{ .TagName }});
return {{ .StorageName }};
}
{{ .Docs }}
const {{ .Type }}& {{ .Name }}() const {
ZX_ASSERT(is_{{ .Name }}());
return {{ .StorageName }};
}
{{ $.Name }}& set_{{ .Name }}({{ .Type }} value);
{{- end }}
{{- if .IsFlexible }}
{{ .Name }}& SetUnknownData(fidl_xunion_tag_t ordinal, std::vector<uint8_t> bytes{{ if .IsResourceType }}, std::vector<zx::handle> handles{{ end }});
{{- end }}
{{ .TagEnum }} Which() const {
{{ if .IsFlexible }}
switch (tag_) {
case {{ .TagInvalid }}:
{{- range .Members }}
case {{ .TagName }}:
{{- end }}
return {{ .TagEnum }}(tag_);
default:
return {{ .TagUnknown }};
}
{{ else }}
return {{ .TagEnum }}(tag_);
{{ end }}
}
// You probably want to use Which() method instead of Ordinal(). Use Ordinal() only when you need
// access to the raw integral ordinal value.
fidl_xunion_tag_t Ordinal() const {
return tag_;
}
{{- if .IsFlexible }}
const std::vector<uint8_t>* UnknownBytes() const {
if (Which() != {{ .TagUnknown }}) {
return nullptr;
}
{{- if .IsResourceType }}
return &unknown_data_.bytes;
{{- else }}
return &unknown_data_;
{{- end }}
}
{{- if .IsResourceType }}
const std::vector<zx::handle>* UnknownHandles() const {
if (Which() != {{ .TagUnknown }}) {
return nullptr;
}
return &unknown_data_.handles;
}
{{- end }}
{{- end }}
friend ::fidl::Equality<{{ . }}>;
{{- if .Result }}
{{ .Name }}(fpromise::ok_result<{{ .Result.ValueDecl }}>&& result) {
set_response({{ .Result.ValueTypeDecl }}{
{{- if ne 0 .Result.ValueArity }}std::move(result.value){{ end -}}
});
}
{{- end }}
{{- if and .Result .Result.HasError }}
{{ .Name }}(fpromise::error_result<{{ .Result.ErrorDecl }}>&& result) {
set_err(std::move(result.error));
}
{{ .Name }}(fpromise::result<{{ .Result.ValueDecl }}, {{ .Result.ErrorDecl }}>&& result) {
ZX_ASSERT(!result.is_pending());
if (result.is_ok()) {
set_response({{ .Result.ValueTypeDecl }}{
{{- if ne 0 .Result.ValueArity }}result.take_value(){{ end -}}
});
} else {
set_err(result.take_error());
}
}
{{- end }}
{{- if .Result }}
operator fpromise::result<{{ .Result.ValueDecl }}, {{ .Result.CombinedErrorDecl }}>() && {
{{- if and .Result.HasError .Result.HasFrameworkError }}
if (is_err()) {
return fpromise::error({{ .Result.CombinedErrorDecl }}(std::in_place_index<0>, err()));
}
if (is_framework_err()) {
return fpromise::error({{ .Result.CombinedErrorDecl }}(std::in_place_index<1>, framework_err()));
}
{{- else if .Result.HasError }}
if (is_err()) {
return fpromise::error(err());
}
{{- else }}
if (is_framework_err()) {
return fpromise::error(framework_err());
}
{{- end }}
{{- if eq 0 .Result.ValueArity }}
return fpromise::ok();
{{- else if eq 1 .Result.ValueArity }}
{{ .Result.ValueTupleDecl }} value_tuple = std::move(response());
return fpromise::ok(std::move(std::get<0>(value_tuple)));
{{- else }}
return fpromise::ok(std::move(response()));
{{- end }}
}
{{- end }}
private:
void Destroy();
void EnsureStorageInitialized(::fidl_xunion_tag_t tag);
::fidl_xunion_tag_t tag_ = static_cast<fidl_xunion_tag_t>({{ .TagInvalid }});
union {
{{- range .Members }}
{{ .Type }} {{ .StorageName }};
{{- end }}
{{- if .IsFlexible }}
{{ if .IsResourceType }}::fidl::UnknownData{{ else }}std::vector<uint8_t>{{ end }} unknown_data_;
{{- end }}
};
};
inline zx_status_t Clone(const {{ . }}& value,
{{ . }}* result) {
return value.Clone(result);
}
using {{ .Name }}Ptr = ::std::unique_ptr<{{ .Name }}>;
{{- if .IsResourceType }}
{{- EndifFuchsia -}}
{{ end }}
{{- end }}
{{- define "UnionDefinition" }}
{{ EnsureNamespace . }}
{{- if .IsResourceType }}
{{- IfdefFuchsia -}}
{{- end }}
extern "C" const fidl_type_t {{ .CodingTableType.Name }};
const fidl_type_t* {{ .Name }}::FidlType = &{{ .CodingTableType.Name }};
{{ .Name }}::{{ .Name }}() {}
{{ .Name }}::~{{ .Name }}() {
Destroy();
}
{{ .Name }}::{{ .Name }}({{ .Name }}&& other) : tag_(other.tag_) {
switch (tag_) {
{{- range .Members }}
case {{ .TagName }}:
{{- if .Type.NeedsDtor }}
new (&{{ .StorageName }}) {{ .Type }}();
{{- end }}
{{ .StorageName }} = std::move(other.{{ .StorageName }});
break;
{{- end }}
case static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}):
break;
{{- if .IsFlexible }}
default:
new (&unknown_data_) decltype(unknown_data_);
unknown_data_ = std::move(other.unknown_data_);
break;
{{- end }}
}
}
{{ .Name }}& {{ .Name }}::operator=({{ .Name }}&& other) {
if (this != &other) {
Destroy();
tag_ = other.tag_;
switch (tag_) {
{{- range .Members }}
case {{ .TagName }}:
{{- if .Type.NeedsDtor }}
new (&{{ .StorageName }}) {{ .Type }}();
{{- end }}
{{ .StorageName }} = std::move(other.{{ .StorageName }});
break;
{{- end }}
case static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}):
break;
{{- if .IsFlexible }}
default:
new (&unknown_data_) decltype(unknown_data_);
unknown_data_= std::move(other.unknown_data_);
break;
{{- end }}
}
}
return *this;
}
{{ range .Members -}}
{{ $.Name }} {{ $.Name }}::With{{ .UpperCamelCaseName }}({{ .Type }}&& val) {
{{ $.Name }} result;
result.set_{{ .Name }}(std::move(val));
return result;
}
{{ end }}
void {{ .Name }}::Encode(::fidl::Encoder* encoder, size_t offset,
cpp17::optional<::fidl::HandleInformation> maybe_handle_info) {
{{- if .Members }}
const size_t length_before = encoder->CurrentLength();
const size_t handles_before = encoder->CurrentHandleCount();
{{- end }}
switch (Which()) {
{{- range .Members }}
case {{ .TagName }}: {
if (::fidl::EncodingInlineSize<{{ .Type }}>(encoder) <= FIDL_ENVELOPE_INLINING_SIZE_THRESHOLD) {
::fidl::Encode(encoder, &{{ .StorageName }}, offset + offsetof(fidl_union_t, envelope)
{{- if .HandleInformation -}}
, ::fidl::HandleInformation{
.object_type = {{ .HandleInformation.ObjectType }},
.rights = {{ .HandleInformation.Rights }}
}
{{- end -}});
{{/* Call GetPtr after Encode because the buffer may move. */ -}}
fidl_union_t* xunion = encoder->GetPtr<fidl_union_t>(offset);
xunion->tag = tag_;
xunion->envelope.num_handles = static_cast<uint16_t>(encoder->CurrentHandleCount() - handles_before);
xunion->envelope.flags = FIDL_ENVELOPE_FLAGS_INLINING_MASK;
break;
}
::fidl::Encode(
encoder,
&{{ .StorageName }},
encoder->Alloc(::fidl::EncodingInlineSize<{{ .Type }}, ::fidl::Encoder>(encoder))
{{- if .HandleInformation -}}
, ::fidl::HandleInformation{
.object_type = {{ .HandleInformation.ObjectType }},
.rights = {{ .HandleInformation.Rights }}
}
{{- end -}}
);
fidl_union_t* xunion = encoder->GetPtr<fidl_union_t>(offset);
xunion->tag = tag_;
xunion->envelope.num_bytes = static_cast<uint32_t>(encoder->CurrentLength() - length_before);
xunion->envelope.num_handles = static_cast<uint16_t>(encoder->CurrentHandleCount() - handles_before);
xunion->envelope.flags = 0;
break;
}
{{- end }}
{{- if .IsFlexible }}
case {{ .TagUnknown }}: {
{{- if .IsResourceType }}
::fidl::EncodeUnknownData(encoder, &unknown_data_, offset + offsetof(fidl_union_t, envelope));
{{- else }}
::fidl::EncodeUnknownBytes(encoder, &unknown_data_, offset + offsetof(fidl_union_t, envelope));
{{- end }}
*encoder->GetPtr<uint64_t>(offset) = tag_;
break;
}
{{- end }}
default:
break;
}
}
void {{ .Name }}::Decode(::fidl::Decoder* _decoder, {{ .Name }}* value, size_t offset) {
fidl_union_t* xunion = _decoder->GetPtr<fidl_union_t>(offset);
if (xunion->envelope.num_bytes == 0 &&
xunion->envelope.num_handles == 0 &&
xunion->envelope.flags == 0) {
value->EnsureStorageInitialized(static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}));
return;
}
value->EnsureStorageInitialized(xunion->tag);
{{ if len .Members }}
size_t value_offset = _decoder->EnvelopeValueOffset(&xunion->envelope);
switch (value->tag_) {
{{- range .Members }}
case {{ .TagName }}: {
{{- if .Type.NeedsDtor }}
new (&value->{{ .StorageName }}) {{ .Type }}();
{{- end }}
::fidl::Decode(_decoder, &value->{{ .StorageName }}, value_offset);
break;
}
{{- end }}
default: {
{{ if .IsFlexible -}}
auto unknown_info = _decoder->EnvelopeUnknownDataInfo(&xunion->envelope);
{{- if .IsResourceType }}
value->unknown_data_.bytes.resize(unknown_info.num_bytes);
value->unknown_data_.handles.resize(unknown_info.num_handles);
::fidl::DecodeUnknownDataContents(_decoder, &value->unknown_data_, unknown_info.value_offset);
{{- else }}
value->unknown_data_.resize(unknown_info.num_bytes);
::fidl::DecodeUnknownBytesContents(_decoder, &value->unknown_data_, unknown_info.value_offset);
{{- end }}
{{ end -}}
break;
}
}
{{ end }}
}
zx_status_t {{ .Name }}::Clone({{ .Name }}* result) const {
result->Destroy();
result->tag_ = tag_;
switch (tag_) {
case {{ .TagInvalid }}:
return ZX_OK;
{{- range .Members }}
case {{ .TagName }}:
{{- if .Type.NeedsDtor }}
new (&result->{{ .StorageName }}) {{ .Type }}();
{{- end }}
return ::fidl::Clone({{ .StorageName }}, &result->{{ .StorageName }});
{{- end }}
default:
{{- if .IsFlexible }}
new (&result->unknown_data_) decltype(unknown_data_);
return ::fidl::Clone(unknown_data_, &result->unknown_data_);
{{ end -}}
return ZX_OK;
}
}
{{- range $member := .Members }}
{{ $.Name }}& {{ $.Name }}::set_{{ .Name }}({{ .Type }} value) {
EnsureStorageInitialized({{ .TagName }});
{{ .StorageName }} = std::move(value);
return *this;
}
{{- end }}
{{- if .IsFlexible }}
{{ .Name }}& {{ .Name }}::SetUnknownData(fidl_xunion_tag_t ordinal, std::vector<uint8_t> bytes{{ if .IsResourceType }}, std::vector<zx::handle> handles{{ end }}) {
EnsureStorageInitialized(ordinal);
{{- if .IsResourceType }}
unknown_data_.bytes = std::move(bytes);
unknown_data_.handles = std::move(handles);
{{- else }}
unknown_data_ = std::move(bytes);
{{- end }}
return *this;
}
{{- end }}
void {{ .Name }}::Destroy() {
switch (tag_) {
{{- range .Members }}
case {{ .TagName }}:
{{- if .Type.NeedsDtor }}
{{ .StorageName }}.~decltype({{ .StorageName }})();
{{- end }}
break;
{{- end }}
{{ if .IsFlexible }}
case static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}):
break;
default:
unknown_data_.~decltype(unknown_data_)();
break;
{{ else }}
default:
break;
{{ end }}
}
tag_ = static_cast<fidl_xunion_tag_t>({{ .TagInvalid }});
}
void {{ .Name }}::EnsureStorageInitialized(::fidl_xunion_tag_t tag) {
if (tag_ != tag) {
Destroy();
tag_ = tag;
switch (tag_) {
case static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}):
break;
{{- range .Members }}
case {{ .TagName }}:
new (&{{ .StorageName }}) {{ .Type }}();
break;
{{- end }}
default:
{{- if .IsFlexible }}
new (&unknown_data_) decltype(unknown_data_);
{{- end }}
break;
}
}
}
{{- if .IsResourceType }}
{{- EndifFuchsia -}}
{{ end }}
{{- end }}
{{- define "UnionTraits" }}
{{- if .IsResourceType }}
{{- IfdefFuchsia -}}
{{- end }}
template <>
struct IsFidlXUnion<{{ . }}> : public std::true_type {};
template <>
struct CodingTraits<{{ . }}>
: public EncodableCodingTraits<{{ . }}, 16> {};
template <>
struct CodingTraits<std::unique_ptr<{{ . }}>> {
static constexpr size_t kInlineSize = 16;
static void Encode(Encoder* encoder, std::unique_ptr<{{ . }}>* value, size_t offset,
cpp17::optional<::fidl::HandleInformation> maybe_handle_info = cpp17::nullopt) {
{{/* TODO(https://fxbug.dev/42158133): Disallow empty xunions (but permit nullable/optional
xunions). */ -}}
auto&& p_xunion = *value;
if (p_xunion) {
p_xunion->Encode(encoder, offset);
}
}
static void Decode(Decoder* _decoder, std::unique_ptr<{{ . }}>* value, size_t offset) {
fidl_union_t* encoded = _decoder->GetPtr<fidl_union_t>(offset);
if (encoded->tag == 0) {
value->reset(nullptr);
return;
}
value->reset(new {{ . }});
{{ . }}::Decode(_decoder, value->get(), offset);
}
};
inline zx_status_t Clone(const {{ . }}& value,
{{ . }}* result) {
return {{ .Namespace }}::Clone(value, result);
}
template<>
struct Equality<{{ . }}> {
bool operator()(const {{ . }}& _lhs, const {{ . }}& _rhs) const {
if (_lhs.Ordinal() != _rhs.Ordinal()) {
return false;
}
{{ with $xunion := . -}}
switch (_lhs.Ordinal()) {
case static_cast<fidl_xunion_tag_t>({{ .TagInvalid }}):
return true;
{{- range .Members }}
case {{ .TagName }}:
return ::fidl::Equals(_lhs.{{ .StorageName }}, _rhs.{{ .StorageName }});
{{- end }}
{{ if .IsFlexible -}}
default:
return ::fidl::Equals(_lhs.unknown_data_, _rhs.unknown_data_);
{{ else }}
default:
return false;
{{ end -}}
}
{{ end -}}
}
};
{{- if .IsResourceType }}
{{- EndifFuchsia -}}
{{ end }}
{{- end }}