blob: 9a36faf88f0e187c394d2aa485cf27e71de6d30f [file] [log] [blame]
// Copyright 2021 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
{% set namespace_name = Name(metadata.native_namespace) %}
{% set native_namespace = namespace_name.namespace_case() %}
{% set native_dir = impl_dir + namespace_name.Dirs() %}
#include "{{native_dir}}/ChainUtils.h"
#include <tuple>
#include <unordered_set>
#include <utility>
namespace {{native_namespace}} {
{% set namespace = metadata.namespace %}
MaybeError ValidateSTypes(const ChainedStruct* chain,
std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints) {
std::unordered_set<{{namespace}}::SType> allSTypes;
for (; chain; chain = chain->nextInChain) {
DAWN_INVALID_IF(allSTypes.find(chain->sType) != allSTypes.end(),
"Extension chain has duplicate sType %s.", chain->sType);
allSTypes.insert(chain->sType);
}
for (const auto& oneOfConstraint : oneOfConstraints) {
bool satisfied = false;
for ({{namespace}}::SType oneOfSType : oneOfConstraint) {
if (allSTypes.find(oneOfSType) != allSTypes.end()) {
DAWN_INVALID_IF(satisfied,
"sType %s is part of a group of exclusive sTypes that is already present.",
oneOfSType);
satisfied = true;
allSTypes.erase(oneOfSType);
}
}
}
DAWN_INVALID_IF(!allSTypes.empty(), "Unsupported sType %s.", *allSTypes.begin());
return {};
}
MaybeError ValidateSTypes(const ChainedStructOut* chain,
std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints) {
std::unordered_set<{{namespace}}::SType> allSTypes;
for (; chain; chain = chain->nextInChain) {
DAWN_INVALID_IF(allSTypes.find(chain->sType) != allSTypes.end(),
"Extension chain has duplicate sType %s.", chain->sType);
allSTypes.insert(chain->sType);
}
for (const auto& oneOfConstraint : oneOfConstraints) {
bool satisfied = false;
for ({{namespace}}::SType oneOfSType : oneOfConstraint) {
if (allSTypes.find(oneOfSType) != allSTypes.end()) {
DAWN_INVALID_IF(satisfied,
"sType %s is part of a group of exclusive sTypes that is already present.",
oneOfSType);
satisfied = true;
allSTypes.erase(oneOfSType);
}
}
}
DAWN_INVALID_IF(!allSTypes.empty(), "Unsupported sType %s.", *allSTypes.begin());
return {};
}
// Returns true iff the chain's SType matches the extension, false otherwise. If the SType was
// not already matched, sets the unpacked result accordingly. Otherwise, stores the duplicated
// SType in 'duplicate'.
template <typename Root, typename UnpackedT, typename Ext>
bool UnpackExtension(typename UnpackedT::TupleType& unpacked,
typename UnpackedT::BitsetType& bitset,
typename UnpackedT::ChainType chain, bool* duplicate) {
DAWN_ASSERT(chain != nullptr);
if (chain->sType == STypeFor<Ext>) {
auto& member = std::get<Ext>(unpacked);
if (member != nullptr && duplicate) {
*duplicate = true;
} else {
member = reinterpret_cast<Ext>(chain);
bitset.set(detail::UnpackedIndexOf<UnpackedT, Ext>);
}
return true;
}
return false;
}
// Tries to match all possible extensions, returning true iff one of the allowed extensions were
// matched, false otherwise. If the SType was not already matched, sets the unpacked result
// accordingly. Otherwise, stores the duplicated SType in 'duplicate'.
template <typename Root, typename UnpackedT, typename AdditionalExts>
struct AdditionalExtensionUnpacker;
template <typename Root, typename UnpackedT, typename... Exts>
struct AdditionalExtensionUnpacker<Root, UnpackedT, detail::AdditionalExtensionsList<Exts...>> {
static bool Unpack(typename UnpackedT::TupleType& unpacked,
typename UnpackedT::BitsetType& bitset,
typename UnpackedT::ChainType chain,
bool* duplicate) {
return ((UnpackExtension<Root, UnpackedT, Exts>(unpacked, bitset, chain, duplicate)) ||
...);
}
};
//
// Unpacked chain helpers.
//
{% for type in by_category["structure"] %}
{% if not type.extensible %}
{% continue %}
{% endif %}
{% set T = as_cppType(type.name) %}
{% set UnpackedT = "Unpacked<" + T + ">" %}
template <>
{{UnpackedT}} Unpack<{{T}}>(typename {{UnpackedT}}::PtrType chain) {
{{UnpackedT}} result(chain);
for (typename {{UnpackedT}}::ChainType next = chain->nextInChain;
next != nullptr;
next = next->nextInChain) {
switch (next->sType) {
{% for extension in type.extensions %}
{% set Ext = as_cppType(extension.name) %}
case STypeFor<{{Ext}}>: {
using ExtPtrType =
typename detail::PtrTypeFor<{{UnpackedT}}, {{Ext}}>::Type;
std::get<ExtPtrType>(result.mUnpacked) =
static_cast<ExtPtrType>(next);
result.mBitset.set(
detail::UnpackedIndexOf<{{UnpackedT}}, ExtPtrType>
);
break;
}
{% endfor %}
default: {
using Unpacker =
AdditionalExtensionUnpacker<
{{T}},
{{UnpackedT}},
detail::AdditionalExtensions<{{T}}>::List>;
Unpacker::Unpack(result.mUnpacked, result.mBitset, next, nullptr);
break;
}
}
}
return result;
}
template <>
ResultOrError<{{UnpackedT}}> ValidateAndUnpack<{{T}}>(typename {{UnpackedT}}::PtrType chain) {
{{UnpackedT}} result(chain);
for (typename {{UnpackedT}}::ChainType next = chain->nextInChain;
next != nullptr;
next = next->nextInChain) {
bool duplicate = false;
switch (next->sType) {
{% for extension in type.extensions %}
{% set Ext = as_cppType(extension.name) %}
case STypeFor<{{Ext}}>: {
using ExtPtrType =
typename detail::PtrTypeFor<{{UnpackedT}}, {{Ext}}>::Type;
auto& member = std::get<ExtPtrType>(result.mUnpacked);
if (member != nullptr) {
duplicate = true;
} else {
member = static_cast<ExtPtrType>(next);
result.mBitset.set(
detail::UnpackedIndexOf<{{UnpackedT}}, ExtPtrType>
);
}
break;
}
{% endfor %}
default: {
using Unpacker =
AdditionalExtensionUnpacker<
{{T}},
{{UnpackedT}},
detail::AdditionalExtensions<{{T}}>::List>;
if (!Unpacker::Unpack(result.mUnpacked,
result.mBitset,
next,
&duplicate)) {
return DAWN_VALIDATION_ERROR(
"Unexpected chained struct of type %s found on %s chain.",
next->sType, "{{T}}"
);
}
break;
}
}
if (duplicate) {
return DAWN_VALIDATION_ERROR(
"Duplicate chained struct of type %s found on %s chain.",
next->sType, "{{T}}"
);
}
}
return result;
}
{% endfor %}
} // namespace {{native_namespace}}