зеркало из https://github.com/mozilla/gecko-dev.git
886 строки
27 KiB
C++
886 строки
27 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=4 et :
|
|
*/
|
|
/* 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 _QUEUEPARAMTRAITS_H_
|
|
#define _QUEUEPARAMTRAITS_H_ 1
|
|
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/IntegerRange.h"
|
|
#include "mozilla/ipc/ProtocolUtils.h"
|
|
#include "mozilla/ipc/SharedMemoryBasic.h"
|
|
#include "mozilla/ipc/Shmem.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/TypeTraits.h"
|
|
#include "nsString.h"
|
|
#include "WebGLTypes.h"
|
|
|
|
namespace mozilla {
|
|
namespace webgl {
|
|
|
|
enum class QueueStatus {
|
|
// Operation was successful
|
|
kSuccess,
|
|
// The operation failed because the queue isn't ready for it.
|
|
// Either the queue is too full for an insert or too empty for a remove.
|
|
// The operation may succeed if retried.
|
|
kNotReady,
|
|
// The operation required more room than the queue supports.
|
|
// It should not be retried -- it will always fail.
|
|
kTooSmall,
|
|
// The operation failed for some reason that is unrecoverable.
|
|
// All values below this value indicate a fata error.
|
|
kFatalError,
|
|
// Fatal error: Internal processing ran out of memory. This is likely e.g.
|
|
// during de-serialization.
|
|
kOOMError,
|
|
};
|
|
|
|
inline bool IsSuccess(QueueStatus status) {
|
|
return status == QueueStatus::kSuccess;
|
|
}
|
|
|
|
inline bool operator!(const QueueStatus status) { return !IsSuccess(status); }
|
|
|
|
template <typename T>
|
|
struct RemoveCVR {
|
|
typedef typename std::remove_reference<typename std::remove_cv<T>::type>::type
|
|
Type;
|
|
};
|
|
|
|
inline size_t UsedBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) {
|
|
return (aRead <= aWrite) ? aWrite - aRead
|
|
: (aQueueBufferSize - aRead) + aWrite;
|
|
}
|
|
|
|
inline size_t FreeBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) {
|
|
// Remember, queueSize is queueBufferSize-1
|
|
return (aQueueBufferSize - 1) - UsedBytes(aQueueBufferSize, aRead, aWrite);
|
|
}
|
|
|
|
template <typename T>
|
|
struct IsTriviallySerializable
|
|
: public std::integral_constant<bool, std::is_arithmetic<T>::value &&
|
|
!std::is_same<T, bool>::value> {};
|
|
|
|
/**
|
|
* QueueParamTraits provide the user with a way to implement PCQ argument
|
|
* (de)serialization. It uses a PcqView, which permits the system to
|
|
* abandon all changes to the underlying PCQ if any operation fails.
|
|
*
|
|
* The transactional nature of PCQ operations make the ideal behavior a bit
|
|
* complex. Since the PCQ has a fixed amount of memory available to it,
|
|
* TryInsert operations operations are expected to sometimes fail and be
|
|
* re-issued later. We want these failures to be inexpensive. The same
|
|
* goes for TryRemove, which fails when there isn't enough data in
|
|
* the queue yet for them to complete.
|
|
*
|
|
* Their expected interface is:
|
|
*
|
|
* template<> struct QueueParamTraits<typename RemoveCVR<Arg>::Type> {
|
|
* // Write data from aArg into the PCQ.
|
|
* static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg)
|
|
* {...};
|
|
*
|
|
* // Read data from the PCQ into aArg, or just skip the data if aArg is null.
|
|
* static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...}
|
|
* };
|
|
*/
|
|
template <typename Arg>
|
|
struct QueueParamTraits; // Todo: s/QueueParamTraits/SizedParamTraits/
|
|
|
|
/**
|
|
* The marshaller handles all data insertion into the queue.
|
|
*/
|
|
class Marshaller {
|
|
public:
|
|
static QueueStatus WriteObject(uint8_t* aQueue, size_t aQueueBufferSize,
|
|
size_t aRead, size_t* aWrite, const void* aArg,
|
|
size_t aArgLength) {
|
|
const uint8_t* buf = reinterpret_cast<const uint8_t*>(aArg);
|
|
if (FreeBytes(aQueueBufferSize, aRead, *aWrite) < aArgLength) {
|
|
return QueueStatus::kNotReady;
|
|
}
|
|
|
|
if (*aWrite + aArgLength <= aQueueBufferSize) {
|
|
memcpy(aQueue + *aWrite, buf, aArgLength);
|
|
} else {
|
|
size_t firstLen = aQueueBufferSize - *aWrite;
|
|
memcpy(aQueue + *aWrite, buf, firstLen);
|
|
memcpy(aQueue, &buf[firstLen], aArgLength - firstLen);
|
|
}
|
|
*aWrite = (*aWrite + aArgLength) % aQueueBufferSize;
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
static QueueStatus ReadObject(const uint8_t* aQueue, size_t aQueueBufferSize,
|
|
size_t* aRead, size_t aWrite, void* aArg,
|
|
size_t aArgLength) {
|
|
if (UsedBytes(aQueueBufferSize, *aRead, aWrite) < aArgLength) {
|
|
return QueueStatus::kNotReady;
|
|
}
|
|
|
|
if (aArg) {
|
|
uint8_t* buf = reinterpret_cast<uint8_t*>(aArg);
|
|
if (*aRead + aArgLength <= aQueueBufferSize) {
|
|
memcpy(buf, aQueue + *aRead, aArgLength);
|
|
} else {
|
|
size_t firstLen = aQueueBufferSize - *aRead;
|
|
memcpy(buf, aQueue + *aRead, firstLen);
|
|
memcpy(&buf[firstLen], aQueue, aArgLength - firstLen);
|
|
}
|
|
}
|
|
|
|
*aRead = (*aRead + aArgLength) % aQueueBufferSize;
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
inline Range<T> AsRange(T* const begin, T* const end) {
|
|
const auto size = MaybeAs<size_t>(end - begin);
|
|
MOZ_RELEASE_ASSERT(size);
|
|
return {begin, *size};
|
|
}
|
|
|
|
/**
|
|
* Used to give QueueParamTraits a way to write to the Producer without
|
|
* actually altering it, in case the transaction fails.
|
|
* THis object maintains the error state of the transaction and
|
|
* discards commands issued after an error is encountered.
|
|
*/
|
|
template <typename _Producer>
|
|
class ProducerView {
|
|
public:
|
|
using Producer = _Producer;
|
|
|
|
ProducerView(Producer* aProducer, size_t aRead, size_t* aWrite)
|
|
: mProducer(aProducer),
|
|
mRead(aRead),
|
|
mWrite(aWrite),
|
|
mStatus(QueueStatus::kSuccess) {}
|
|
|
|
template <typename T>
|
|
QueueStatus WriteFromRange(const Range<const T>& src) {
|
|
if (!mStatus) return mStatus;
|
|
mProducer->WriteFromRange(src);
|
|
return mStatus;
|
|
}
|
|
/**
|
|
* Copy bytes from aBuffer to the producer if there is enough room.
|
|
* aBufferSize must not be 0.
|
|
*/
|
|
template <typename T>
|
|
inline QueueStatus Write(const T* begin, const T* end) {
|
|
MOZ_RELEASE_ASSERT(begin <= end);
|
|
if (!mStatus) return mStatus;
|
|
WriteFromRange(AsRange(begin, end));
|
|
return mStatus;
|
|
}
|
|
|
|
template <typename T>
|
|
inline QueueStatus WritePod(const T& in) {
|
|
static_assert(std::is_trivially_copyable_v<T>);
|
|
const auto begin = reinterpret_cast<const uint8_t*>(&in);
|
|
return Write(begin, begin + sizeof(T));
|
|
}
|
|
|
|
/**
|
|
* Serialize aArg using Arg's QueueParamTraits.
|
|
*/
|
|
template <typename Arg>
|
|
QueueStatus WriteParam(const Arg& aArg) {
|
|
return mozilla::webgl::QueueParamTraits<
|
|
typename RemoveCVR<Arg>::Type>::Write(*this, aArg);
|
|
}
|
|
|
|
QueueStatus GetStatus() { return mStatus; }
|
|
|
|
private:
|
|
Producer* mProducer;
|
|
size_t mRead;
|
|
size_t* mWrite;
|
|
QueueStatus mStatus;
|
|
};
|
|
|
|
/**
|
|
* Used to give QueueParamTraits a way to read from the Consumer without
|
|
* actually altering it, in case the transaction fails.
|
|
*/
|
|
template <typename _Consumer>
|
|
class ConsumerView {
|
|
public:
|
|
using Consumer = _Consumer;
|
|
|
|
ConsumerView(Consumer* aConsumer, size_t* aRead, size_t aWrite)
|
|
: mConsumer(aConsumer),
|
|
mRead(aRead),
|
|
mWrite(aWrite),
|
|
mStatus(QueueStatus::kSuccess) {}
|
|
|
|
/**
|
|
* Read bytes from the consumer if there is enough data. aBuffer may
|
|
* be null (in which case the data is skipped)
|
|
*/
|
|
template <typename T>
|
|
inline QueueStatus Read(T* const destBegin, T* const destEnd) {
|
|
MOZ_ASSERT(destBegin);
|
|
MOZ_RELEASE_ASSERT(destBegin <= destEnd);
|
|
if (!mStatus) return mStatus;
|
|
|
|
const auto dest = AsRange(destBegin, destEnd);
|
|
const auto view = ReadRange<T>(dest.length());
|
|
if (!view) return mStatus;
|
|
const auto byteSize = ByteSize(dest);
|
|
if (byteSize) {
|
|
memcpy(dest.begin().get(), view->begin().get(), byteSize);
|
|
}
|
|
return mStatus;
|
|
}
|
|
|
|
/// Return a view wrapping the shmem.
|
|
template <typename T>
|
|
inline Maybe<Range<const T>> ReadRange(const size_t elemCount) {
|
|
if (!mStatus) return {};
|
|
const auto view = mConsumer->template ReadRange<T>(elemCount);
|
|
if (!view) {
|
|
mStatus = QueueStatus::kTooSmall;
|
|
}
|
|
return view;
|
|
}
|
|
|
|
template <typename T>
|
|
inline QueueStatus ReadPod(T* out) {
|
|
static_assert(std::is_trivially_copyable_v<T>);
|
|
const auto begin = reinterpret_cast<uint8_t*>(out);
|
|
return Read(begin, begin + sizeof(T));
|
|
}
|
|
|
|
/**
|
|
* Deserialize aArg using Arg's QueueParamTraits.
|
|
* If the return value is not Success then aArg is not changed.
|
|
*/
|
|
template <typename Arg>
|
|
QueueStatus ReadParam(Arg* aArg) {
|
|
MOZ_ASSERT(aArg);
|
|
return mozilla::webgl::QueueParamTraits<std::remove_cv_t<Arg>>::Read(*this,
|
|
aArg);
|
|
}
|
|
|
|
QueueStatus GetStatus() { return mStatus; }
|
|
|
|
private:
|
|
Consumer* mConsumer;
|
|
size_t* mRead;
|
|
size_t mWrite;
|
|
QueueStatus mStatus;
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
/**
|
|
* True for types that can be (de)serialized by memcpy.
|
|
*/
|
|
template <typename Arg>
|
|
struct QueueParamTraits {
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView, const Arg& aArg) {
|
|
static_assert(mozilla::webgl::template IsTriviallySerializable<Arg>::value,
|
|
"No QueueParamTraits specialization was found for this type "
|
|
"and it does not satisfy IsTriviallySerializable.");
|
|
// Write self as binary
|
|
const auto begin = &aArg;
|
|
return aProducerView.Write(begin, begin + 1);
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, Arg* aArg) {
|
|
static_assert(mozilla::webgl::template IsTriviallySerializable<Arg>::value,
|
|
"No QueueParamTraits specialization was found for this type "
|
|
"and it does not satisfy IsTriviallySerializable.");
|
|
// Read self as binary
|
|
return aConsumerView.Read(aArg, aArg + 1);
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <>
|
|
struct QueueParamTraits<bool> {
|
|
using ParamType = bool;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
uint8_t temp = aArg ? 1 : 0;
|
|
return aProducerView.WriteParam(temp);
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
uint8_t temp;
|
|
if (IsSuccess(aConsumerView.ReadParam(&temp))) {
|
|
MOZ_ASSERT(temp == 1 || temp == 0);
|
|
*aArg = temp ? true : false;
|
|
}
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
// Adapted from IPC::EnumSerializer, this class safely handles enum values,
|
|
// validating that they are in range using the same EnumValidators as IPDL
|
|
// (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive).
|
|
template <typename E, typename EnumValidator>
|
|
struct EnumSerializer {
|
|
typedef E ParamType;
|
|
typedef typename std::underlying_type<E>::type DataType;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aValue) {
|
|
MOZ_RELEASE_ASSERT(EnumValidator::IsLegalValue(aValue));
|
|
return aProducerView.WriteParam(DataType(aValue));
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
|
|
DataType value;
|
|
if (!aConsumerView.ReadParam(&value)) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::IPCReadErrorReason, "Bad iter"_ns);
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
if (!EnumValidator::IsLegalValue(ParamType(value))) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::IPCReadErrorReason, "Illegal value"_ns);
|
|
return QueueStatus::kFatalError;
|
|
}
|
|
|
|
*aResult = ParamType(value);
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
using IPC::ContiguousEnumValidator;
|
|
using IPC::ContiguousEnumValidatorInclusive;
|
|
|
|
template <typename E, E MinLegal, E HighBound>
|
|
struct ContiguousEnumSerializer
|
|
: EnumSerializer<E, ContiguousEnumValidator<E, MinLegal, HighBound>> {};
|
|
|
|
template <typename E, E MinLegal, E MaxLegal>
|
|
struct ContiguousEnumSerializerInclusive
|
|
: EnumSerializer<E,
|
|
ContiguousEnumValidatorInclusive<E, MinLegal, MaxLegal>> {
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <>
|
|
struct QueueParamTraits<QueueStatus>
|
|
: public ContiguousEnumSerializerInclusive<
|
|
QueueStatus, QueueStatus::kSuccess, QueueStatus::kOOMError> {};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <>
|
|
struct QueueParamTraits<webgl::TexUnpackBlobDesc> {
|
|
using ParamType = webgl::TexUnpackBlobDesc;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& view, const ParamType& in) {
|
|
MOZ_ASSERT(!in.image);
|
|
const bool isSurf = bool(in.surf);
|
|
if (!view.WriteParam(in.imageTarget) || !view.WriteParam(in.size) ||
|
|
!view.WriteParam(in.srcAlphaType) || !view.WriteParam(in.unpacking) ||
|
|
!view.WriteParam(in.cpuData) || !view.WriteParam(in.pboOffset) ||
|
|
!view.WriteParam(isSurf)) {
|
|
return view.GetStatus();
|
|
}
|
|
if (isSurf) {
|
|
gfx::DataSourceSurface::ScopedMap map(in.surf,
|
|
gfx::DataSourceSurface::READ);
|
|
if (!map.IsMapped()) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
const auto& surfSize = in.surf->GetSize();
|
|
const auto stride = *MaybeAs<size_t>(map.GetStride());
|
|
if (!view.WriteParam(surfSize) ||
|
|
!view.WriteParam(in.surf->GetFormat()) || !view.WriteParam(stride)) {
|
|
return view.GetStatus();
|
|
}
|
|
|
|
const size_t dataSize = stride * surfSize.height;
|
|
const auto& begin = map.GetData();
|
|
if (!view.Write(begin, begin + dataSize)) {
|
|
return view.GetStatus();
|
|
}
|
|
}
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& view, ParamType* const out) {
|
|
bool isSurf;
|
|
if (!view.ReadParam(&out->imageTarget) || !view.ReadParam(&out->size) ||
|
|
!view.ReadParam(&out->srcAlphaType) ||
|
|
!view.ReadParam(&out->unpacking) || !view.ReadParam(&out->cpuData) ||
|
|
!view.ReadParam(&out->pboOffset) || !view.ReadParam(&isSurf)) {
|
|
return view.GetStatus();
|
|
}
|
|
if (isSurf) {
|
|
gfx::IntSize surfSize;
|
|
gfx::SurfaceFormat format;
|
|
size_t stride;
|
|
if (!view.ReadParam(&surfSize) || !view.ReadParam(&format) ||
|
|
!view.ReadParam(&stride)) {
|
|
return view.GetStatus();
|
|
}
|
|
out->surf = gfx::Factory::CreateDataSourceSurfaceWithStride(
|
|
surfSize, format, stride, true);
|
|
if (!out->surf) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
|
|
gfx::DataSourceSurface::ScopedMap map(out->surf,
|
|
gfx::DataSourceSurface::WRITE);
|
|
if (!map.IsMapped()) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
const size_t dataSize = stride * surfSize.height;
|
|
const auto& begin = map.GetData();
|
|
if (!view.Read(begin, begin + dataSize)) {
|
|
return view.GetStatus();
|
|
}
|
|
}
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <>
|
|
struct QueueParamTraits<nsACString> {
|
|
using ParamType = nsACString;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) {
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
uint32_t len = aArg.Length();
|
|
if ((!aProducerView.WriteParam(len)) || (len == 0)) {
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
return aProducerView.Write(aArg.BeginReading(), len);
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
bool isVoid = false;
|
|
if (!IsSuccess(aConsumerView.ReadParam(&isVoid))) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
aArg->SetIsVoid(isVoid);
|
|
if (isVoid) {
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
uint32_t len = 0;
|
|
if (!IsSuccess(aConsumerView.ReadParam(&len))) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (len == 0) {
|
|
*aArg = "";
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
char* buf = new char[len + 1];
|
|
if (!buf) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
if (!IsSuccess(aConsumerView.Read(buf, len))) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
buf[len] = '\0';
|
|
aArg->Adopt(buf, len);
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct QueueParamTraits<nsAString> {
|
|
using ParamType = nsAString;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) {
|
|
return aProducerView.GetStatus();
|
|
}
|
|
// DLP: No idea if this includes null terminator
|
|
uint32_t len = aArg.Length();
|
|
if ((!aProducerView.WriteParam(len)) || (len == 0)) {
|
|
return aProducerView.GetStatus();
|
|
}
|
|
constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type);
|
|
return aProducerView.Write(aArg.BeginReading(), len * sizeofchar);
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
bool isVoid = false;
|
|
if (!aConsumerView.ReadParam(&isVoid)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
aArg->SetIsVoid(isVoid);
|
|
if (isVoid) {
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
// DLP: No idea if this includes null terminator
|
|
uint32_t len = 0;
|
|
if (!aConsumerView.ReadParam(&len)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (len == 0) {
|
|
*aArg = nsString();
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
uint32_t sizeofchar = sizeof(typename ParamType::char_type);
|
|
typename ParamType::char_type* buf = nullptr;
|
|
buf = static_cast<typename ParamType::char_type*>(
|
|
malloc((len + 1) * sizeofchar));
|
|
if (!buf) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
|
|
if (!aConsumerView.Read(buf, len * sizeofchar)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
buf[len] = L'\0';
|
|
aArg->Adopt(buf, len);
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct QueueParamTraits<nsCString> : public QueueParamTraits<nsACString> {
|
|
using ParamType = nsCString;
|
|
};
|
|
|
|
template <>
|
|
struct QueueParamTraits<nsString> : public QueueParamTraits<nsAString> {
|
|
using ParamType = nsString;
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <typename NSTArrayType,
|
|
bool =
|
|
IsTriviallySerializable<typename NSTArrayType::elem_type>::value>
|
|
struct NSArrayQueueParamTraits;
|
|
|
|
// For ElementTypes that are !IsTriviallySerializable
|
|
template <typename _ElementType>
|
|
struct NSArrayQueueParamTraits<nsTArray<_ElementType>, false> {
|
|
using ElementType = _ElementType;
|
|
using ParamType = nsTArray<ElementType>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
aProducerView.WriteParam(aArg.Length());
|
|
for (auto& elt : aArg) {
|
|
aProducerView.WriteParam(elt);
|
|
}
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
size_t arrayLen;
|
|
if (!aConsumerView.ReadParam(&arrayLen)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (!aArg->AppendElements(arrayLen, fallible)) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
|
|
for (auto i : IntegerRange(arrayLen)) {
|
|
ElementType& elt = aArg->ElementAt(i);
|
|
aConsumerView.ReadParam(elt);
|
|
}
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
};
|
|
|
|
// For ElementTypes that are IsTriviallySerializable
|
|
template <typename _ElementType>
|
|
struct NSArrayQueueParamTraits<nsTArray<_ElementType>, true> {
|
|
using ElementType = _ElementType;
|
|
using ParamType = nsTArray<ElementType>;
|
|
|
|
// TODO: Are there alignment issues?
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
size_t arrayLen = aArg.Length();
|
|
aProducerView.WriteParam(arrayLen);
|
|
return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType));
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
size_t arrayLen;
|
|
if (!aConsumerView.ReadParam(&arrayLen)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (!aArg->AppendElements(arrayLen, fallible)) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
|
|
return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType));
|
|
}
|
|
};
|
|
|
|
template <typename ElementType>
|
|
struct QueueParamTraits<nsTArray<ElementType>>
|
|
: public NSArrayQueueParamTraits<nsTArray<ElementType>> {
|
|
using ParamType = nsTArray<ElementType>;
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <typename ArrayType,
|
|
bool =
|
|
IsTriviallySerializable<typename ArrayType::ElementType>::value>
|
|
struct ArrayQueueParamTraits;
|
|
|
|
// For ElementTypes that are !IsTriviallySerializable
|
|
template <typename _ElementType, size_t Length>
|
|
struct ArrayQueueParamTraits<Array<_ElementType, Length>, false> {
|
|
using ElementType = _ElementType;
|
|
using ParamType = Array<ElementType, Length>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
for (const auto& elt : aArg) {
|
|
aProducerView.WriteParam(elt);
|
|
}
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
for (auto& elt : *aArg) {
|
|
aConsumerView.ReadParam(elt);
|
|
}
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
};
|
|
|
|
// For ElementTypes that are IsTriviallySerializable
|
|
template <typename _ElementType, size_t Length>
|
|
struct ArrayQueueParamTraits<Array<_ElementType, Length>, true> {
|
|
using ElementType = _ElementType;
|
|
using ParamType = Array<ElementType, Length>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length]));
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length]));
|
|
}
|
|
};
|
|
|
|
template <typename ElementType, size_t Length>
|
|
struct QueueParamTraits<Array<ElementType, Length>>
|
|
: public ArrayQueueParamTraits<Array<ElementType, Length>> {
|
|
using ParamType = Array<ElementType, Length>;
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <typename ElementType>
|
|
struct QueueParamTraits<Maybe<ElementType>> {
|
|
using ParamType = Maybe<ElementType>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
aProducerView.WriteParam(static_cast<bool>(aArg));
|
|
return aArg ? aProducerView.WriteParam(aArg.ref())
|
|
: aProducerView.GetStatus();
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
bool isSome;
|
|
if (!aConsumerView.ReadParam(&isSome)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (!isSome) {
|
|
aArg->reset();
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
aArg->emplace();
|
|
return aConsumerView.ReadParam(aArg->ptr());
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
// Maybe<Variant> needs special behavior since Variant is not default
|
|
// constructable. The Variant's first type must be default constructible.
|
|
template <typename T, typename... Ts>
|
|
struct QueueParamTraits<Maybe<Variant<T, Ts...>>> {
|
|
using ParamType = Maybe<Variant<T, Ts...>>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
aProducerView.WriteParam(aArg.mIsSome);
|
|
return (aArg.mIsSome) ? aProducerView.WriteParam(aArg.ref())
|
|
: aProducerView.GetStatus();
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
bool isSome;
|
|
if (!aConsumerView.ReadParam(&isSome)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
if (!isSome) {
|
|
aArg->reset();
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
aArg->emplace(VariantType<T>());
|
|
return aConsumerView.ReadParam(aArg->ptr());
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <typename TypeA, typename TypeB>
|
|
struct QueueParamTraits<std::pair<TypeA, TypeB>> {
|
|
using ParamType = std::pair<TypeA, TypeB>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
aProducerView.WriteParam(aArg.first());
|
|
return aProducerView.WriteParam(aArg.second());
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
aConsumerView.ReadParam(aArg->first());
|
|
return aConsumerView.ReadParam(aArg->second());
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
struct QueueParamTraits<UniquePtr<T>> {
|
|
using ParamType = UniquePtr<T>;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView,
|
|
const ParamType& aArg) {
|
|
// TODO: Clean up move with PCQ
|
|
aProducerView.WriteParam(!static_cast<bool>(aArg));
|
|
if (aArg && aProducerView.WriteParam(*aArg.get())) {
|
|
const_cast<ParamType&>(aArg).reset();
|
|
}
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
|
|
bool isNull;
|
|
if (!aConsumerView.ReadParam(&isNull)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
if (isNull) {
|
|
aArg->reset(nullptr);
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
|
|
T* obj = nullptr;
|
|
obj = new T();
|
|
if (!obj) {
|
|
return QueueStatus::kOOMError;
|
|
}
|
|
aArg->reset(obj);
|
|
return aConsumerView.ReadParam(obj);
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
template <>
|
|
struct QueueParamTraits<mozilla::ipc::Shmem> {
|
|
using ParamType = mozilla::ipc::Shmem;
|
|
|
|
template <typename U>
|
|
static QueueStatus Write(ProducerView<U>& aProducerView, ParamType&& aParam) {
|
|
if (!aProducerView.WriteParam(
|
|
aParam.Id(mozilla::ipc::Shmem::PrivateIPDLCaller()))) {
|
|
return aProducerView.GetStatus();
|
|
}
|
|
|
|
aParam.RevokeRights(mozilla::ipc::Shmem::PrivateIPDLCaller());
|
|
aParam.forget(mozilla::ipc::Shmem::PrivateIPDLCaller());
|
|
}
|
|
|
|
template <typename U>
|
|
static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
|
|
ParamType::id_t id;
|
|
if (!aConsumerView.ReadParam(&id)) {
|
|
return aConsumerView.GetStatus();
|
|
}
|
|
|
|
mozilla::ipc::Shmem::SharedMemory* rawmem =
|
|
aConsumerView.LookupSharedMemory(id);
|
|
if (!rawmem) {
|
|
return QueueStatus::kFatalError;
|
|
}
|
|
|
|
*aResult = mozilla::ipc::Shmem(mozilla::ipc::Shmem::PrivateIPDLCaller(),
|
|
rawmem, id);
|
|
return QueueStatus::kSuccess;
|
|
}
|
|
};
|
|
|
|
} // namespace webgl
|
|
} // namespace mozilla
|
|
|
|
#endif // _QUEUEPARAMTRAITS_H_
|