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/SharedMemory.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;
102 struct BytesAlwaysValidT
<const webgl::UniformDataVal
> {
103 static constexpr bool value
= true;
109 * Used to give QueueParamTraits a way to write to the Producer without
110 * actually altering it, in case the transaction fails.
111 * THis object maintains the error state of the transaction and
112 * discards commands issued after an error is encountered.
114 template <typename _Producer
>
117 using Producer
= _Producer
;
119 explicit ProducerView(Producer
* aProducer
) : mProducer(aProducer
) {}
121 template <typename T
>
122 bool WriteFromRange(const Range
<const T
>& src
) {
123 static_assert(BytesAlwaysValidT
<T
>::value
);
124 if (MOZ_LIKELY(mOk
)) {
125 mOk
&= mProducer
->WriteFromRange(src
);
131 * Copy bytes from aBuffer to the producer if there is enough room.
132 * aBufferSize must not be 0.
134 template <typename T
>
135 inline bool Write(const T
* begin
, const T
* end
) {
136 MOZ_RELEASE_ASSERT(begin
<= end
);
137 return WriteFromRange(AsRange(begin
, end
));
141 * Serialize aArg using Arg's QueueParamTraits.
143 template <typename Arg
>
144 bool WriteParam(const Arg
& aArg
) {
145 return mozilla::webgl::QueueParamTraits
<
146 typename RemoveCVR
<Arg
>::Type
>::Write(*this, aArg
);
149 bool Ok() const { return mOk
; }
152 Producer
* const mProducer
;
157 * Used to give QueueParamTraits a way to read from the Consumer without
158 * actually altering it, in case the transaction fails.
160 template <typename _Consumer
>
163 using Consumer
= _Consumer
;
165 explicit ConsumerView(Consumer
* aConsumer
) : mConsumer(aConsumer
) {}
168 * Read bytes from the consumer if there is enough data. aBuffer may
169 * be null (in which case the data is skipped)
171 template <typename T
>
172 inline bool Read(T
* const destBegin
, T
* const destEnd
) {
173 MOZ_ASSERT(destBegin
);
174 MOZ_RELEASE_ASSERT(destBegin
<= destEnd
);
176 const auto dest
= AsRange(destBegin
, destEnd
);
177 const auto view
= ReadRange
<T
>(dest
.length());
178 if (MOZ_LIKELY(view
)) {
179 const auto byteSize
= ByteSize(dest
);
180 if (MOZ_LIKELY(byteSize
)) {
181 memcpy(dest
.begin().get(), view
->begin().get(), byteSize
);
187 /// Return a view wrapping the shmem.
188 template <typename T
>
189 inline Maybe
<Range
<const T
>> ReadRange(const size_t elemCount
) {
190 static_assert(BytesAlwaysValidT
<T
>::value
);
191 if (MOZ_UNLIKELY(!mOk
)) return {};
192 const auto view
= mConsumer
->template ReadRange
<T
>(elemCount
);
198 * Deserialize aArg using Arg's QueueParamTraits.
199 * If the return value is not Success then aArg is not changed.
201 template <typename Arg
>
202 bool ReadParam(Arg
* aArg
) {
204 return mozilla::webgl::QueueParamTraits
<std::remove_cv_t
<Arg
>>::Read(*this,
208 bool Ok() const { return mOk
; }
211 Consumer
* const mConsumer
;
217 template <typename Arg
>
218 struct QueueParamTraits
{
219 template <typename ProducerView
>
220 static bool Write(ProducerView
& aProducerView
, const Arg
& aArg
) {
221 static_assert(BytesAlwaysValidT
<Arg
>::value
,
222 "No QueueParamTraits specialization was found for this type "
223 "and it does not satisfy BytesAlwaysValid.");
224 // Write self as binary
225 const auto pArg
= &aArg
;
226 return aProducerView
.Write(pArg
, pArg
+ 1);
229 template <typename ConsumerView
>
230 static bool Read(ConsumerView
& aConsumerView
, Arg
* aArg
) {
231 static_assert(BytesAlwaysValidT
<Arg
>::value
,
232 "No QueueParamTraits specialization was found for this type "
233 "and it does not satisfy BytesAlwaysValid.");
234 // Read self as binary
235 return aConsumerView
.Read(aArg
, aArg
+ 1);
239 // ---------------------------------------------------------------
242 struct QueueParamTraits
<bool> {
243 using ParamType
= bool;
245 template <typename U
>
246 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
247 uint8_t temp
= aArg
? 1 : 0;
248 return aProducerView
.WriteParam(temp
);
251 template <typename U
>
252 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
254 if (aConsumerView
.ReadParam(&temp
)) {
255 MOZ_ASSERT(temp
== 1 || temp
== 0);
256 *aArg
= temp
? true : false;
258 return aConsumerView
.Ok();
262 // ---------------------------------------------------------------
265 struct QueueParamTraits_IsEnumCase
{
266 template <typename ProducerView
>
267 static bool Write(ProducerView
& aProducerView
, const T
& aArg
) {
268 MOZ_ASSERT(IsEnumCase(aArg
));
269 const auto shadow
= static_cast<std::underlying_type_t
<T
>>(aArg
);
270 aProducerView
.WriteParam(shadow
);
274 template <typename ConsumerView
>
275 static bool Read(ConsumerView
& aConsumerView
, T
* aArg
) {
276 auto shadow
= std::underlying_type_t
<T
>{};
277 aConsumerView
.ReadParam(&shadow
);
278 const auto e
= AsEnumCase
<T
>(shadow
);
279 if (!e
) return false;
285 // ---------------------------------------------------------------
287 // We guarantee our robustness via these requirements:
288 // * Object.MutTiedFields() gives us a tuple,
289 // * where the combined sizeofs all field types sums to sizeof(Object),
290 // * (thus we know we are exhaustively listing all fields)
291 // * where feeding each field back into ParamTraits succeeds,
292 // * and ParamTraits is only automated for BytesAlwaysValidT<T> types.
293 // (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float
294 // types, or array or std::arrays of such types)
295 // (Yes, bit-field fields are rejected by MutTiedFields too)
298 struct QueueParamTraits_TiedFields
{
299 template <typename ProducerView
>
300 static bool Write(ProducerView
& aProducerView
, const T
& aArg
) {
301 const auto fields
= TiedFields(aArg
);
302 static_assert(AreAllBytesTiedFields
<T
>(),
303 "Are there missing fields or padding between fields?");
306 MapTuple(fields
, [&](const auto& field
) {
307 ok
&= aProducerView
.WriteParam(field
);
313 template <typename ConsumerView
>
314 static bool Read(ConsumerView
& aConsumerView
, T
* aArg
) {
315 const auto fields
= TiedFields(*aArg
);
316 static_assert(AreAllBytesTiedFields
<T
>());
319 MapTuple(fields
, [&](auto& field
) {
320 ok
&= aConsumerView
.ReadParam(&field
);
327 // ---------------------------------------------------------------
329 // Adapted from IPC::EnumSerializer, this class safely handles enum values,
330 // validating that they are in range using the same EnumValidators as IPDL
331 // (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive).
332 template <typename E
, typename EnumValidator
>
333 struct EnumSerializer
{
335 using DataType
= typename
std::underlying_type
<E
>::type
;
337 template <typename U
>
338 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aValue
) {
340 EnumValidator::IsLegalValue(static_cast<DataType
>(aValue
)));
341 return aProducerView
.WriteParam(DataType(aValue
));
344 template <typename U
>
345 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aResult
) {
347 if (!aConsumerView
.ReadParam(&value
)) {
348 CrashReporter::RecordAnnotationCString(
349 CrashReporter::Annotation::IPCReadErrorReason
, "Bad iter");
352 if (!EnumValidator::IsLegalValue(static_cast<DataType
>(value
))) {
353 CrashReporter::RecordAnnotationCString(
354 CrashReporter::Annotation::IPCReadErrorReason
, "Illegal value");
358 *aResult
= ParamType(value
);
363 using IPC::ContiguousEnumValidator
;
364 using IPC::ContiguousEnumValidatorInclusive
;
366 template <typename E
, E MinLegal
, E HighBound
>
367 struct ContiguousEnumSerializer
368 : EnumSerializer
<E
, ContiguousEnumValidator
<E
, MinLegal
, HighBound
>> {};
370 template <typename E
, E MinLegal
, E MaxLegal
>
371 struct ContiguousEnumSerializerInclusive
373 ContiguousEnumValidatorInclusive
<E
, MinLegal
, MaxLegal
>> {
376 // ---------------------------------------------------------------
379 struct QueueParamTraits
<webgl::TexUnpackBlobDesc
> {
380 using ParamType
= webgl::TexUnpackBlobDesc
;
382 template <typename U
>
383 static bool Write(ProducerView
<U
>& view
, const ParamType
& in
) {
384 MOZ_RELEASE_ASSERT(!in
.image
);
385 MOZ_RELEASE_ASSERT(!in
.sd
);
386 const bool isDataSurf
= bool(in
.dataSurf
);
387 if (!view
.WriteParam(in
.imageTarget
) || !view
.WriteParam(in
.size
) ||
388 !view
.WriteParam(in
.srcAlphaType
) || !view
.WriteParam(in
.unpacking
) ||
389 !view
.WriteParam(in
.cpuData
) || !view
.WriteParam(in
.pboOffset
) ||
390 !view
.WriteParam(in
.structuredSrcSize
) ||
391 !view
.WriteParam(in
.applyUnpackTransforms
) ||
392 !view
.WriteParam(isDataSurf
)) {
396 const auto& surf
= in
.dataSurf
;
397 gfx::DataSourceSurface::ScopedMap
map(surf
, gfx::DataSourceSurface::READ
);
398 if (!map
.IsMapped()) {
401 const auto& surfSize
= surf
->GetSize();
402 const auto stride
= *MaybeAs
<size_t>(map
.GetStride());
403 if (!view
.WriteParam(surfSize
) || !view
.WriteParam(surf
->GetFormat()) ||
404 !view
.WriteParam(stride
)) {
408 const size_t dataSize
= stride
* surfSize
.height
;
409 const auto& begin
= map
.GetData();
410 const auto range
= Range
<const uint8_t>{begin
, dataSize
};
411 if (!view
.WriteFromRange(range
)) {
418 template <typename U
>
419 static bool Read(ConsumerView
<U
>& view
, ParamType
* const out
) {
421 if (!view
.ReadParam(&out
->imageTarget
) || !view
.ReadParam(&out
->size
) ||
422 !view
.ReadParam(&out
->srcAlphaType
) ||
423 !view
.ReadParam(&out
->unpacking
) || !view
.ReadParam(&out
->cpuData
) ||
424 !view
.ReadParam(&out
->pboOffset
) ||
425 !view
.ReadParam(&out
->structuredSrcSize
) ||
426 !view
.ReadParam(&out
->applyUnpackTransforms
) ||
427 !view
.ReadParam(&isDataSurf
)) {
431 gfx::IntSize surfSize
;
432 gfx::SurfaceFormat format
;
434 if (!view
.ReadParam(&surfSize
) || !view
.ReadParam(&format
) ||
435 !view
.ReadParam(&stride
)) {
438 const size_t dataSize
= stride
* surfSize
.height
;
439 const auto range
= view
.template ReadRange
<uint8_t>(dataSize
);
440 if (!range
) return false;
442 // DataSourceSurface demands pointer-to-mutable.
443 const auto bytes
= const_cast<uint8_t*>(range
->begin().get());
444 out
->dataSurf
= gfx::Factory::CreateWrappingDataSourceSurface(
445 bytes
, stride
, surfSize
, format
);
446 MOZ_ASSERT(out
->dataSurf
);
452 // ---------------------------------------------------------------
455 struct QueueParamTraits
<nsACString
> {
456 using ParamType
= nsACString
;
458 template <typename U
>
459 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
460 if ((!aProducerView
.WriteParam(aArg
.IsVoid())) || aArg
.IsVoid()) {
464 uint32_t len
= aArg
.Length();
465 if ((!aProducerView
.WriteParam(len
)) || (len
== 0)) {
469 return aProducerView
.Write(aArg
.BeginReading(), len
);
472 template <typename U
>
473 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
475 if (!aConsumerView
.ReadParam(&isVoid
)) {
478 aArg
->SetIsVoid(isVoid
);
484 if (!aConsumerView
.ReadParam(&len
)) {
493 char* buf
= new char[len
+ 1];
497 if (!aConsumerView
.Read(buf
, len
)) {
501 aArg
->Adopt(buf
, len
);
507 struct QueueParamTraits
<nsAString
> {
508 using ParamType
= nsAString
;
510 template <typename U
>
511 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
512 if ((!aProducerView
.WriteParam(aArg
.IsVoid())) || (aArg
.IsVoid())) {
515 // DLP: No idea if this includes null terminator
516 uint32_t len
= aArg
.Length();
517 if ((!aProducerView
.WriteParam(len
)) || (len
== 0)) {
520 constexpr const uint32_t sizeofchar
= sizeof(typename
ParamType::char_type
);
521 return aProducerView
.Write(aArg
.BeginReading(), len
* sizeofchar
);
524 template <typename U
>
525 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
527 if (!aConsumerView
.ReadParam(&isVoid
)) {
530 aArg
->SetIsVoid(isVoid
);
535 // DLP: No idea if this includes null terminator
537 if (!aConsumerView
.ReadParam(&len
)) {
546 uint32_t sizeofchar
= sizeof(typename
ParamType::char_type
);
547 typename
ParamType::char_type
* buf
= nullptr;
548 buf
= static_cast<typename
ParamType::char_type
*>(
549 malloc((len
+ 1) * sizeofchar
));
554 if (!aConsumerView
.Read(buf
, len
* sizeofchar
)) {
559 aArg
->Adopt(buf
, len
);
565 struct QueueParamTraits
<nsCString
> : public QueueParamTraits
<nsACString
> {
566 using ParamType
= nsCString
;
570 struct QueueParamTraits
<nsString
> : public QueueParamTraits
<nsAString
> {
571 using ParamType
= nsString
;
574 // ---------------------------------------------------------------
576 template <typename NSTArrayType
,
577 bool = BytesAlwaysValidT
<typename
NSTArrayType::value_type
>::value
>
578 struct NSArrayQueueParamTraits
;
580 // For ElementTypes that are !BytesAlwaysValidT
581 template <typename _ElementType
>
582 struct NSArrayQueueParamTraits
<nsTArray
<_ElementType
>, false> {
583 using ElementType
= _ElementType
;
584 using ParamType
= nsTArray
<ElementType
>;
586 template <typename U
>
587 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
588 aProducerView
.WriteParam(aArg
.Length());
589 for (auto& elt
: aArg
) {
590 aProducerView
.WriteParam(elt
);
595 template <typename U
>
596 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
598 if (!aConsumerView
.ReadParam(&arrayLen
)) {
602 if (!aArg
->AppendElements(arrayLen
, fallible
)) {
606 for (auto i
: IntegerRange(arrayLen
)) {
607 ElementType
& elt
= aArg
->ElementAt(i
);
608 aConsumerView
.ReadParam(elt
);
610 return aConsumerView
.Ok();
614 // For ElementTypes that are BytesAlwaysValidT
615 template <typename _ElementType
>
616 struct NSArrayQueueParamTraits
<nsTArray
<_ElementType
>, true> {
617 using ElementType
= _ElementType
;
618 using ParamType
= nsTArray
<ElementType
>;
620 // TODO: Are there alignment issues?
621 template <typename U
>
622 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
623 size_t arrayLen
= aArg
.Length();
624 aProducerView
.WriteParam(arrayLen
);
625 return aProducerView
.Write(&aArg
[0], aArg
.Length() * sizeof(ElementType
));
628 template <typename U
>
629 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
631 if (!aConsumerView
.ReadParam(&arrayLen
)) {
635 if (!aArg
->AppendElements(arrayLen
, fallible
)) {
639 return aConsumerView
.Read(aArg
->Elements(), arrayLen
* sizeof(ElementType
));
643 template <typename ElementType
>
644 struct QueueParamTraits
<nsTArray
<ElementType
>>
645 : public NSArrayQueueParamTraits
<nsTArray
<ElementType
>> {
646 using ParamType
= nsTArray
<ElementType
>;
649 // ---------------------------------------------------------------
651 template <typename ArrayType
,
652 bool = BytesAlwaysValidT
<typename
ArrayType::ElementType
>::value
>
653 struct ArrayQueueParamTraits
;
655 // For ElementTypes that are !BytesAlwaysValidT
656 template <typename _ElementType
, size_t Length
>
657 struct ArrayQueueParamTraits
<Array
<_ElementType
, Length
>, false> {
658 using ElementType
= _ElementType
;
659 using ParamType
= Array
<ElementType
, Length
>;
661 template <typename U
>
662 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
663 for (const auto& elt
: aArg
) {
664 aProducerView
.WriteParam(elt
);
666 return aProducerView
.Ok();
669 template <typename U
>
670 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
671 for (auto& elt
: *aArg
) {
672 aConsumerView
.ReadParam(elt
);
674 return aConsumerView
.Ok();
678 // For ElementTypes that are BytesAlwaysValidT
679 template <typename _ElementType
, size_t Length
>
680 struct ArrayQueueParamTraits
<Array
<_ElementType
, Length
>, true> {
681 using ElementType
= _ElementType
;
682 using ParamType
= Array
<ElementType
, Length
>;
684 template <typename U
>
685 static auto Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
686 return aProducerView
.Write(aArg
.begin(), sizeof(ElementType
[Length
]));
689 template <typename U
>
690 static auto Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
691 return aConsumerView
.Read(aArg
->begin(), sizeof(ElementType
[Length
]));
695 template <typename ElementType
, size_t Length
>
696 struct QueueParamTraits
<Array
<ElementType
, Length
>>
697 : public ArrayQueueParamTraits
<Array
<ElementType
, Length
>> {
698 using ParamType
= Array
<ElementType
, Length
>;
701 // ---------------------------------------------------------------
703 template <typename ElementType
>
704 struct QueueParamTraits
<Maybe
<ElementType
>> {
705 using ParamType
= Maybe
<ElementType
>;
707 template <typename U
>
708 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
709 aProducerView
.WriteParam(static_cast<bool>(aArg
));
711 aProducerView
.WriteParam(aArg
.ref());
713 return aProducerView
.Ok();
716 template <typename U
>
717 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
719 if (!aConsumerView
.ReadParam(&isSome
)) {
729 return aConsumerView
.ReadParam(aArg
->ptr());
733 // ---------------------------------------------------------------
735 template <typename TypeA
, typename TypeB
>
736 struct QueueParamTraits
<std::pair
<TypeA
, TypeB
>> {
737 using ParamType
= std::pair
<TypeA
, TypeB
>;
739 template <typename U
>
740 static bool Write(ProducerView
<U
>& aProducerView
, const ParamType
& aArg
) {
741 aProducerView
.WriteParam(aArg
.first());
742 return aProducerView
.WriteParam(aArg
.second());
745 template <typename U
>
746 static bool Read(ConsumerView
<U
>& aConsumerView
, ParamType
* aArg
) {
747 aConsumerView
.ReadParam(aArg
->first());
748 return aConsumerView
.ReadParam(aArg
->second());
752 } // namespace mozilla::webgl
754 #endif // _QUEUEPARAMTRAITS_H_