mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
834b8edbaf
Previously we ran into crashes here on platforms that don't support unaligned accesses like ARM32. Differential Revision: https://phabricator.services.mozilla.com/D126391
321 lines
8.5 KiB
C++
321 lines
8.5 KiB
C++
/* 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 <string.h>
|
|
#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 <size_t S>
|
|
explicit BufferReader(const AutoTArray<uint8_t, S>& aData)
|
|
: mPtr(aData.Elements()),
|
|
mRemaining(aData.Length()),
|
|
mLength(aData.Length()) {}
|
|
explicit BufferReader(const nsTArray<uint8_t>& 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<uint8_t>& 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<uint8_t, nsresult> 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<uint16_t, nsresult> 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<int16_t, nsresult> 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<uint32_t, nsresult> 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<int32_t, nsresult> Read24() {
|
|
return ReadU24().map([](uint32_t x) { return (int32_t)x; });
|
|
}
|
|
|
|
mozilla::Result<int32_t, nsresult> 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<uint32_t, nsresult> 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<int32_t, nsresult> 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<uint32_t, nsresult> 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<uint64_t, nsresult> 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<int64_t, nsresult> 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<uint8_t, nsresult> 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<uint16_t, nsresult> 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<uint32_t, nsresult> 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<int32_t, nsresult> Peek24() const {
|
|
return PeekU24().map([](uint32_t x) { return (int32_t)x; });
|
|
}
|
|
|
|
mozilla::Result<uint32_t, nsresult> 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 <typename T>
|
|
bool CanReadType() const {
|
|
return mRemaining >= sizeof(T);
|
|
}
|
|
|
|
template <typename T>
|
|
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 <typename T>
|
|
[[nodiscard]] bool ReadArray(nsTArray<T>& 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<const T*>(ptr), aLength);
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
[[nodiscard]] bool ReadArray(FallibleTArray<T>& 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<const T*>(ptr),
|
|
aLength, mozilla::fallible));
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const uint8_t* mPtr;
|
|
size_t mRemaining;
|
|
size_t mLength;
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif
|