| // |
| // Copyright 2012 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // BinaryStream.h: Provides binary serialization of simple types. |
| |
| #ifndef COMMON_BINARYSTREAM_H_ |
| #define COMMON_BINARYSTREAM_H_ |
| |
| #include <stdint.h> |
| |
| #include <cstddef> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "common/PackedEnums.h" |
| #include "common/angleutils.h" |
| #include "common/span.h" |
| #include "common/span_util.h" |
| |
| namespace gl |
| { |
| template <typename IntT> |
| struct PromotedIntegerType |
| { |
| using type = typename std::conditional< |
| std::is_signed<IntT>::value, |
| typename std::conditional<sizeof(IntT) <= 4, int32_t, int64_t>::type, |
| typename std::conditional<sizeof(IntT) <= 4, uint32_t, uint64_t>::type>::type; |
| }; |
| |
| class BinaryInputStream : angle::NonCopyable |
| { |
| public: |
| BinaryInputStream(angle::Span<const uint8_t> data) : mData(data) {} |
| |
| // readInt will generate an error for bool types |
| template <class IntT> |
| IntT readInt() |
| { |
| static_assert(!std::is_same<bool, std::remove_cv<IntT>()>(), "Use readBool"); |
| using PromotedIntT = typename PromotedIntegerType<IntT>::type; |
| PromotedIntT value = 0; |
| read(angle::byte_span_from_ref(value)); |
| ASSERT(angle::IsValueInRangeForNumericType<IntT>(value)); |
| return static_cast<IntT>(value); |
| } |
| |
| template <class IntT> |
| void readInt(IntT *outValue) |
| { |
| *outValue = readInt<IntT>(); |
| } |
| |
| template <class T> |
| void readVector(std::vector<T> *param) |
| { |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| ASSERT(param->empty()); |
| size_t size = readInt<size_t>(); |
| if (size > 0) |
| { |
| param->resize(size); |
| readBytes(angle::as_writable_byte_span(*param)); |
| } |
| } |
| |
| template <typename E, typename T> |
| void readPackedEnumMap(angle::PackedEnumMap<E, T> *param) |
| { |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| readBytes(angle::as_writable_byte_span(*param)); |
| } |
| |
| template <class T> |
| void readStruct(T *param) |
| { |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| readBytes(angle::byte_span_from_ref(*param)); |
| } |
| |
| template <class EnumT> |
| EnumT readEnum() |
| { |
| using UnderlyingType = typename std::underlying_type<EnumT>::type; |
| return static_cast<EnumT>(readInt<UnderlyingType>()); |
| } |
| |
| template <class EnumT> |
| void readEnum(EnumT *outValue) |
| { |
| *outValue = readEnum<EnumT>(); |
| } |
| |
| bool readBool() |
| { |
| int value = 0; |
| read(angle::byte_span_from_ref(value)); |
| return value > 0; |
| } |
| |
| void readBool(bool *outValue) { *outValue = readBool(); } |
| |
| void readBytes(angle::Span<uint8_t> outArray) { read(outArray); } |
| |
| std::string readString() |
| { |
| std::string outString; |
| readString(&outString); |
| return outString; |
| } |
| |
| void readString(std::string *v) |
| { |
| size_t length; |
| readInt(&length); |
| |
| if (mError) |
| { |
| return; |
| } |
| |
| angle::CheckedNumeric<size_t> checkedOffset(mOffset); |
| checkedOffset += length; |
| |
| if (!checkedOffset.IsValid() || checkedOffset.ValueOrDie() > mData.size()) |
| { |
| mError = true; |
| return; |
| } |
| auto char_span = angle::as_chars(mData).subspan(mOffset, length); |
| v->assign(char_span.data(), char_span.size()); |
| mOffset = checkedOffset.ValueOrDie(); |
| } |
| |
| float readFloat() |
| { |
| float f = 0.0f; |
| read(angle::byte_span_from_ref(f)); |
| return f; |
| } |
| |
| void skip(size_t length) |
| { |
| angle::CheckedNumeric<size_t> checkedOffset(mOffset); |
| checkedOffset += length; |
| |
| if (!checkedOffset.IsValid() || checkedOffset.ValueOrDie() > mData.size()) |
| { |
| mError = true; |
| return; |
| } |
| |
| mOffset = checkedOffset.ValueOrDie(); |
| } |
| |
| bool error() const { return mError; } |
| bool endOfStream() const { return mOffset == mData.size(); } |
| |
| // data() and size() methods allow implicit conversion to span. |
| const uint8_t *data() const { return mData.data(); } |
| size_t size() const { return mData.size(); } |
| |
| angle::Span<const uint8_t> remainingSpan() const { return mData.subspan(mOffset); } |
| |
| private: |
| void read(angle::Span<uint8_t> dstSpan) |
| { |
| angle::CheckedNumeric<size_t> checkedOffset(mOffset); |
| checkedOffset += dstSpan.size(); |
| |
| if (!checkedOffset.IsValid() || checkedOffset.ValueOrDie() > mData.size()) |
| { |
| mError = true; |
| return; |
| } |
| |
| angle::Span<const uint8_t> srcSpan = mData.subspan(mOffset, dstSpan.size()); |
| angle::SpanMemcpy(dstSpan, srcSpan); |
| mOffset = checkedOffset.ValueOrDie(); |
| } |
| |
| bool mError = false; |
| size_t mOffset = 0; |
| angle::Span<const uint8_t> mData; |
| }; |
| |
| class BinaryOutputStream : angle::NonCopyable |
| { |
| public: |
| BinaryOutputStream() = default; |
| ~BinaryOutputStream() = default; |
| |
| // writeInt also handles bool types |
| template <class IntT> |
| void writeInt(IntT param) |
| { |
| static_assert(std::is_integral<IntT>::value, "Not an integral type"); |
| static_assert(!std::is_same<bool, std::remove_cv<IntT>()>(), "Use writeBool"); |
| using PromotedIntT = typename PromotedIntegerType<IntT>::type; |
| ASSERT(angle::IsValueInRangeForNumericType<PromotedIntT>(param)); |
| PromotedIntT intValue = static_cast<PromotedIntT>(param); |
| write(angle::byte_span_from_ref(intValue)); |
| } |
| |
| // Specialized writeInt for values that can also be exactly -1. |
| template <class UintT> |
| void writeIntOrNegOne(UintT param) |
| { |
| if (param == static_cast<UintT>(-1)) |
| { |
| writeInt(-1); |
| } |
| else |
| { |
| writeInt(param); |
| } |
| } |
| |
| template <class T> |
| void writeVector(const std::vector<T> ¶m) |
| { |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| writeInt(param.size()); |
| if (param.size() > 0) |
| { |
| writeBytes(angle::as_byte_span(param)); |
| } |
| } |
| |
| template <typename E, typename T> |
| void writePackedEnumMap(const angle::PackedEnumMap<E, T> ¶m) |
| { |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| writeBytes(angle::as_byte_span(param)); |
| } |
| |
| template <class T> |
| void writeStruct(const T ¶m) |
| { |
| static_assert(!std::is_pointer<T>::value, |
| "Must pass in a struct, not the pointer to struct"); |
| static_assert(std::is_trivially_copyable<T>(), "must be memcpy-able"); |
| writeBytes(angle::byte_span_from_ref(param)); |
| } |
| |
| template <class EnumT> |
| void writeEnum(EnumT param) |
| { |
| using UnderlyingType = typename std::underlying_type<EnumT>::type; |
| writeInt<UnderlyingType>(static_cast<UnderlyingType>(param)); |
| } |
| |
| void writeString(std::string_view v) |
| { |
| writeInt(v.size()); |
| write(angle::as_byte_span(v)); |
| } |
| |
| void writeBytes(angle::Span<const uint8_t> bytes) { write(bytes); } |
| |
| void writeBool(bool value) |
| { |
| const int intValue = value ? 1 : 0; |
| write(angle::byte_span_from_ref(intValue)); |
| } |
| |
| void writeFloat(float value) { write(angle::byte_span_from_ref(value)); } |
| |
| // data() and size() methods allow implicit conversion to span. |
| const uint8_t *data() const { return mData.data(); } |
| size_t size() const { return mData.size(); } |
| |
| // No further use of this stream allowed after data is taken. |
| std::vector<uint8_t> takeData() { return std::move(mData); } |
| |
| private: |
| void write(angle::Span<const uint8_t> srcSpan) |
| { |
| mData.insert(mData.end(), srcSpan.begin(), srcSpan.end()); |
| } |
| |
| std::vector<uint8_t> mData; |
| }; |
| |
| } // namespace gl |
| |
| #endif // COMMON_BINARYSTREAM_H_ |