{%- import "validation_macros.tmpl" as validation_macros %}
{%- set class_name = struct.name ~ "_Data" %}

// static
bool {{class_name}}::Validate(
    const void* data,
    mojo::internal::ValidationContext* validation_context) {
  if (!data)
    return true;

  if (!ValidateStructHeaderAndClaimMemory(data, validation_context))
    return false;

  // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if
  // the message comes from an older version.
  const {{class_name}}* object = static_cast<const {{class_name}}*>(data);

  static constexpr struct {
    uint32_t version;
    uint32_t num_bytes;
  } kVersionSizes[] = {
{%- for version in struct.versions -%}
    { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%}
{%- endfor -%}
  };

  if (object->header_.version <=
          kVersionSizes[arraysize(kVersionSizes) - 1].version) {
    // Scan in reverse order to optimize for more recent versions.
    for (int i = arraysize(kVersionSizes) - 1; i >= 0; --i) {
      if (object->header_.version >= kVersionSizes[i].version) {
        if (object->header_.num_bytes == kVersionSizes[i].num_bytes)
          break;

        ReportValidationError(
            validation_context,
            mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
        return false;
      }
    }
  } else if (object->header_.num_bytes <
                 kVersionSizes[arraysize(kVersionSizes) - 1].num_bytes) {
    ReportValidationError(
        validation_context,
        mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
    return false;
  }

{#- Before validating fields introduced at a certain version, we need to add
    a version check, which makes sure we skip further validation if |object|
    is from an earlier version. |last_checked_version| records the last
    version that we have added such version check. #}
{%- set last_checked_version = 0 %}
{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
{%-   set kind = packed_field.field.kind %}
{%-   if kind|is_object_kind or kind|is_any_handle_or_interface_kind or
         kind|is_enum_kind %}
{%-     if packed_field.min_version > last_checked_version %}
{%-       set last_checked_version = packed_field.min_version %}
  if (object->header_.version < {{packed_field.min_version}})
    return true;
{%-     endif %}
{%-     set field_expr = "object->" ~ packed_field.field.name %}
{{validation_macros.validate_field(packed_field.field, loop.index, field_expr, struct.name, true)}}
{%-   endif %}
{%- endfor %}

  return true;
}

{{class_name}}::{{class_name}}()
    : header_({sizeof(*this), {{struct.versions[-1].version}}}) {}
