1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #if !defined(MediaSpan_h)
7 #  define MediaSpan_h
8 
9 #  include "MediaData.h"
10 #  include "mozilla/RefPtr.h"
11 #  include "mozilla/Span.h"
12 
13 namespace mozilla {
14 
15 // A MediaSpan wraps a MediaByteBuffer and exposes a slice/span, or subregion,
16 // of the buffer. This allows you to slice off a logical subregion without
17 // needing to reallocate a new buffer to hold it. You can also append to a
18 // MediaSpan without affecting other MediaSpans referencing the same buffer
19 // (the MediaSpan receiving the append will allocate a new buffer to store its
20 // result if necessary, to ensure other MediaSpans referencing its original
21 // buffer are unaffected). Note there are no protections here that something
22 // other than MediaSpans doesn't modify the underlying MediaByteBuffer while
23 // a MediaSpan is alive.
24 class MediaSpan {
25  public:
26   ~MediaSpan() = default;
27 
28   explicit MediaSpan(const MediaSpan& aOther) = default;
29 
30   MediaSpan(MediaSpan&& aOther) = default;
31 
MediaSpan(const RefPtr<MediaByteBuffer> & aBuffer)32   explicit MediaSpan(const RefPtr<MediaByteBuffer>& aBuffer)
33       : mBuffer(aBuffer), mStart(0), mLength(aBuffer ? aBuffer->Length() : 0) {
34     MOZ_DIAGNOSTIC_ASSERT(mBuffer);
35   }
36 
MediaSpan(MediaByteBuffer * aBuffer)37   explicit MediaSpan(MediaByteBuffer* aBuffer)
38       : mBuffer(aBuffer), mStart(0), mLength(aBuffer ? aBuffer->Length() : 0) {
39     MOZ_DIAGNOSTIC_ASSERT(mBuffer);
40   }
41 
42   MediaSpan& operator=(const MediaSpan& aOther) = default;
43 
WithCopyOf(const RefPtr<MediaByteBuffer> & aBuffer)44   static MediaSpan WithCopyOf(const RefPtr<MediaByteBuffer>& aBuffer) {
45     RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(aBuffer->Length());
46     buffer->AppendElements(*aBuffer);
47     return MediaSpan(buffer);
48   }
49 
IsEmpty()50   bool IsEmpty() const { return Length() == 0; }
51 
52   // Note: It's unsafe to store the pointer returned by this function, as an
53   // append operation could cause the wrapped MediaByteBuffer to be
54   // reallocated, invalidating pointers previously returned by this function.
Elements()55   const uint8_t* Elements() const {
56     MOZ_DIAGNOSTIC_ASSERT(mStart < mBuffer->Length());
57     return mBuffer->Elements() + mStart;
58   }
59 
Length()60   size_t Length() const { return mLength; }
61 
62   uint8_t operator[](size_t aIndex) const {
63     MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
64     return (*mBuffer)[mStart + aIndex];
65   }
66 
Append(const MediaSpan & aBuffer)67   bool Append(const MediaSpan& aBuffer) { return Append(aBuffer.Buffer()); }
68 
Append(MediaByteBuffer * aBuffer)69   bool Append(MediaByteBuffer* aBuffer) {
70     if (!aBuffer) {
71       return true;
72     }
73     if (mStart + mLength < mBuffer->Length()) {
74       // This MediaSpan finishes before the end of its buffer. The buffer
75       // could be shared with another MediaSpan. So we can't just append to
76       // the underlying buffer without risking damaging other MediaSpans' data.
77       // So we must reallocate a new buffer, copy our old data into it, and
78       // append the new data into it.
79       RefPtr<MediaByteBuffer> buffer =
80           new MediaByteBuffer(mLength + aBuffer->Length());
81       if (!buffer->AppendElements(Elements(), Length(), fallible) ||
82           !buffer->AppendElements(*aBuffer, fallible)) {
83         return false;
84       }
85       mBuffer = buffer;
86       mLength += aBuffer->Length();
87       return true;
88     }
89     if (!mBuffer->AppendElements(*aBuffer, fallible)) {
90       return false;
91     }
92     mLength += aBuffer->Length();
93     return true;
94   }
95 
96   // Returns a new MediaSpan, spanning from the start of this span,
97   // up until aEnd.
To(size_t aEnd)98   MediaSpan To(size_t aEnd) const {
99     MOZ_DIAGNOSTIC_ASSERT(aEnd <= Length());
100     return MediaSpan(mBuffer, mStart, aEnd);
101   }
102 
103   // Returns a new MediaSpan, spanning from aStart bytes offset from
104   // the start of this span, until the end of this span.
From(size_t aStart)105   MediaSpan From(size_t aStart) const {
106     MOZ_DIAGNOSTIC_ASSERT(aStart <= Length());
107     return MediaSpan(mBuffer, mStart + aStart, Length() - aStart);
108   }
109 
RemoveFront(size_t aNumBytes)110   void RemoveFront(size_t aNumBytes) {
111     MOZ_DIAGNOSTIC_ASSERT(aNumBytes <= Length());
112     mStart += aNumBytes;
113     mLength -= aNumBytes;
114   }
115 
Buffer()116   MediaByteBuffer* Buffer() const { return mBuffer; }
117 
118  private:
MediaSpan(MediaByteBuffer * aBuffer,size_t aStart,size_t aLength)119   MediaSpan(MediaByteBuffer* aBuffer, size_t aStart, size_t aLength)
120       : mBuffer(aBuffer), mStart(aStart), mLength(aLength) {
121     MOZ_DIAGNOSTIC_ASSERT(mStart + mLength <= mBuffer->Length());
122   }
123 
124   RefPtr<MediaByteBuffer> mBuffer;
125   size_t mStart = 0;
126   size_t mLength = 0;
127 };
128 
129 }  // namespace mozilla
130 
131 #endif  // MediaSpan_h
132