1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "mojo/public/cpp/base/big_buffer.h"
6
7 #include "base/logging.h"
8
9 namespace mojo_base {
10
11 namespace {
12
13 // In the case of shared memory allocation failure, we still attempt to fall
14 // back onto inline bytes unless the buffer size exceeds a very large threshold,
15 // given by this constant.
16 constexpr size_t kMaxFallbackInlineBytes = 127 * 1024 * 1024;
17
18 } // namespace
19
20 namespace internal {
21
22 BigBufferSharedMemoryRegion::BigBufferSharedMemoryRegion() = default;
23
BigBufferSharedMemoryRegion(mojo::ScopedSharedBufferHandle buffer_handle,size_t size)24 BigBufferSharedMemoryRegion::BigBufferSharedMemoryRegion(
25 mojo::ScopedSharedBufferHandle buffer_handle,
26 size_t size)
27 : size_(size),
28 buffer_handle_(std::move(buffer_handle)),
29 buffer_mapping_(buffer_handle_->Map(size)) {}
30
31 BigBufferSharedMemoryRegion::BigBufferSharedMemoryRegion(
32 BigBufferSharedMemoryRegion&& other) = default;
33
34 BigBufferSharedMemoryRegion::~BigBufferSharedMemoryRegion() = default;
35
36 BigBufferSharedMemoryRegion& BigBufferSharedMemoryRegion::operator=(
37 BigBufferSharedMemoryRegion&& other) = default;
38
TakeBufferHandle()39 mojo::ScopedSharedBufferHandle BigBufferSharedMemoryRegion::TakeBufferHandle() {
40 DCHECK(buffer_handle_.is_valid());
41 buffer_mapping_.reset();
42 return std::move(buffer_handle_);
43 }
44
45 } // namespace internal
46
47 namespace {
48
TryCreateSharedMemory(size_t size,BigBuffer::StorageType * storage_type,base::Optional<internal::BigBufferSharedMemoryRegion> * shared_memory)49 void TryCreateSharedMemory(
50 size_t size,
51 BigBuffer::StorageType* storage_type,
52 base::Optional<internal::BigBufferSharedMemoryRegion>* shared_memory) {
53 if (size > BigBuffer::kMaxInlineBytes) {
54 auto buffer = mojo::SharedBufferHandle::Create(size);
55 if (buffer.is_valid()) {
56 internal::BigBufferSharedMemoryRegion shm_region(std::move(buffer), size);
57 if (shm_region.memory()) {
58 *storage_type = BigBuffer::StorageType::kSharedMemory;
59 shared_memory->emplace(std::move(shm_region));
60 return;
61 }
62 }
63
64 if (size > kMaxFallbackInlineBytes) {
65 // The data is too large to even bother with inline fallback, so we
66 // instead produce an invalid buffer. This will always fail validation on
67 // the receiving end.
68 *storage_type = BigBuffer::StorageType::kInvalidBuffer;
69 return;
70 }
71 }
72
73 // We can use inline memory.
74 *storage_type = BigBuffer::StorageType::kBytes;
75 }
76
77 } // namespace
78
79 // static
80 constexpr size_t BigBuffer::kMaxInlineBytes;
81
BigBuffer()82 BigBuffer::BigBuffer() : storage_type_(StorageType::kBytes), bytes_size_(0) {}
83
BigBuffer(BigBuffer && other)84 BigBuffer::BigBuffer(BigBuffer&& other)
85 : storage_type_(other.storage_type_),
86 bytes_(std::move(other.bytes_)),
87 bytes_size_(other.bytes_size_),
88 shared_memory_(std::move(other.shared_memory_)) {
89 // Make sure |other| looks empty.
90 other.storage_type_ = StorageType::kInvalidBuffer;
91 other.bytes_size_ = 0;
92 }
93
BigBuffer(base::span<const uint8_t> data)94 BigBuffer::BigBuffer(base::span<const uint8_t> data) {
95 *this = BigBufferView::ToBigBuffer(BigBufferView(data));
96 }
97
BigBuffer(const std::vector<uint8_t> & data)98 BigBuffer::BigBuffer(const std::vector<uint8_t>& data)
99 : BigBuffer(base::make_span(data)) {}
100
BigBuffer(internal::BigBufferSharedMemoryRegion shared_memory)101 BigBuffer::BigBuffer(internal::BigBufferSharedMemoryRegion shared_memory)
102 : storage_type_(StorageType::kSharedMemory),
103 shared_memory_(std::move(shared_memory)) {}
104
BigBuffer(size_t size)105 BigBuffer::BigBuffer(size_t size) {
106 TryCreateSharedMemory(size, &storage_type_, &shared_memory_);
107 if (storage_type_ == BigBuffer::StorageType::kBytes) {
108 // Either |size| is small enough or shared memory allocation failed, and
109 // fallback to inline allocation is feasible.
110 bytes_ = std::make_unique<uint8_t[]>(size);
111 bytes_size_ = size;
112 }
113 }
114
115 BigBuffer::~BigBuffer() = default;
116
operator =(BigBuffer && other)117 BigBuffer& BigBuffer::operator=(BigBuffer&& other) {
118 storage_type_ = other.storage_type_;
119 bytes_ = std::move(other.bytes_);
120 bytes_size_ = other.bytes_size_;
121 shared_memory_ = std::move(other.shared_memory_);
122 // Make sure |other| looks empty.
123 other.storage_type_ = StorageType::kInvalidBuffer;
124 other.bytes_size_ = 0;
125 return *this;
126 }
127
data()128 uint8_t* BigBuffer::data() {
129 return const_cast<uint8_t*>(const_cast<const BigBuffer*>(this)->data());
130 }
131
data() const132 const uint8_t* BigBuffer::data() const {
133 switch (storage_type_) {
134 case StorageType::kBytes:
135 return bytes_.get();
136 case StorageType::kSharedMemory:
137 DCHECK(shared_memory_->buffer_mapping_);
138 return static_cast<const uint8_t*>(
139 const_cast<const void*>(shared_memory_->buffer_mapping_.get()));
140 case StorageType::kInvalidBuffer:
141 // We return null here but do not assert unlike the default case. No
142 // consumer is allowed to dereference this when |size()| is zero anyway.
143 return nullptr;
144 default:
145 NOTREACHED();
146 return nullptr;
147 }
148 }
149
size() const150 size_t BigBuffer::size() const {
151 switch (storage_type_) {
152 case StorageType::kBytes:
153 return bytes_size_;
154 case StorageType::kSharedMemory:
155 return shared_memory_->size();
156 case StorageType::kInvalidBuffer:
157 return 0;
158 default:
159 NOTREACHED();
160 return 0;
161 }
162 }
163
164 BigBufferView::BigBufferView() = default;
165
166 BigBufferView::BigBufferView(BigBufferView&& other) = default;
167
BigBufferView(base::span<const uint8_t> bytes)168 BigBufferView::BigBufferView(base::span<const uint8_t> bytes) {
169 TryCreateSharedMemory(bytes.size(), &storage_type_, &shared_memory_);
170 if (storage_type_ == BigBuffer::StorageType::kSharedMemory) {
171 DCHECK(shared_memory_->memory());
172 std::copy(bytes.begin(), bytes.end(),
173 static_cast<uint8_t*>(shared_memory_->memory()));
174 return;
175 }
176 if (storage_type_ == BigBuffer::StorageType::kBytes) {
177 // Either the data is small enough or shared memory allocation failed.
178 // Either way we fall back to directly referencing the input bytes.
179 bytes_ = bytes;
180 }
181 }
182
183 BigBufferView::~BigBufferView() = default;
184
185 BigBufferView& BigBufferView::operator=(BigBufferView&& other) = default;
186
SetBytes(base::span<const uint8_t> bytes)187 void BigBufferView::SetBytes(base::span<const uint8_t> bytes) {
188 DCHECK(bytes_.empty());
189 DCHECK(!shared_memory_);
190 storage_type_ = BigBuffer::StorageType::kBytes;
191 bytes_ = bytes;
192 }
193
SetSharedMemory(internal::BigBufferSharedMemoryRegion shared_memory)194 void BigBufferView::SetSharedMemory(
195 internal::BigBufferSharedMemoryRegion shared_memory) {
196 DCHECK(bytes_.empty());
197 DCHECK(!shared_memory_);
198 storage_type_ = BigBuffer::StorageType::kSharedMemory;
199 shared_memory_ = std::move(shared_memory);
200 }
201
data() const202 base::span<const uint8_t> BigBufferView::data() const {
203 if (storage_type_ == BigBuffer::StorageType::kBytes) {
204 return bytes_;
205 } else if (storage_type_ == BigBuffer::StorageType::kSharedMemory) {
206 DCHECK(shared_memory_.has_value());
207 return base::make_span(static_cast<const uint8_t*>(const_cast<const void*>(
208 shared_memory_->memory())),
209 shared_memory_->size());
210 }
211
212 return base::span<const uint8_t>();
213 }
214
215 // static
ToBigBuffer(BigBufferView view)216 BigBuffer BigBufferView::ToBigBuffer(BigBufferView view) {
217 BigBuffer buffer;
218 buffer.storage_type_ = view.storage_type_;
219 if (view.storage_type_ == BigBuffer::StorageType::kBytes) {
220 buffer.bytes_ = std::make_unique<uint8_t[]>(view.bytes_.size());
221 buffer.bytes_size_ = view.bytes_.size();
222 std::copy(view.bytes_.begin(), view.bytes_.end(), buffer.bytes_.get());
223 } else if (view.storage_type_ == BigBuffer::StorageType::kSharedMemory) {
224 buffer.shared_memory_ = std::move(*view.shared_memory_);
225 }
226 return buffer;
227 }
228
229 // static
CreateInvalidForTest()230 BigBufferView BigBufferView::CreateInvalidForTest() {
231 BigBufferView view;
232 view.storage_type_ = BigBuffer::StorageType::kInvalidBuffer;
233 return view;
234 }
235
236 } // namespace mojo_base
237