|  | // Copyright 2023 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. | 
|  |  | 
|  | #ifndef SRC_TINT_UTILS_BYTES_DECODER_H_ | 
|  | #define SRC_TINT_UTILS_BYTES_DECODER_H_ | 
|  |  | 
|  | #include <bitset> | 
|  | #include <climits> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  | #include <unordered_map> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "src/tint/utils/bytes/reader.h" | 
|  | #include "src/tint/utils/reflection.h" | 
|  |  | 
|  | namespace tint::bytes { | 
|  |  | 
|  | template <typename T, typename = void> | 
|  | struct Decoder; | 
|  |  | 
|  | /// Decodes T from @p reader. | 
|  | /// @param reader the byte reader | 
|  | /// @param args additional arguments used by Decoder<T>::Decode() | 
|  | /// @returns the decoded object | 
|  | template <typename T, typename... ARGS> | 
|  | Result<T> Decode(Reader& reader, ARGS&&... args) { | 
|  | return Decoder<T>::Decode(reader, std::forward<ARGS>(args)...); | 
|  | } | 
|  |  | 
|  | /// Decoder specialization for integer types | 
|  | template <typename T> | 
|  | struct Decoder<T, std::enable_if_t<std::is_integral_v<T>>> { | 
|  | /// Decode decodes the integer type from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @param endianness the endianness of the integer | 
|  | /// @returns the decoded integer type, or an error if the stream is too short. | 
|  | static Result<T> Decode(Reader& reader, Endianness endianness = Endianness::kLittle) { | 
|  | return reader.Int<T>(endianness); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for floating point types | 
|  | template <typename T> | 
|  | struct Decoder<T, std::enable_if_t<std::is_floating_point_v<T>>> { | 
|  | /// Decode decodes the floating point type from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded floating point type, or an error if the stream is too short. | 
|  | static Result<T> Decode(Reader& reader) { return reader.Float<T>(); } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for a uint16_t length prefixed string. | 
|  | template <> | 
|  | struct Decoder<std::string, void> { | 
|  | /// Decode decodes the string from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded string, or an error if the stream is too short. | 
|  | static Result<std::string> Decode(Reader& reader) { | 
|  | auto len = reader.Int<uint16_t>(); | 
|  | if (len != Success) { | 
|  | return len.Failure(); | 
|  | } | 
|  | return reader.String(len.Get()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for bool types | 
|  | template <> | 
|  | struct Decoder<bool, void> { | 
|  | /// Decode decodes the boolean from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded boolean, or an error if the stream is too short. | 
|  | static Result<bool> Decode(Reader& reader) { return reader.Bool(); } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for types that use TINT_REFLECT | 
|  | template <typename T> | 
|  | struct Decoder<T, std::enable_if_t<HasReflection<T>>> { | 
|  | /// Decode decodes the reflected type from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded reflected type, or an error if the stream is too short. | 
|  | static Result<T> Decode(Reader& reader) { | 
|  | T object{}; | 
|  | diag::List errs; | 
|  | ForeachField(object, [&](auto& field) {  // | 
|  | auto value = bytes::Decode<std::decay_t<decltype(field)>>(reader); | 
|  | if (value == Success) { | 
|  | field = value.Get(); | 
|  | } else { | 
|  | errs.Add(value.Failure().reason); | 
|  | } | 
|  | }); | 
|  | if (errs.empty()) { | 
|  | return object; | 
|  | } | 
|  | return Failure{errs}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::unordered_map | 
|  | template <typename K, typename V> | 
|  | struct Decoder<std::unordered_map<K, V>, void> { | 
|  | /// Decode decodes the map from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded map, or an error if the stream is too short. | 
|  | static Result<std::unordered_map<K, V>> Decode(Reader& reader) { | 
|  | std::unordered_map<K, V> out; | 
|  |  | 
|  | while (!reader.IsEOF()) { | 
|  | auto stop = bytes::Decode<bool>(reader); | 
|  | if (stop != Success) { | 
|  | return stop.Failure(); | 
|  | } | 
|  | if (stop.Get()) { | 
|  | break; | 
|  | } | 
|  | auto key = bytes::Decode<K>(reader); | 
|  | if (key != Success) { | 
|  | return key.Failure(); | 
|  | } | 
|  | auto val = bytes::Decode<V>(reader); | 
|  | if (val != Success) { | 
|  | return val.Failure(); | 
|  | } | 
|  | out.emplace(std::move(key.Get()), std::move(val.Get())); | 
|  | } | 
|  |  | 
|  | return out; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::unordered_set | 
|  | template <typename V> | 
|  | struct Decoder<std::unordered_set<V>, void> { | 
|  | /// Decode decodes the set from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded set, or an error if the stream is too short. | 
|  | static Result<std::unordered_set<V>> Decode(Reader& reader) { | 
|  | std::unordered_set<V> out; | 
|  |  | 
|  | while (!reader.IsEOF()) { | 
|  | auto stop = bytes::Decode<bool>(reader); | 
|  | if (stop != Success) { | 
|  | return stop.Failure(); | 
|  | } | 
|  | if (stop.Get()) { | 
|  | break; | 
|  | } | 
|  | auto val = bytes::Decode<V>(reader); | 
|  | if (val != Success) { | 
|  | return val.Failure(); | 
|  | } | 
|  | out.emplace(std::move(val.Get())); | 
|  | } | 
|  |  | 
|  | return out; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::vector | 
|  | template <typename V> | 
|  | struct Decoder<std::vector<V>, void> { | 
|  | /// Decode decodes the vector from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded vector, or an error if the stream is too short. | 
|  | static Result<std::vector<V>> Decode(Reader& reader) { | 
|  | std::vector<V> out; | 
|  |  | 
|  | while (!reader.IsEOF()) { | 
|  | auto stop = bytes::Decode<bool>(reader); | 
|  | if (stop != Success) { | 
|  | return stop.Failure(); | 
|  | } | 
|  | if (stop.Get()) { | 
|  | break; | 
|  | } | 
|  | auto val = bytes::Decode<V>(reader); | 
|  | if (val != Success) { | 
|  | return val.Failure(); | 
|  | } | 
|  | out.emplace_back(std::move(val.Get())); | 
|  | } | 
|  |  | 
|  | return out; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::optional | 
|  | template <typename T> | 
|  | struct Decoder<std::optional<T>, void> { | 
|  | /// Decode decodes the optional from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded optional, or an error if the stream is too short. | 
|  | static Result<std::optional<T>> Decode(Reader& reader) { | 
|  | auto has_value = bytes::Decode<bool>(reader); | 
|  | if (has_value != Success) { | 
|  | return has_value.Failure(); | 
|  | } | 
|  | if (!has_value.Get()) { | 
|  | return std::optional<T>{std::nullopt}; | 
|  | } | 
|  | auto value = bytes::Decode<T>(reader); | 
|  | if (value != Success) { | 
|  | return value.Failure(); | 
|  | } | 
|  | return std::optional<T>{value.Get()}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::bitset | 
|  | template <std::size_t N> | 
|  | struct Decoder<std::bitset<N>, void> { | 
|  | /// Decode decodes the bitset from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded bitset, or an error if the stream is too short. | 
|  | static Result<std::bitset<N>> Decode(Reader& reader) { | 
|  | Vector<std::byte, 32> vec; | 
|  | vec.Resize((N + CHAR_BIT - 1) / CHAR_BIT); | 
|  |  | 
|  | if (auto len = reader.Read(&vec[0], vec.Length()); len != vec.Length()) { | 
|  | return Failure{"EOF"}; | 
|  | } | 
|  |  | 
|  | std::bitset<N> out; | 
|  | for (std::size_t i = 0; i < N; i++) { | 
|  | std::size_t w = i / CHAR_BIT; | 
|  | std::size_t b = i - (w * CHAR_BIT); | 
|  | out[i] = ((vec[w] >> b) & std::byte{1}) != std::byte{0}; | 
|  | } | 
|  | return out; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for std::tuple | 
|  | template <typename FIRST, typename... OTHERS> | 
|  | struct Decoder<std::tuple<FIRST, OTHERS...>, void> { | 
|  | /// Decode decodes the tuple from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @returns the decoded tuple, or an error if the stream is too short. | 
|  | static Result<std::tuple<FIRST, OTHERS...>> Decode(Reader& reader) { | 
|  | auto first = bytes::Decode<FIRST>(reader); | 
|  | if (first != Success) { | 
|  | return first.Failure(); | 
|  | } | 
|  | if constexpr (sizeof...(OTHERS) > 0) { | 
|  | auto others = bytes::Decode<std::tuple<OTHERS...>>(reader); | 
|  | if (others != Success) { | 
|  | return others.Failure(); | 
|  | } | 
|  | return std::tuple_cat(std::tuple<FIRST>(first.Get()), others.Get()); | 
|  | } else { | 
|  | return std::tuple<FIRST>(first.Get()); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Decoder specialization for enum types that have a range defined with TINT_REFLECT_ENUM_RANGE | 
|  | template <typename T> | 
|  | struct Decoder<T, std::void_t<decltype(tint::EnumRange<T>::kMax)>> { | 
|  | /// Decode decodes the enum type from @p reader. | 
|  | /// @param reader the reader to decode from | 
|  | /// @param endianness the endianness of the enum | 
|  | /// @returns the decoded enum type, or an error if the stream is too short. | 
|  | static Result<T> Decode(Reader& reader, Endianness endianness = Endianness::kLittle) { | 
|  | using Range = tint::EnumRange<T>; | 
|  | using U = std::underlying_type_t<T>; | 
|  | auto value = reader.Int<U>(endianness); | 
|  | if (value != Success) { | 
|  | return value.Failure(); | 
|  | } | 
|  | static constexpr U kMin = static_cast<U>(Range::kMin); | 
|  | static constexpr U kMax = static_cast<U>(Range::kMax); | 
|  | if (value.Get() < kMin || value.Get() > kMax) { | 
|  | return Failure{"value " + std::to_string(value.Get()) + " out of range for enum"}; | 
|  | } | 
|  | return static_cast<T>(value.Get()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace tint::bytes | 
|  |  | 
|  | #endif  // SRC_TINT_UTILS_BYTES_DECODER_H_ |