1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #ifndef _QUEUEPARAMTRAITS_H_
9 #define _QUEUEPARAMTRAITS_H_ 1
11 #include "ipc/EnumSerializer.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/IntegerRange.h"
15 #include "mozilla/ipc/ProtocolUtils.h"
16 #include "mozilla/ipc/SharedMemoryBasic.h"
17 #include "mozilla/Logging.h"
18 #include "mozilla/TimeStamp.h"
19 #include "nsExceptionHandler.h"
21 #include "WebGLTypes.h"
25 namespace mozilla::webgl
{
30 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
;
34 * QueueParamTraits provide the user with a way to implement PCQ argument
35 * (de)serialization. It uses a PcqView, which permits the system to
36 * abandon all changes to the underlying PCQ if any operation fails.
38 * The transactional nature of PCQ operations make the ideal behavior a bit
39 * complex. Since the PCQ has a fixed amount of memory available to it,
40 * TryInsert operations operations are expected to sometimes fail and be
41 * re-issued later. We want these failures to be inexpensive. The same
42 * goes for TryRemove, which fails when there isn't enough data in
43 * the queue yet for them to complete.
45 * Their expected interface is:
47 * template<> struct QueueParamTraits<typename RemoveCVR<Arg>::Type> {
48 * // Write data from aArg into the PCQ.
49 * static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg)
52 * // Read data from the PCQ into aArg, or just skip the data if aArg is null.
53 * static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...}
56 template <typename Arg
>
57 struct QueueParamTraits
; // Todo: s/QueueParamTraits/SizedParamTraits/
60 inline Range
<T
> AsRange(T
* const begin
, T
* const end
) {
61 const auto size
= MaybeAs
<size_t>(end
- begin
);
62 MOZ_RELEASE_ASSERT(size
);
63 return {begin
, *size
};
70 struct BytesAlwaysValidT
{
71 using non_cv
= typename
std::remove_cv
<T
>::type
;
72 static constexpr bool value
=
73 std::is_arithmetic
<T
>::value
&& !std::is_same
<non_cv
, bool>::value
;
75 static_assert(BytesAlwaysValidT
<float>::value
);
76 static_assert(!BytesAlwaysValidT
<bool>::value
);
77 static_assert(!BytesAlwaysValidT
<const bool>::value
);
78 static_assert(!BytesAlwaysValidT
<int*>::value
);
79 static_assert(BytesAlwaysValidT
<intptr_t>::value
);
81 template <class T
, size_t N
>
82 struct BytesAlwaysValidT
<std::array
<T
, N
>> {
83 static constexpr bool value
= BytesAlwaysValidT
<T
>::value
;
85 static_assert(BytesAlwaysValidT
<std::array
<int, 4>>::value
);
86 static_assert(!BytesAlwaysValidT
<std::array
<bool, 4>>::value
);
88 template <class T
, size_t N
>
89 struct BytesAlwaysValidT
<T
[N
]> {
90 static constexpr bool value
= BytesAlwaysValidT
<T
>::value
;
92 static_assert(BytesAlwaysValidT
<int[4]>::value
);
93 static_assert(!BytesAlwaysValidT
<bool[4]>::value
);
98 struct BytesAlwaysValidT
<webgl::UniformDataVal
> {
99 static constexpr bool value
= true;
105 * Used to give QueueParamTraits a way to write to the Producer without
106 * actually altering it, in case the transaction fails.
107 * THis object maintains the error state of the transaction and
108 * discards commands issued after an error is encountered.
110 template <typename _Producer
>
113 using Producer
= _Producer
;
115 explicit ProducerView(Producer
* aProducer
) : mProducer(aProducer
) {}
117 template <typename T
>
118 bool WriteFromRange(const Range
<const T
>& src
) {
119 static_assert(BytesAlwaysValidT
<T
>::value
);
120 if (MOZ_LIKELY(mOk
)) {
121 mOk
&= mProducer
->WriteFromRange(src
);
127 * Copy bytes from aBuffer to the producer if there is enough room.
128 * aBufferSize must not be 0.
130 template <typename T
>
131 inline bool Write(const T
* begin
, const T
* end
) {
132 MOZ_RELEASE_ASSERT(begin
<= end
);
133 return WriteFromRange(AsRange(begin
, end
));
137 * Serialize aArg using Arg's QueueParamTraits.
139 template <typename Arg
>
140 bool WriteParam(const Arg
& aArg
) {
141 return mozilla::webgl::QueueParamTraits
<
142 typename RemoveCVR
<Arg
>::Type
>::Write(*this, aArg
);
145 bool Ok() const { return mOk
; }
148 Producer
* const mProducer
;
153 * Used to give QueueParamTraits a way to read from the Consumer without
154 * actually altering it, in case the transaction fails.
156 template <typename _Consumer
>
159 using Consumer
= _Consumer
;
161 explicit ConsumerView(Consumer
* aConsumer
) : mConsumer(aConsumer
) {}
164 * Read bytes from the consumer if there is enough data. aBuffer may
165 * be null (in which case the data is skipped)
167 template <typename T
>
168 inline bool Read(T
* const destBegin
, T
* const destEnd
) {
169 MOZ_ASSERT(destBegin
);
170 MOZ_RELEASE_ASSERT(destBegin
<= destEnd
);
172 const auto dest
= AsRange(destBegin
, destEnd
);
173 const auto view
= ReadRange
<T
>(dest
.length());
174 if (MOZ_LIKELY(view
)) {
175 const auto byteSize
= ByteSize(dest
);
176 if (MOZ_LIKELY(byteSize
)) {
177 memcpy(dest
.begin().get(), view
->begin().get(), byteSize
);
183 /// Return a view wrapping the shmem.
184 template <typename T
>
185 inline Maybe
<Range
<const T
>> ReadRange(const size_t elemCount
) {
186 static_assert(BytesAlwaysValidT
<T
>::value
);
187 if (MOZ_UNLIKELY(!mOk
)) return {};
188 const auto view
= mConsumer
->template ReadRange
<T
>(elemCount
);
194 * Deserialize aArg using Arg's QueueParamTraits.
195 * If the return value is not Success then aArg is not changed.
197 template <typename Arg
>
198 bool ReadParam(Arg
* aArg
) {
200 return mozilla::webgl::QueueParamTraits
<std::remove_cv_t
<Arg
>>::Read(*this,
204 bool Ok() const { return mOk
; }
207 Consumer
* const mConsumer
;
213 template <typename Arg
>
214 struct QueueParamTraits
{
215 template <typename ProducerView
>
216 static bool Write(ProducerView
& aProducerView
, const Arg
& aArg
) {
217 static_assert(BytesAlwaysValidT
<Arg
>::value
,
218 "No QueueParamTraits specialization was found for this type "
219 "and it does not satisfy BytesAlwaysValid.");
220 // Write self as binary
221 const auto pArg
= &aArg
;
222 return aProducerView
.Write(pArg
, pArg
+ 1);
225 template <typename ConsumerView
>
226 static bool Read(ConsumerView
& aConsumerView
, Arg
* aArg
) {
227 static_assert(BytesAlwaysValidT
<Arg
>::value
,
228 "No QueueParamTraits specialization was found for this type "
229 "and it does not satisfy BytesAlwaysValid.");
230 // Read self as binary
231 return aConsumerView
.Read(aArg
, aArg
+ 1);
235 // ---------------------------------------------------------------
238 struct QueueParamTraits
<bool> {
239 using ParamType
= bool;
241 template <typename U
>
242 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
243 uint8_t temp
= aArg
? 1 : 0;
244 return aProducerView
.WriteParam(temp
);
247 template <typename U
>
248 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
250 if (aConsumerView
.ReadParam(&temp
)) {
251 MOZ_ASSERT(temp
== 1 || temp
== 0);
252 *aArg
= temp
? true : false;
254 return aConsumerView
.Ok();
258 // ---------------------------------------------------------------
261 Maybe
<T
> AsValidEnum(const std::underlying_type_t
<T
> raw_val
) {
262 const auto raw_enum
= T
{raw_val
}; // This is the risk we prevent!
263 if (!IsEnumCase(raw_enum
)) return {};
264 return Some(raw_enum
);
270 struct QueueParamTraits_IsEnumCase
{
271 template <typename ProducerView
>
272 static bool Write(ProducerView
& aProducerView
, const T
& aArg
) {
273 MOZ_ASSERT(IsEnumCase(aArg
));
274 const auto shadow
= static_cast<std::underlying_type_t
<T
>>(aArg
);
275 aProducerView
.WriteParam(shadow
);
279 template <typename ConsumerView
>
280 static bool Read(ConsumerView
& aConsumerView
, T
* aArg
) {
281 auto shadow
= std::underlying_type_t
<T
>{};
282 aConsumerView
.ReadParam(&shadow
);
283 const auto e
= AsValidEnum
<T
>(shadow
);
284 if (!e
) return false;
290 // ---------------------------------------------------------------
292 // We guarantee our robustness via these requirements:
293 // * Object.MutTiedFields() gives us a tuple,
294 // * where the combined sizeofs all field types sums to sizeof(Object),
295 // * (thus we know we are exhaustively listing all fields)
296 // * where feeding each field back into ParamTraits succeeds,
297 // * and ParamTraits is only automated for BytesAlwaysValidT<T> types.
298 // (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float
299 // types, or array or std::arrays of such types)
300 // (Yes, bit-field fields are rejected by MutTiedFields too)
303 struct QueueParamTraits_TiedFields
{
304 template <typename ProducerView
>
305 static bool Write(ProducerView
& aProducerView
, const T
& aArg
) {
306 const auto fields
= TiedFields(aArg
);
307 static_assert(AreAllBytesTiedFields
<T
>(),
308 "Are there missing fields or padding between fields?");
311 MapTuple(fields
, [&](const auto& field
) {
312 ok
&= aProducerView
.WriteParam(field
);
318 template <typename ConsumerView
>
319 static bool Read(ConsumerView
& aConsumerView
, T
* aArg
) {
320 const auto fields
= TiedFields(*aArg
);
321 static_assert(AreAllBytesTiedFields
<T
>());
324 MapTuple(fields
, [&](auto& field
) {
325 ok
&= aConsumerView
.ReadParam(&field
);
332 // ---------------------------------------------------------------
334 // Adapted from IPC::EnumSerializer, this class safely handles enum values,
335 // validating that they are in range using the same EnumValidators as IPDL
336 // (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive).
337 template <typename E
, typename EnumValidator
>
338 struct EnumSerializer
{
340 using DataType
= typename
std::underlying_type
<E
>::type
;
342 template <typename U
>
343 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aValue
) {
345 EnumValidator::IsLegalValue(static_cast<DataType
>(aValue
)));
346 return aProducerView
.WriteParam(DataType(aValue
));
349 template <typename U
>
350 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aResult
) {
352 if (!aConsumerView
.ReadParam(&value
)) {
353 CrashReporter::AnnotateCrashReport(
354 CrashReporter::Annotation::IPCReadErrorReason
, "Bad iter"_ns
);
357 if (!EnumValidator::IsLegalValue(static_cast<DataType
>(value
))) {
358 CrashReporter::AnnotateCrashReport(
359 CrashReporter::Annotation::IPCReadErrorReason
, "Illegal value"_ns
);
363 *aResult
= ParamType(value
);
368 using IPC::ContiguousEnumValidator
;
369 using IPC::ContiguousEnumValidatorInclusive
;
371 template <typename E
, E MinLegal
, E HighBound
>
372 struct ContiguousEnumSerializer
373 : EnumSerializer
<E
, ContiguousEnumValidator
<E
, MinLegal
, HighBound
>> {};
375 template <typename E
, E MinLegal
, E MaxLegal
>
376 struct ContiguousEnumSerializerInclusive
378 ContiguousEnumValidatorInclusive
<E
, MinLegal
, MaxLegal
>> {
381 // ---------------------------------------------------------------
384 struct QueueParamTraits
<webgl::TexUnpackBlobDesc
> {
385 using ParamType
= webgl::TexUnpackBlobDesc
;
387 template <typename U
>
388 static bool Write(ProducerView
<U
>& view
, const ParamType
& in
) {
389 MOZ_RELEASE_ASSERT(!in
.image
);
390 MOZ_RELEASE_ASSERT(!in
.sd
);
391 const bool isDataSurf
= bool(in
.dataSurf
);
392 if (!view
.WriteParam(in
.imageTarget
) || !view
.WriteParam(in
.size
) ||
393 !view
.WriteParam(in
.srcAlphaType
) || !view
.WriteParam(in
.unpacking
) ||
394 !view
.WriteParam(in
.cpuData
) || !view
.WriteParam(in
.pboOffset
) ||
395 !view
.WriteParam(in
.structuredSrcSize
) ||
396 !view
.WriteParam(in
.applyUnpackTransforms
) ||
397 !view
.WriteParam(isDataSurf
)) {
401 const auto& surf
= in
.dataSurf
;
402 gfx::DataSourceSurface::ScopedMap
map(surf
, gfx::DataSourceSurface::READ
);
403 if (!map
.IsMapped()) {
406 const auto& surfSize
= surf
->GetSize();
407 const auto stride
= *MaybeAs
<size_t>(map
.GetStride());
408 if (!view
.WriteParam(surfSize
) || !view
.WriteParam(surf
->GetFormat()) ||
409 !view
.WriteParam(stride
)) {
413 const size_t dataSize
= stride
* surfSize
.height
;
414 const auto& begin
= map
.GetData();
415 const auto range
= Range
<const uint8_t>{begin
, dataSize
};
416 if (!view
.WriteFromRange(range
)) {
423 template <typename U
>
424 static bool Read(ConsumerView
<U
>& view
, ParamType
* const out
) {
426 if (!view
.ReadParam(&out
->imageTarget
) || !view
.ReadParam(&out
->size
) ||
427 !view
.ReadParam(&out
->srcAlphaType
) ||
428 !view
.ReadParam(&out
->unpacking
) || !view
.ReadParam(&out
->cpuData
) ||
429 !view
.ReadParam(&out
->pboOffset
) ||
430 !view
.ReadParam(&out
->structuredSrcSize
) ||
431 !view
.ReadParam(&out
->applyUnpackTransforms
) ||
432 !view
.ReadParam(&isDataSurf
)) {
436 gfx::IntSize surfSize
;
437 gfx::SurfaceFormat format
;
439 if (!view
.ReadParam(&surfSize
) || !view
.ReadParam(&format
) ||
440 !view
.ReadParam(&stride
)) {
443 const size_t dataSize
= stride
* surfSize
.height
;
444 const auto range
= view
.template ReadRange
<uint8_t>(dataSize
);
445 if (!range
) return false;
447 // DataSourceSurface demands pointer-to-mutable.
448 const auto bytes
= const_cast<uint8_t*>(range
->begin().get());
449 out
->dataSurf
= gfx::Factory::CreateWrappingDataSourceSurface(
450 bytes
, stride
, surfSize
, format
);
451 MOZ_ASSERT(out
->dataSurf
);
457 // ---------------------------------------------------------------
460 struct QueueParamTraits
<nsACString
> {
461 using ParamType
= nsACString
;
463 template <typename U
>
464 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
465 if ((!aProducerView
.WriteParam(aArg
.IsVoid())) || aArg
.IsVoid()) {
469 uint32_t len
= aArg
.Length();
470 if ((!aProducerView
.WriteParam(len
)) || (len
== 0)) {
474 return aProducerView
.Write(aArg
.BeginReading(), len
);
477 template <typename U
>
478 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
480 if (!aConsumerView
.ReadParam(&isVoid
)) {
483 aArg
->SetIsVoid(isVoid
);
489 if (!aConsumerView
.ReadParam(&len
)) {
498 char* buf
= new char[len
+ 1];
502 if (!aConsumerView
.Read(buf
, len
)) {
506 aArg
->Adopt(buf
, len
);
512 struct QueueParamTraits
<nsAString
> {
513 using ParamType
= nsAString
;
515 template <typename U
>
516 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
517 if ((!aProducerView
.WriteParam(aArg
.IsVoid())) || (aArg
.IsVoid())) {
520 // DLP: No idea if this includes null terminator
521 uint32_t len
= aArg
.Length();
522 if ((!aProducerView
.WriteParam(len
)) || (len
== 0)) {
525 constexpr const uint32_t sizeofchar
= sizeof(typename
ParamType::char_type
);
526 return aProducerView
.Write(aArg
.BeginReading(), len
* sizeofchar
);
529 template <typename U
>
530 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
532 if (!aConsumerView
.ReadParam(&isVoid
)) {
535 aArg
->SetIsVoid(isVoid
);
540 // DLP: No idea if this includes null terminator
542 if (!aConsumerView
.ReadParam(&len
)) {
551 uint32_t sizeofchar
= sizeof(typename
ParamType::char_type
);
552 typename
ParamType::char_type
* buf
= nullptr;
553 buf
= static_cast<typename
ParamType::char_type
*>(
554 malloc((len
+ 1) * sizeofchar
));
559 if (!aConsumerView
.Read(buf
, len
* sizeofchar
)) {
564 aArg
->Adopt(buf
, len
);
570 struct QueueParamTraits
<nsCString
> : public QueueParamTraits
<nsACString
> {
571 using ParamType
= nsCString
;
575 struct QueueParamTraits
<nsString
> : public QueueParamTraits
<nsAString
> {
576 using ParamType
= nsString
;
579 // ---------------------------------------------------------------
581 template <typename NSTArrayType
,
582 bool = BytesAlwaysValidT
<typename
NSTArrayType::value_type
>::value
>
583 struct NSArrayQueueParamTraits
;
585 // For ElementTypes that are !BytesAlwaysValidT
586 template <typename _ElementType
>
587 struct NSArrayQueueParamTraits
<nsTArray
<_ElementType
>, false> {
588 using ElementType
= _ElementType
;
589 using ParamType
= nsTArray
<ElementType
>;
591 template <typename U
>
592 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
593 aProducerView
.WriteParam(aArg
.Length());
594 for (auto& elt
: aArg
) {
595 aProducerView
.WriteParam(elt
);
600 template <typename U
>
601 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
603 if (!aConsumerView
.ReadParam(&arrayLen
)) {
607 if (!aArg
->AppendElements(arrayLen
, fallible
)) {
611 for (auto i
: IntegerRange(arrayLen
)) {
612 ElementType
& elt
= aArg
->ElementAt(i
);
613 aConsumerView
.ReadParam(elt
);
615 return aConsumerView
.Ok();
619 // For ElementTypes that are BytesAlwaysValidT
620 template <typename _ElementType
>
621 struct NSArrayQueueParamTraits
<nsTArray
<_ElementType
>, true> {
622 using ElementType
= _ElementType
;
623 using ParamType
= nsTArray
<ElementType
>;
625 // TODO: Are there alignment issues?
626 template <typename U
>
627 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
628 size_t arrayLen
= aArg
.Length();
629 aProducerView
.WriteParam(arrayLen
);
630 return aProducerView
.Write(&aArg
[0], aArg
.Length() * sizeof(ElementType
));
633 template <typename U
>
634 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
636 if (!aConsumerView
.ReadParam(&arrayLen
)) {
640 if (!aArg
->AppendElements(arrayLen
, fallible
)) {
644 return aConsumerView
.Read(aArg
->Elements(), arrayLen
* sizeof(ElementType
));
648 template <typename ElementType
>
649 struct QueueParamTraits
<nsTArray
<ElementType
>>
650 : public NSArrayQueueParamTraits
<nsTArray
<ElementType
>> {
651 using ParamType
= nsTArray
<ElementType
>;
654 // ---------------------------------------------------------------
656 template <typename ArrayType
,
657 bool = BytesAlwaysValidT
<typename
ArrayType::ElementType
>::value
>
658 struct ArrayQueueParamTraits
;
660 // For ElementTypes that are !BytesAlwaysValidT
661 template <typename _ElementType
, size_t Length
>
662 struct ArrayQueueParamTraits
<Array
<_ElementType
, Length
>, false> {
663 using ElementType
= _ElementType
;
664 using ParamType
= Array
<ElementType
, Length
>;
666 template <typename U
>
667 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
668 for (const auto& elt
: aArg
) {
669 aProducerView
.WriteParam(elt
);
671 return aProducerView
.Ok();
674 template <typename U
>
675 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
676 for (auto& elt
: *aArg
) {
677 aConsumerView
.ReadParam(elt
);
679 return aConsumerView
.Ok();
683 // For ElementTypes that are BytesAlwaysValidT
684 template <typename _ElementType
, size_t Length
>
685 struct ArrayQueueParamTraits
<Array
<_ElementType
, Length
>, true> {
686 using ElementType
= _ElementType
;
687 using ParamType
= Array
<ElementType
, Length
>;
689 template <typename U
>
690 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
691 return aProducerView
.Write(aArg
.begin(), sizeof(ElementType
[Length
]));
694 template <typename U
>
695 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
696 return aConsumerView
.Read(aArg
->begin(), sizeof(ElementType
[Length
]));
700 template <typename ElementType
, size_t Length
>
701 struct QueueParamTraits
<Array
<ElementType
, Length
>>
702 : public ArrayQueueParamTraits
<Array
<ElementType
, Length
>> {
703 using ParamType
= Array
<ElementType
, Length
>;
706 // ---------------------------------------------------------------
708 template <typename ElementType
>
709 struct QueueParamTraits
<Maybe
<ElementType
>> {
710 using ParamType
= Maybe
<ElementType
>;
712 template <typename U
>
713 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
714 aProducerView
.WriteParam(static_cast<bool>(aArg
));
716 aProducerView
.WriteParam(aArg
.ref());
718 return aProducerView
.Ok();
721 template <typename U
>
722 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
724 if (!aConsumerView
.ReadParam(&isSome
)) {
734 return aConsumerView
.ReadParam(aArg
->ptr());
738 // ---------------------------------------------------------------
740 template <typename TypeA
, typename TypeB
>
741 struct QueueParamTraits
<std::pair
<TypeA
, TypeB
>> {
742 using ParamType
= std::pair
<TypeA
, TypeB
>;
744 template <typename U
>
745 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
746 aProducerView
.WriteParam(aArg
.first());
747 return aProducerView
.WriteParam(aArg
.second());
750 template <typename U
>
751 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
752 aConsumerView
.ReadParam(aArg
->first());
753 return aConsumerView
.ReadParam(aArg
->second());
757 } // namespace mozilla::webgl
759 #endif // _QUEUEPARAMTRAITS_H_