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