Austin Eng f104fea367 Remove size_t from wire transfer structs
This makes the primitives in the serialized wire protocol
the same across platforms and architectures which is better
for both fuzzing and remoting Dawn.

Commands that used size_t are updated to use uint64_t, and
the server-side implementation checks if conversion to
size_t would narrow.

Bug: dawn:680
Change-Id: Icef9dc11a72699685ed7191c34d6a922b652c887
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/41582
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
2021-02-18 22:36:19 +00:00

747 lines
33 KiB
C++

//* Copyright 2017 The Dawn Authors
//*
//* Licensed under the Apache License, Version 2.0 (the "License");
//* you may not use this file except in compliance with the License.
//* You may obtain a copy of the License at
//*
//* http://www.apache.org/licenses/LICENSE-2.0
//*
//* Unless required by applicable law or agreed to in writing, software
//* distributed under the License is distributed on an "AS IS" BASIS,
//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//* See the License for the specific language governing permissions and
//* limitations under the License.
#include "dawn_wire/WireCmd_autogen.h"
#include "common/Assert.h"
#include "common/Log.h"
#include "dawn_wire/Wire.h"
#include <algorithm>
#include <cstring>
#include <limits>
//* Helper macros so that the main [de]serialization functions can be written in a generic manner.
//* Outputs an rvalue that's the number of elements a pointer member points to.
{% macro member_length(member, record_accessor) -%}
{%- if member.length == "constant" -%}
{{member.constant_length}}u
{%- else -%}
{{record_accessor}}{{as_varName(member.length.name)}}
{%- endif -%}
{%- endmacro %}
//* Outputs the type that will be used on the wire for the member
{% macro member_transfer_type(member) -%}
{%- if member.type.category == "object" -%}
ObjectId
{%- elif member.type.category == "structure" -%}
{{as_cType(member.type.name)}}Transfer
{%- elif member.type.category == "bitmask" -%}
{{as_cType(member.type.name)}}Flags
{%- else -%}
{{ assert(as_cType(member.type.name) != "size_t") }}
{{as_cType(member.type.name)}}
{%- endif -%}
{%- endmacro %}
//* Outputs the size of one element of the type that will be used on the wire for the member
{% macro member_transfer_sizeof(member) -%}
sizeof({{member_transfer_type(member)}})
{%- endmacro %}
//* Outputs the serialization code to put `in` in `out`
{% macro serialize_member(member, in, out) %}
{%- if member.type.category == "object" -%}
{%- set Optional = "Optional" if member.optional else "" -%}
{{out}} = provider.Get{{Optional}}Id({{in}});
{% elif member.type.category == "structure"%}
{%- set Provider = ", provider" if member.type.may_have_dawn_object else "" -%}
{% if member.annotation == "const*const*" %}
{{as_cType(member.type.name)}}Serialize(*{{in}}, &{{out}}, buffer{{Provider}});
{% else %}
{{as_cType(member.type.name)}}Serialize({{in}}, &{{out}}, buffer{{Provider}});
{% endif %}
{%- else -%}
{{out}} = {{in}};
{%- endif -%}
{% endmacro %}
//* Outputs the deserialization code to put `in` in `out`
{% macro deserialize_member(member, in, out) %}
{%- if member.type.category == "object" -%}
{%- set Optional = "Optional" if member.optional else "" -%}
DESERIALIZE_TRY(resolver.Get{{Optional}}FromId({{in}}, &{{out}}));
{%- elif member.type.category == "structure" -%}
DESERIALIZE_TRY({{as_cType(member.type.name)}}Deserialize(&{{out}}, &{{in}}, deserializeBuffer, allocator
{%- if member.type.may_have_dawn_object -%}
, resolver
{%- endif -%}
));
{%- else -%}
static_assert(sizeof({{out}}) >= sizeof({{in}}), "Deserialize assignment may not narrow.");
{{out}} = {{in}};
{%- endif -%}
{% endmacro %}
namespace {
struct WGPUChainedStructTransfer {
WGPUSType sType;
bool hasNext;
};
} // anonymous namespace
//* The main [de]serialization macro
//* Methods are very similar to structures that have one member corresponding to each arguments.
//* This macro takes advantage of the similarity to output [de]serialization code for a record
//* that is either a structure or a method, with some special cases for each.
{% macro write_record_serialization_helpers(record, name, members, is_cmd=False, is_return_command=False) %}
{% set Return = "Return" if is_return_command else "" %}
{% set Cmd = "Cmd" if is_cmd else "" %}
{% set Inherits = " : CmdHeader" if is_cmd else "" %}
//* Structure for the wire format of each of the records. Members that are values
//* are embedded directly in the structure. Other members are assumed to be in the
//* memory directly following the structure in the buffer.
struct {{Return}}{{name}}Transfer{{Inherits}} {
static_assert({{[is_cmd, record.extensible, record.chained].count(True)}} <= 1,
"Record must be at most one of is_cmd, extensible, and chained.");
{% if is_cmd %}
//* Start the transfer structure with the command ID, so that casting to WireCmd gives the ID.
{{Return}}WireCmd commandId;
{% elif record.extensible %}
bool hasNextInChain;
{% elif record.chained %}
WGPUChainedStructTransfer chain;
{% endif %}
//* Value types are directly in the command, objects being replaced with their IDs.
{% for member in members if member.annotation == "value" %}
{{member_transfer_type(member)}} {{as_varName(member.name)}};
{% endfor %}
//* const char* have their length embedded directly in the command.
{% for member in members if member.length == "strlen" %}
uint64_t {{as_varName(member.name)}}Strlen;
{% endfor %}
{% for member in members if member.optional and member.annotation != "value" and member.type.category != "object" %}
bool has_{{as_varName(member.name)}};
{% endfor %}
};
{% if is_cmd %}
static_assert(offsetof({{Return}}{{name}}Transfer, commandSize) == 0, "");
static_assert(offsetof({{Return}}{{name}}Transfer, commandId) == sizeof(CmdHeader), "");
{% endif %}
{% if record.chained %}
static_assert(offsetof({{Return}}{{name}}Transfer, chain) == 0, "");
{% endif %}
//* Returns the required transfer size for `record` in addition to the transfer structure.
DAWN_DECLARE_UNUSED size_t {{Return}}{{name}}GetExtraRequiredSize(const {{Return}}{{name}}{{Cmd}}& record) {
DAWN_UNUSED(record);
size_t result = 0;
//* Gather how much space will be needed for the extension chain.
{% if record.extensible %}
if (record.nextInChain != nullptr) {
result += GetChainedStructExtraRequiredSize(record.nextInChain);
}
{% endif %}
//* Special handling of const char* that have their length embedded directly in the command
{% for member in members if member.length == "strlen" %}
{% set memberName = as_varName(member.name) %}
{% if member.optional %}
bool has_{{memberName}} = record.{{memberName}} != nullptr;
if (has_{{memberName}})
{% endif %}
{
result += std::strlen(record.{{memberName}});
}
{% endfor %}
//* Gather how much space will be needed for pointer members.
{% for member in members if member.length != "strlen" and not member.skip_serialize %}
{% if member.type.category != "object" and member.optional %}
if (record.{{as_varName(member.name)}} != nullptr)
{% endif %}
{
{% if member.annotation != "value" %}
auto memberLength = {{member_length(member, "record.")}};
result += memberLength * {{member_transfer_sizeof(member)}};
//* Structures might contain more pointers so we need to add their extra size as well.
{% if member.type.category == "structure" %}
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
{% if member.annotation == "const*const*" %}
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(*record.{{as_varName(member.name)}}[i]);
{% else %}
{{assert(member.annotation == "const*")}}
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]);
{% endif %}
}
{% endif %}
{% elif member.type.category == "structure" %}
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}});
{% endif %}
}
{% endfor %}
return result;
}
// GetExtraRequiredSize isn't used for structures that are value members of other structures
// because we assume they cannot contain pointers themselves.
DAWN_UNUSED_FUNC({{Return}}{{name}}GetExtraRequiredSize);
//* Serializes `record` into `transfer`, using `buffer` to get more space for pointed-to data
//* and `provider` to serialize objects.
DAWN_DECLARE_UNUSED bool {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer,
SerializeBuffer* buffer
{%- if record.may_have_dawn_object -%}
, const ObjectIdProvider& provider
{%- endif -%}
) {
DAWN_UNUSED(buffer);
//* Handle special transfer members of methods.
{% if is_cmd %}
transfer->commandId = {{Return}}WireCmd::{{name}};
{% endif %}
//* Value types are directly in the transfer record, objects being replaced with their IDs.
{% for member in members if member.annotation == "value" %}
{% set memberName = as_varName(member.name) %}
{{serialize_member(member, "record." + memberName, "transfer->" + memberName)}}
{% endfor %}
{% if record.extensible %}
if (record.nextInChain != nullptr) {
transfer->hasNextInChain = true;
SERIALIZE_TRY(SerializeChainedStruct(record.nextInChain, buffer, provider));
} else {
transfer->hasNextInChain = false;
}
{% endif %}
{% if record.chained %}
//* Should be set by the root descriptor's call to SerializeChainedStruct.
ASSERT(transfer->chain.sType == {{as_cEnum(types["s type"].name, record.name)}});
ASSERT(transfer->chain.hasNext == (record.chain.next != nullptr));
{% endif %}
//* Special handling of const char* that have their length embedded directly in the command
{% for member in members if member.length == "strlen" %}
{% set memberName = as_varName(member.name) %}
{% if member.optional %}
bool has_{{memberName}} = record.{{memberName}} != nullptr;
transfer->has_{{memberName}} = has_{{memberName}};
if (has_{{memberName}})
{% endif %}
{
transfer->{{memberName}}Strlen = std::strlen(record.{{memberName}});
char* stringInBuffer;
SERIALIZE_TRY(buffer->NextN(transfer->{{memberName}}Strlen, &stringInBuffer));
memcpy(stringInBuffer, record.{{memberName}}, transfer->{{memberName}}Strlen);
}
{% endfor %}
//* Allocate space and write the non-value arguments in it.
{% for member in members if member.annotation != "value" and member.length != "strlen" and not member.skip_serialize %}
{% set memberName = as_varName(member.name) %}
{% if member.type.category != "object" and member.optional %}
bool has_{{memberName}} = record.{{memberName}} != nullptr;
transfer->has_{{memberName}} = has_{{memberName}};
if (has_{{memberName}})
{% endif %}
{
auto memberLength = {{member_length(member, "record.")}};
{{member_transfer_type(member)}}* memberBuffer;
SERIALIZE_TRY(buffer->NextN(memberLength, &memberBuffer));
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
{{serialize_member(member, "record." + memberName + "[i]", "memberBuffer[i]" )}}
}
}
{% endfor %}
return true;
}
DAWN_UNUSED_FUNC({{Return}}{{name}}Serialize);
//* Deserializes `transfer` into `record` getting more serialized data from `buffer` and `size`
//* if needed, using `allocator` to store pointed-to values and `resolver` to translate object
//* Ids to actual objects.
DAWN_DECLARE_UNUSED DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const volatile {{Return}}{{name}}Transfer* transfer,
DeserializeBuffer* deserializeBuffer, DeserializeAllocator* allocator
{%- if record.may_have_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
DAWN_UNUSED(allocator);
{% if is_cmd %}
ASSERT(transfer->commandId == {{Return}}WireCmd::{{name}});
{% endif %}
{% if record.derived_method %}
record->selfId = transfer->self;
{% endif %}
//* Value types are directly in the transfer record, objects being replaced with their IDs.
{% for member in members if member.annotation == "value" %}
{% set memberName = as_varName(member.name) %}
{{deserialize_member(member, "transfer->" + memberName, "record->" + memberName)}}
{% endfor %}
{% if record.extensible %}
record->nextInChain = nullptr;
if (transfer->hasNextInChain) {
DESERIALIZE_TRY(DeserializeChainedStruct(&record->nextInChain, deserializeBuffer, allocator, resolver));
}
{% endif %}
{% if record.chained %}
//* Should be set by the root descriptor's call to DeserializeChainedStruct.
//* Don't check |record->chain.next| matches because it is not set until the
//* next iteration inside DeserializeChainedStruct.
ASSERT(record->chain.sType == {{as_cEnum(types["s type"].name, record.name)}});
ASSERT(record->chain.next == nullptr);
{% endif %}
//* Special handling of const char* that have their length embedded directly in the command
{% for member in members if member.length == "strlen" %}
{% set memberName = as_varName(member.name) %}
{% if member.optional %}
bool has_{{memberName}} = transfer->has_{{memberName}};
record->{{memberName}} = nullptr;
if (has_{{memberName}})
{% endif %}
{
uint64_t stringLength64 = transfer->{{memberName}}Strlen;
if (stringLength64 >= std::numeric_limits<size_t>::max()) {
//* Cannot allocate space for the string. It can be at most
//* size_t::max() - 1. We need 1 byte for the null-terminator.
return DeserializeResult::FatalError;
}
size_t stringLength = static_cast<size_t>(stringLength64);
const volatile char* stringInBuffer;
DESERIALIZE_TRY(deserializeBuffer->ReadN(stringLength, &stringInBuffer));
char* copiedString;
DESERIALIZE_TRY(GetSpace(allocator, stringLength + 1, &copiedString));
//* We can cast away the volatile qualifier because DeserializeBuffer::ReadN already
//* validated that the range [stringInBuffer, stringInBuffer + stringLength) is valid.
//* memcpy may have an unknown access pattern, but this is fine since the string is only
//* data and won't affect control flow of this function.
memcpy(copiedString, const_cast<const char*>(stringInBuffer), stringLength);
copiedString[stringLength] = '\0';
record->{{memberName}} = copiedString;
}
{% endfor %}
//* Get extra buffer data, and copy pointed to values in extra allocated space.
{% for member in members if member.annotation != "value" and member.length != "strlen" %}
{% set memberName = as_varName(member.name) %}
{% if member.type.category != "object" and member.optional %}
bool has_{{memberName}} = transfer->has_{{memberName}};
record->{{memberName}} = nullptr;
if (has_{{memberName}})
{% endif %}
{
auto memberLength = {{member_length(member, "record->")}};
const volatile {{member_transfer_type(member)}}* memberBuffer;
DESERIALIZE_TRY(deserializeBuffer->ReadN(memberLength, &memberBuffer));
{{as_cType(member.type.name)}}* copiedMembers;
DESERIALIZE_TRY(GetSpace(allocator, memberLength, &copiedMembers));
{% if member.annotation == "const*const*" %}
{{as_cType(member.type.name)}}** pointerArray;
DESERIALIZE_TRY(GetSpace(allocator, memberLength, &pointerArray));
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
pointerArray[i] = &copiedMembers[i];
}
record->{{memberName}} = pointerArray;
{% else %}
record->{{memberName}} = copiedMembers;
{% endif %}
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
{{deserialize_member(member, "memberBuffer[i]", "copiedMembers[i]")}}
}
}
{% endfor %}
return DeserializeResult::Success;
}
DAWN_UNUSED_FUNC({{Return}}{{name}}Deserialize);
{% endmacro %}
{% macro write_command_serialization_methods(command, is_return) %}
{% set Return = "Return" if is_return else "" %}
{% set Name = Return + command.name.CamelCase() %}
{% set Cmd = Name + "Cmd" %}
size_t {{Cmd}}::GetRequiredSize() const {
size_t size = sizeof({{Name}}Transfer) + {{Name}}GetExtraRequiredSize(*this);
return size;
}
bool {{Cmd}}::Serialize(size_t commandSize, SerializeBuffer* buffer
{%- if not is_return -%}
, const ObjectIdProvider& objectIdProvider
{%- endif -%}
) const {
{{Name}}Transfer* transfer;
SERIALIZE_TRY(buffer->Next(&transfer));
transfer->commandSize = commandSize;
SERIALIZE_TRY({{Name}}Serialize(*this, transfer, buffer
{%- if command.may_have_dawn_object -%}
, objectIdProvider
{%- endif -%}
));
return true;
}
DeserializeResult {{Cmd}}::Deserialize(DeserializeBuffer* deserializeBuffer, DeserializeAllocator* allocator
{%- if command.may_have_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
const volatile {{Name}}Transfer* transfer;
DESERIALIZE_TRY(deserializeBuffer->Read(&transfer));
return {{Name}}Deserialize(this, transfer, deserializeBuffer, allocator
{%- if command.may_have_dawn_object -%}
, resolver
{%- endif -%}
);
}
{% endmacro %}
namespace dawn_wire {
// Macro to simplify error handling, similar to DAWN_TRY but for DeserializeResult.
#define DESERIALIZE_TRY(EXPR) \
do { \
DeserializeResult exprResult = EXPR; \
if (exprResult != DeserializeResult::Success) { \
return exprResult; \
} \
} while (0)
#define SERIALIZE_TRY(EXPR) \
do { \
if (!(EXPR)) { \
return false; \
} \
} while (0)
ObjectHandle::ObjectHandle() = default;
ObjectHandle::ObjectHandle(ObjectId id, ObjectGeneration generation)
: id(id), generation(generation) {
}
ObjectHandle::ObjectHandle(const volatile ObjectHandle& rhs)
: id(rhs.id), generation(rhs.generation) {
}
ObjectHandle& ObjectHandle::operator=(const volatile ObjectHandle& rhs) {
id = rhs.id;
generation = rhs.generation;
return *this;
}
ObjectHandle& ObjectHandle::AssignFrom(const ObjectHandle& rhs) {
id = rhs.id;
generation = rhs.generation;
return *this;
}
ObjectHandle& ObjectHandle::AssignFrom(const volatile ObjectHandle& rhs) {
id = rhs.id;
generation = rhs.generation;
return *this;
}
template <typename BufferT>
template <typename T>
bool BufferConsumer<BufferT>::Peek(T** data) {
if (sizeof(T) > mSize) {
return false;
}
*data = reinterpret_cast<T*>(mBuffer);
return true;
}
template <typename BufferT>
template <typename T>
bool BufferConsumer<BufferT>::Next(T** data) {
if (sizeof(T) > mSize) {
return false;
}
*data = reinterpret_cast<T*>(mBuffer);
mBuffer += sizeof(T);
mSize -= sizeof(T);
return true;
}
template <typename BufferT>
template <typename T, typename N>
bool BufferConsumer<BufferT>::NextN(N count, T** data) {
static_assert(std::is_unsigned<N>::value, "|count| argument of NextN must be unsigned.");
constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits<size_t>::max() / sizeof(T);
if (count > kMaxCountWithoutOverflows) {
return false;
}
// Cannot overflow because |count| is not greater than |kMaxCountWithoutOverflows|.
size_t totalSize = sizeof(T) * count;
if (totalSize > mSize) {
return false;
}
*data = reinterpret_cast<T*>(mBuffer);
mBuffer += totalSize;
mSize -= totalSize;
return true;
}
namespace {
// Allocates enough space from allocator to countain T[count] and return it in out.
// Return FatalError if the allocator couldn't allocate the memory.
// Always writes to |out| on success.
template <typename T, typename N>
DeserializeResult GetSpace(DeserializeAllocator* allocator, N count, T** out) {
constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits<size_t>::max() / sizeof(T);
if (count > kMaxCountWithoutOverflows) {
return DeserializeResult::FatalError;
}
size_t totalSize = sizeof(T) * count;
*out = static_cast<T*>(allocator->GetSpace(totalSize));
if (*out == nullptr) {
return DeserializeResult::FatalError;
}
return DeserializeResult::Success;
}
size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct);
DAWN_NO_DISCARD bool SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
SerializeBuffer* buffer,
const ObjectIdProvider& provider);
DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
DeserializeBuffer* deserializeBuffer,
DeserializeAllocator* allocator,
const ObjectIdResolver& resolver);
//* Output structure [de]serialization first because it is used by commands.
{% for type in by_category["structure"] %}
{% set name = as_cType(type.name) %}
{% if type.name.CamelCase() not in client_side_structures %}
{{write_record_serialization_helpers(type, name, type.members,
is_cmd=False)}}
{% endif %}
{% endfor %}
size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct) {
ASSERT(chainedStruct != nullptr);
size_t result = 0;
while (chainedStruct != nullptr) {
switch (chainedStruct->sType) {
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
case {{as_cEnum(types["s type"].name, sType.name)}}: {
const auto& typedStruct = *reinterpret_cast<{{as_cType(sType.name)}} const *>(chainedStruct);
result += sizeof({{as_cType(sType.name)}}Transfer);
result += {{as_cType(sType.name)}}GetExtraRequiredSize(typedStruct);
chainedStruct = typedStruct.chain.next;
break;
}
{% endfor %}
default:
// Invalid enum. Reserve space just for the transfer header (sType and hasNext).
result += sizeof(WGPUChainedStructTransfer);
chainedStruct = chainedStruct->next;
break;
}
}
return result;
}
DAWN_NO_DISCARD bool SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
SerializeBuffer* buffer,
const ObjectIdProvider& provider) {
ASSERT(chainedStruct != nullptr);
ASSERT(buffer != nullptr);
do {
switch (chainedStruct->sType) {
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
{% set CType = as_cType(sType.name) %}
case {{as_cEnum(types["s type"].name, sType.name)}}: {
{{CType}}Transfer* transfer;
SERIALIZE_TRY(buffer->Next(&transfer));
transfer->chain.sType = chainedStruct->sType;
transfer->chain.hasNext = chainedStruct->next != nullptr;
SERIALIZE_TRY({{CType}}Serialize(*reinterpret_cast<{{CType}} const*>(chainedStruct), transfer, buffer
{%- if types[sType.name.get()].may_have_dawn_object -%}
, provider
{%- endif -%}
));
chainedStruct = chainedStruct->next;
} break;
{% endfor %}
default: {
// Invalid enum. Serialize just the transfer header with Invalid as the sType.
// TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
if (chainedStruct->sType != WGPUSType_Invalid) {
dawn::WarningLog() << "Unknown sType " << chainedStruct->sType << " discarded.";
}
WGPUChainedStructTransfer* transfer;
SERIALIZE_TRY(buffer->Next(&transfer));
transfer->sType = WGPUSType_Invalid;
transfer->hasNext = chainedStruct->next != nullptr;
// Still move on in case there are valid structs after this.
chainedStruct = chainedStruct->next;
break;
}
}
} while (chainedStruct != nullptr);
return true;
}
DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
DeserializeBuffer* deserializeBuffer,
DeserializeAllocator* allocator,
const ObjectIdResolver& resolver) {
bool hasNext;
do {
const volatile WGPUChainedStructTransfer* header;
DESERIALIZE_TRY(deserializeBuffer->Peek(&header));
WGPUSType sType = header->sType;
switch (sType) {
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
{% set CType = as_cType(sType.name) %}
case {{as_cEnum(types["s type"].name, sType.name)}}: {
const volatile {{CType}}Transfer* transfer;
DESERIALIZE_TRY(deserializeBuffer->Read(&transfer));
{{CType}}* outStruct;
DESERIALIZE_TRY(GetSpace(allocator, sizeof({{CType}}), &outStruct));
outStruct->chain.sType = sType;
outStruct->chain.next = nullptr;
*outChainNext = &outStruct->chain;
outChainNext = &outStruct->chain.next;
DESERIALIZE_TRY({{CType}}Deserialize(outStruct, transfer, deserializeBuffer, allocator
{%- if types[sType.name.get()].may_have_dawn_object -%}
, resolver
{%- endif -%}
));
hasNext = transfer->chain.hasNext;
} break;
{% endfor %}
default: {
// Invalid enum. Deserialize just the transfer header with Invalid as the sType.
// TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
if (sType != WGPUSType_Invalid) {
dawn::WarningLog() << "Unknown sType " << sType << " discarded.";
}
const volatile WGPUChainedStructTransfer* transfer;
DESERIALIZE_TRY(deserializeBuffer->Read(&transfer));
WGPUChainedStruct* outStruct;
DESERIALIZE_TRY(GetSpace(allocator, sizeof(WGPUChainedStruct), &outStruct));
outStruct->sType = WGPUSType_Invalid;
outStruct->next = nullptr;
// Still move on in case there are valid structs after this.
*outChainNext = outStruct;
outChainNext = &outStruct->next;
hasNext = transfer->hasNext;
break;
}
}
} while (hasNext);
return DeserializeResult::Success;
}
//* Output [de]serialization helpers for commands
{% for command in cmd_records["command"] %}
{% set name = command.name.CamelCase() %}
{{write_record_serialization_helpers(command, name, command.members,
is_cmd=True)}}
{% endfor %}
//* Output [de]serialization helpers for return commands
{% for command in cmd_records["return command"] %}
{% set name = command.name.CamelCase() %}
{{write_record_serialization_helpers(command, name, command.members,
is_cmd=True, is_return_command=True)}}
{% endfor %}
} // anonymous namespace
{% for command in cmd_records["command"] %}
{{ write_command_serialization_methods(command, False) }}
{% endfor %}
{% for command in cmd_records["return command"] %}
{{ write_command_serialization_methods(command, True) }}
{% endfor %}
// Implementations of serialization/deserialization of WPGUDeviceProperties.
size_t SerializedWGPUDevicePropertiesSize(const WGPUDeviceProperties* deviceProperties) {
return sizeof(WGPUDeviceProperties) +
WGPUDevicePropertiesGetExtraRequiredSize(*deviceProperties);
}
void SerializeWGPUDeviceProperties(const WGPUDeviceProperties* deviceProperties,
char* buffer) {
SerializeBuffer serializeBuffer(buffer, SerializedWGPUDevicePropertiesSize(deviceProperties));
WGPUDevicePropertiesTransfer* transfer;
bool success =
serializeBuffer.Next(&transfer) &&
WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer);
ASSERT(success);
}
bool DeserializeWGPUDeviceProperties(WGPUDeviceProperties* deviceProperties,
const volatile char* buffer,
size_t size) {
const volatile WGPUDevicePropertiesTransfer* transfer;
DeserializeBuffer deserializeBuffer(buffer, size);
if (deserializeBuffer.Read(&transfer) != DeserializeResult::Success) {
return false;
}
return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer,
nullptr) == DeserializeResult::Success;
}
} // namespace dawn_wire