{%- set class_name = union.name ~ "_Data" -%}
{%- set enum_name = union.name ~ "_Tag" -%}
{%- import "struct_macros.tmpl" as struct_macros %}

class {{export_attribute}} {{class_name}} {
 public:
  // Used to identify Mojom Union Data Classes.
  typedef void MojomUnionDataType;

  {{class_name}}() {}
  // Do nothing in the destructor since it won't be called when it is a
  // non-inlined union.
  ~{{class_name}}() {}

  class BufferWriter {
   public:
    BufferWriter() = default;

    void Allocate(mojo::internal::Buffer* serialization_buffer) {
      serialization_buffer_ = serialization_buffer;
      index_ = serialization_buffer_->Allocate(sizeof({{class_name}}));
      new (data()) {{class_name}}();
    }

    void AllocateInline(mojo::internal::Buffer* serialization_buffer,
                        void* ptr) {
      const char* start = static_cast<const char*>(
          serialization_buffer->data());
      const char* slot = static_cast<const char*>(ptr);
      DCHECK_GT(slot, start);
      serialization_buffer_ = serialization_buffer;
      index_ = slot - start;
      new (data()) {{class_name}}();
    }

    bool is_null() const { return !serialization_buffer_; }
    {{class_name}}* data() {
      DCHECK(!is_null());
      return serialization_buffer_->Get<{{class_name}}>(index_);
    }
    {{class_name}}* operator->() { return data(); }

   private:
    mojo::internal::Buffer* serialization_buffer_ = nullptr;
    size_t index_ = 0;
  };

  static bool Validate(const void* data,
                       mojo::internal::ValidationContext* validation_context,
                       bool inlined);

  bool is_null() const { return size == 0; }

  void set_null() {
    size = 0U;
    tag = static_cast<{{enum_name}}>(0);
    data.unknown = 0U;
  }

  enum class {{enum_name}} : uint32_t {
{%  for field in union.fields %}
    {{field.name|upper}},
{%- endfor %}
  };

  // A note on layout:
  // "Each non-static data member is allocated as if it were the sole member of
  // a struct." - Section 9.5.2 ISO/IEC 14882:2011 (The C++ Spec)
  union MOJO_ALIGNAS(8) Union_ {
    Union_() : unknown(0) {}

{%- for field in union.fields %}
{%-   if field.kind.spec == 'b' %}
    uint8_t f_{{field.name}} : 1;
{%-   else %}
    {{field.kind|cpp_union_field_type}} f_{{field.name}};
{%-   endif %}
{%- endfor %}
    uint64_t unknown;
  };

  uint32_t size;
  {{enum_name}} tag;
  Union_ data;
};
static_assert(sizeof({{class_name}}) == mojo::internal::kUnionDataSize,
              "Bad sizeof({{class_name}})");
