/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef BUFFER_READER_H_ #define BUFFER_READER_H_ #include #include "mozilla/EndianUtils.h" #include "nscore.h" #include "nsTArray.h" #include "MediaData.h" #include "MediaSpan.h" #include "mozilla/Logging.h" #include "mozilla/Result.h" namespace mozilla { extern mozilla::LazyLogModule gMP4MetadataLog; class MOZ_RAII BufferReader { public: BufferReader() : mPtr(nullptr), mRemaining(0), mLength(0) {} BufferReader(const uint8_t* aData, size_t aSize) : mPtr(aData), mRemaining(aSize), mLength(aSize) {} template explicit BufferReader(const AutoTArray& aData) : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length()) {} explicit BufferReader(const nsTArray& aData) : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length()) {} explicit BufferReader(const mozilla::MediaByteBuffer* aData) : mPtr(aData->Elements()), mRemaining(aData->Length()), mLength(aData->Length()) {} explicit BufferReader(const mozilla::MediaSpan& aData) : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length()) {} void SetData(const nsTArray& aData) { MOZ_ASSERT(!mPtr && !mRemaining); mPtr = aData.Elements(); mRemaining = aData.Length(); mLength = mRemaining; } ~BufferReader() = default; size_t Offset() const { return mLength - mRemaining; } size_t Remaining() const { return mRemaining; } mozilla::Result ReadU8() { auto ptr = Read(1); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return *ptr; } mozilla::Result ReadU16() { auto ptr = Read(2); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readUint16(ptr); } mozilla::Result ReadLE16() { auto ptr = Read(2); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::LittleEndian::readInt16(ptr); } mozilla::Result ReadU24() { auto ptr = Read(3); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; } mozilla::Result Read24() { return ReadU24().map([](uint32_t x) { return (int32_t)x; }); } mozilla::Result ReadLE24() { auto ptr = Read(3); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } int32_t result = int32_t(ptr[2] << 16 | ptr[1] << 8 | ptr[0]); if (result & 0x00800000u) { result -= 0x1000000; } return result; } mozilla::Result ReadU32() { auto ptr = Read(4); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readUint32(ptr); } mozilla::Result Read32() { auto ptr = Read(4); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readInt32(ptr); } mozilla::Result ReadLEU32() { auto ptr = Read(4); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::LittleEndian::readUint32(ptr); } mozilla::Result ReadU64() { auto ptr = Read(8); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readUint64(ptr); } mozilla::Result Read64() { auto ptr = Read(8); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readInt64(ptr); } const uint8_t* Read(size_t aCount) { if (aCount > mRemaining) { mPtr += mRemaining; mRemaining = 0; return nullptr; } mRemaining -= aCount; const uint8_t* result = mPtr; mPtr += aCount; return result; } const uint8_t* Rewind(size_t aCount) { MOZ_ASSERT(aCount <= Offset()); size_t rewind = Offset(); if (aCount < rewind) { rewind = aCount; } mRemaining += rewind; mPtr -= rewind; return mPtr; } mozilla::Result PeekU8() const { auto ptr = Peek(1); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return *ptr; } mozilla::Result PeekU16() const { auto ptr = Peek(2); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readUint16(ptr); } mozilla::Result PeekU24() const { auto ptr = Peek(3); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; } mozilla::Result Peek24() const { return PeekU24().map([](uint32_t x) { return (int32_t)x; }); } mozilla::Result PeekU32() { auto ptr = Peek(4); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return mozilla::Err(NS_ERROR_FAILURE); } return mozilla::BigEndian::readUint32(ptr); } const uint8_t* Peek(size_t aCount) const { if (aCount > mRemaining) { return nullptr; } return mPtr; } const uint8_t* Seek(size_t aOffset) { if (aOffset >= mLength) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure, offset: %zu", __func__, aOffset)); return nullptr; } mPtr = mPtr - Offset() + aOffset; mRemaining = mLength - aOffset; return mPtr; } const uint8_t* Reset() { mPtr -= Offset(); mRemaining = mLength; return mPtr; } uint32_t Align() const { return 4 - ((intptr_t)mPtr & 3); } template bool CanReadType() const { return mRemaining >= sizeof(T); } template T ReadType() { auto ptr = Read(sizeof(T)); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return 0; } // handle unaligned accesses by memcpying T ret; memcpy(&ret, ptr, sizeof(T)); return ret; } template [[nodiscard]] bool ReadArray(nsTArray& aDest, size_t aLength) { auto ptr = Read(aLength * sizeof(T)); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return false; } aDest.Clear(); aDest.AppendElements(reinterpret_cast(ptr), aLength); return true; } template [[nodiscard]] bool ReadArray(FallibleTArray& aDest, size_t aLength) { auto ptr = Read(aLength * sizeof(T)); if (!ptr) { MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error, ("%s: failure", __func__)); return false; } aDest.Clear(); if (!aDest.SetCapacity(aLength, mozilla::fallible)) { return false; } MOZ_ALWAYS_TRUE(aDest.AppendElements(reinterpret_cast(ptr), aLength, mozilla::fallible)); return true; } private: const uint8_t* mPtr; size_t mRemaining; size_t mLength; }; } // namespace mozilla #endif