// // 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 #include #include #include #include #include "common/PackedEnums.h" #include "common/angleutils.h" #include "common/span.h" #include "common/span_util.h" namespace gl { template struct PromotedIntegerType { using type = typename std::conditional< std::is_signed::value, typename std::conditional::type, typename std::conditional::type>::type; }; class BinaryInputStream : angle::NonCopyable { public: BinaryInputStream(angle::Span data) : mData(data) {} // readInt will generate an error for bool types template IntT readInt() { static_assert(!std::is_same()>(), "Use readBool"); using PromotedIntT = typename PromotedIntegerType::type; PromotedIntT value = 0; read(angle::byte_span_from_ref(value)); ASSERT(angle::IsValueInRangeForNumericType(value)); return static_cast(value); } template void readInt(IntT *outValue) { *outValue = readInt(); } template void readVector(std::vector *param) { static_assert(std::is_trivially_copyable(), "must be memcpy-able"); ASSERT(param->empty()); size_t size = readInt(); if (size > 0) { param->resize(size); readBytes(angle::as_writable_byte_span(*param)); } } template void readPackedEnumMap(angle::PackedEnumMap *param) { static_assert(std::is_trivially_copyable(), "must be memcpy-able"); readBytes(angle::as_writable_byte_span(*param)); } template void readStruct(T *param) { static_assert(std::is_trivially_copyable(), "must be memcpy-able"); readBytes(angle::byte_span_from_ref(*param)); } template EnumT readEnum() { using UnderlyingType = typename std::underlying_type::type; return static_cast(readInt()); } template void readEnum(EnumT *outValue) { *outValue = readEnum(); } 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 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 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 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 remainingSpan() const { return mData.subspan(mOffset); } private: void read(angle::Span dstSpan) { angle::CheckedNumeric checkedOffset(mOffset); checkedOffset += dstSpan.size(); if (!checkedOffset.IsValid() || checkedOffset.ValueOrDie() > mData.size()) { mError = true; return; } angle::Span srcSpan = mData.subspan(mOffset, dstSpan.size()); angle::SpanMemcpy(dstSpan, srcSpan); mOffset = checkedOffset.ValueOrDie(); } bool mError = false; size_t mOffset = 0; angle::Span mData; }; class BinaryOutputStream : angle::NonCopyable { public: BinaryOutputStream() = default; ~BinaryOutputStream() = default; // writeInt also handles bool types template void writeInt(IntT param) { static_assert(std::is_integral::value, "Not an integral type"); static_assert(!std::is_same()>(), "Use writeBool"); using PromotedIntT = typename PromotedIntegerType::type; ASSERT(angle::IsValueInRangeForNumericType(param)); PromotedIntT intValue = static_cast(param); write(angle::byte_span_from_ref(intValue)); } // Specialized writeInt for values that can also be exactly -1. template void writeIntOrNegOne(UintT param) { if (param == static_cast(-1)) { writeInt(-1); } else { writeInt(param); } } template void writeVector(const std::vector ¶m) { static_assert(std::is_trivially_copyable(), "must be memcpy-able"); writeInt(param.size()); if (param.size() > 0) { writeBytes(angle::as_byte_span(param)); } } template void writePackedEnumMap(const angle::PackedEnumMap ¶m) { static_assert(std::is_trivially_copyable(), "must be memcpy-able"); writeBytes(angle::as_byte_span(param)); } template void writeStruct(const T ¶m) { static_assert(!std::is_pointer::value, "Must pass in a struct, not the pointer to struct"); static_assert(std::is_trivially_copyable(), "must be memcpy-able"); writeBytes(angle::byte_span_from_ref(param)); } template void writeEnum(EnumT param) { using UnderlyingType = typename std::underlying_type::type; writeInt(static_cast(param)); } void writeString(std::string_view v) { writeInt(v.size()); write(angle::as_byte_span(v)); } void writeBytes(angle::Span 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 takeData() { return std::move(mData); } private: void write(angle::Span srcSpan) { mData.insert(mData.end(), srcSpan.begin(), srcSpan.end()); } std::vector mData; }; } // namespace gl #endif // COMMON_BINARYSTREAM_H_