[go: nahoru, domu]

blob: 218b84fbfb2d4e695fc3a1b580e698b14046a0c2 [file] [log] [blame]
Avi Drissman0ff8a7e2022-09-13 18:35:421// Copyright 2011 The Chromium Authors
tommi@chromium.orgfbd31eb2011-03-01 00:19:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef COURGETTE_MEMORY_ALLOCATOR_H_
6#define COURGETTE_MEMORY_ALLOCATOR_H_
7
aviab98dcc92015-12-21 19:35:338#include <stddef.h>
9#include <stdint.h>
grt825b91f2015-11-06 23:06:5710#include <stdlib.h>
Piotr Tworekafba0d72020-04-28 08:02:4011#include <string.h>
tommi@chromium.orgfbd31eb2011-03-01 00:19:0212
rvargas@chromium.org578d5dae2014-02-21 16:07:5213#include "base/files/file.h"
tommi@chromium.orgfbd31eb2011-03-01 00:19:0214#include "base/logging.h"
Keishi Hattori0e45c022021-11-27 09:25:5215#include "base/memory/raw_ptr.h"
grt825b91f2015-11-06 23:06:5716#include "base/process/memory.h"
Xiaohan Wang9a8720c22022-01-08 01:23:2717#include "build/build_config.h"
tommi@chromium.orgfbd31eb2011-03-01 00:19:0218
tommi@chromium.orgc8240b12011-03-22 20:19:4919#ifndef NDEBUG
20
21// A helper class to track down call sites that are not handling error cases.
22template<class T>
23class CheckReturnValue {
24 public:
25 // Not marked explicit on purpose.
26 CheckReturnValue(T value) : value_(value), checked_(false) { // NOLINT
27 }
28 CheckReturnValue(const CheckReturnValue& other)
29 : value_(other.value_), checked_(other.checked_) {
30 other.checked_ = true;
31 }
32
33 CheckReturnValue& operator=(const CheckReturnValue& other) {
34 if (this != &other) {
35 DCHECK(checked_);
36 value_ = other.value_;
37 checked_ = other.checked_;
38 other.checked_ = true;
39 }
grt47568132016-11-22 17:16:5740 return *this;
tommi@chromium.orgc8240b12011-03-22 20:19:4941 }
42
43 ~CheckReturnValue() {
44 DCHECK(checked_);
45 }
46
47 operator const T&() const {
48 checked_ = true;
49 return value_;
50 }
51
52 private:
53 T value_;
54 mutable bool checked_;
55};
56typedef CheckReturnValue<bool> CheckBool;
57#else
58typedef bool CheckBool;
59#endif
60
tommi@chromium.orgfbd31eb2011-03-01 00:19:0261namespace courgette {
62
grt825b91f2015-11-06 23:06:5763// Allocates memory for an instance of type T, instantiates an object in that
64// memory with arguments |args| (of type ArgTypes), and returns the constructed
65// instance. Returns null if allocation fails.
66template <class T, class... ArgTypes>
67T* UncheckedNew(ArgTypes... args) {
68 void* ram = nullptr;
69 return base::UncheckedMalloc(sizeof(T), &ram) ? new (ram) T(args...)
70 : nullptr;
71}
72
73// Complement of UncheckedNew(): destructs |object| and releases its memory.
74template <class T>
75void UncheckedDelete(T* object) {
76 if (object) {
77 object->T::~T();
78 free(object);
79 }
80}
81
mostynb1007a4a2016-04-11 23:18:0682// A deleter for std::unique_ptr that will delete the object via
83// UncheckedDelete().
grt825b91f2015-11-06 23:06:5784template <class T>
85struct UncheckedDeleter {
86 inline void operator()(T* ptr) const { UncheckedDelete(ptr); }
87};
88
Xiaohan Wang9a8720c22022-01-08 01:23:2789#if BUILDFLAG(IS_WIN)
tommi@chromium.orgfbd31eb2011-03-01 00:19:0290
tommi@chromium.orgfbd31eb2011-03-01 00:19:0291// Manages a read/write virtual mapping of a physical file.
92class FileMapping {
93 public:
94 FileMapping();
95 ~FileMapping();
96
97 // Map a file from beginning to |size|.
tommi@chromium.org43a9e242011-04-06 17:42:4598 bool Create(HANDLE file, size_t size);
tommi@chromium.orgfbd31eb2011-03-01 00:19:0299 void Close();
100
101 // Returns true iff a mapping has been created.
102 bool valid() const;
103
104 // Returns a writable pointer to the beginning of the memory mapped file.
Lukasz Anforowiczab019862020-05-29 02:44:24105 // If Create has not been called successfully, return value is nullptr.
tommi@chromium.orgfbd31eb2011-03-01 00:19:02106 void* view() const;
107
108 protected:
tommi@chromium.org43a9e242011-04-06 17:42:45109 bool InitializeView(size_t size);
tommi@chromium.org6c131952011-03-03 23:39:32110
tommi@chromium.orgfbd31eb2011-03-01 00:19:02111 HANDLE mapping_;
Keishi Hattori0e45c022021-11-27 09:25:52112 raw_ptr<void> view_;
tommi@chromium.orgfbd31eb2011-03-01 00:19:02113};
114
115// Manages a temporary file and a memory mapping of the temporary file.
116// The memory that this class manages holds a pointer back to the TempMapping
117// object itself, so that given a memory pointer allocated by this class,
118// you can get a pointer to the TempMapping instance that owns that memory.
119class TempMapping {
120 public:
121 TempMapping();
122 ~TempMapping();
123
124 // Creates a temporary file of size |size| and maps it into the current
tommi@chromium.org43a9e242011-04-06 17:42:45125 // process's address space.
126 bool Initialize(size_t size);
tommi@chromium.orgfbd31eb2011-03-01 00:19:02127
128 // Returns a writable pointer to the reserved memory.
129 void* memory() const;
130
tommi@chromium.org43a9e242011-04-06 17:42:45131 // Returns true if the mapping is valid and memory is available.
132 bool valid() const;
133
tommi@chromium.orgfbd31eb2011-03-01 00:19:02134 // Returns a pointer to the TempMapping instance that allocated the |mem|
135 // block of memory. It's the callers responsibility to make sure that
136 // the memory block was allocated by the TempMapping class.
137 static TempMapping* GetMappingFromPtr(void* mem);
138
139 protected:
rvargas@chromium.org578d5dae2014-02-21 16:07:52140 base::File file_;
tommi@chromium.orgfbd31eb2011-03-01 00:19:02141 FileMapping mapping_;
142};
143
tommi@chromium.org43a9e242011-04-06 17:42:45144// A memory allocator class that allocates memory either from the heap or via a
145// temporary file. The interface is STL inspired but the class does not throw
Lukasz Anforowiczab019862020-05-29 02:44:24146// STL exceptions on allocation failure. Instead it returns nullptr.
tommi@chromium.org43a9e242011-04-06 17:42:45147// A file allocation will be made if either the requested memory size exceeds
148// |kMaxHeapAllocationSize| or if a heap allocation fails.
tommi@chromium.orgfbd31eb2011-03-01 00:19:02149// Allocating the memory as a mapping of a temporary file solves the problem
150// that there might not be enough physical memory and pagefile to support the
151// allocation. This can happen because these resources are too small, or
152// already committed to other processes. Provided there is enough disk, the
153// temporary file acts like a pagefile that other processes can't access.
154template<class T>
155class MemoryAllocator {
156 public:
157 typedef T value_type;
158 typedef value_type* pointer;
159 typedef value_type& reference;
160 typedef const value_type* const_pointer;
161 typedef const value_type& const_reference;
162 typedef size_t size_type;
163 typedef ptrdiff_t difference_type;
164
165 // Each allocation is tagged with a single byte so that we know how to
166 // deallocate it.
huangs160b06e2017-06-02 15:19:09167 enum AllocationType : uint8_t {
168 HEAP_ALLOCATION = 0xF0, // Non-trivial constants to detect corruption.
169 FILE_ALLOCATION = 0x0F,
tommi@chromium.orgfbd31eb2011-03-01 00:19:02170 };
171
172 // 5MB is the maximum heap allocation size that we'll attempt.
173 // When applying a patch for Chrome 10.X we found that at this
174 // threshold there were 17 allocations higher than this threshold
175 // (largest at 136MB) 10 allocations just below the threshold and 6362
176 // smaller allocations.
177 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
178
179 template<class OtherT>
180 struct rebind {
tommi@chromium.org43a9e242011-04-06 17:42:45181 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
tommi@chromium.orgfbd31eb2011-03-01 00:19:02182 typedef MemoryAllocator<OtherT> other;
183 };
184
cblume0bf02bd52017-03-20 22:50:29185 MemoryAllocator() {}
tommi@chromium.orgfbd31eb2011-03-01 00:19:02186
bradnelson@google.com18e07fd2011-03-29 01:21:23187 // We can't use an explicit constructor here, as dictated by our style guide.
188 // The implementation of basic_string in Visual Studio 2010 prevents this.
cblume0bf02bd52017-03-20 22:50:29189 MemoryAllocator(const MemoryAllocator<T>& other) { // NOLINT
tommi@chromium.orgfbd31eb2011-03-01 00:19:02190 }
191
cblume0bf02bd52017-03-20 22:50:29192 template <class OtherT>
193 MemoryAllocator(const MemoryAllocator<OtherT>& other) { // NOLINT
tommi@chromium.orgfbd31eb2011-03-01 00:19:02194 }
195
196 ~MemoryAllocator() {
197 }
198
199 void deallocate(pointer ptr, size_type size) {
aviab98dcc92015-12-21 19:35:33200 uint8_t* mem = reinterpret_cast<uint8_t*>(ptr);
tommi@chromium.orgfbd31eb2011-03-01 00:19:02201 mem -= sizeof(T);
huangs160b06e2017-06-02 15:19:09202 if (mem[0] == HEAP_ALLOCATION)
grt825b91f2015-11-06 23:06:57203 free(mem);
huangs160b06e2017-06-02 15:19:09204 else if (mem[0] == FILE_ALLOCATION)
grt825b91f2015-11-06 23:06:57205 UncheckedDelete(TempMapping::GetMappingFromPtr(mem));
huangs160b06e2017-06-02 15:19:09206 else
207 LOG(FATAL);
tommi@chromium.orgfbd31eb2011-03-01 00:19:02208 }
209
210 pointer allocate(size_type count) {
211 // We use the first byte of each allocation to mark the allocation type.
212 // However, so that the allocation is properly aligned, we allocate an
213 // extra element and then use the first byte of the first element
214 // to mark the allocation type.
215 count++;
216
217 if (count > max_size())
Lukasz Anforowiczab019862020-05-29 02:44:24218 return nullptr;
tommi@chromium.orgfbd31eb2011-03-01 00:19:02219
220 size_type bytes = count * sizeof(T);
Lukasz Anforowiczab019862020-05-29 02:44:24221 uint8_t* mem = nullptr;
tommi@chromium.orgfbd31eb2011-03-01 00:19:02222
223 // First see if we can do this allocation on the heap.
grt825b91f2015-11-06 23:06:57224 if (count < kMaxHeapAllocationSize &&
225 base::UncheckedMalloc(bytes, reinterpret_cast<void**>(&mem))) {
aviab98dcc92015-12-21 19:35:33226 mem[0] = static_cast<uint8_t>(HEAP_ALLOCATION);
tommi@chromium.orgfbd31eb2011-03-01 00:19:02227 } else {
grt825b91f2015-11-06 23:06:57228 // Back the allocation with a temp file if either the request exceeds the
229 // max heap allocation threshold or the heap allocation failed.
230 TempMapping* mapping = UncheckedNew<TempMapping>();
231 if (mapping) {
232 if (mapping->Initialize(bytes)) {
aviab98dcc92015-12-21 19:35:33233 mem = reinterpret_cast<uint8_t*>(mapping->memory());
234 mem[0] = static_cast<uint8_t>(FILE_ALLOCATION);
grt825b91f2015-11-06 23:06:57235 } else {
236 UncheckedDelete(mapping);
237 }
tommi@chromium.org43a9e242011-04-06 17:42:45238 }
tommi@chromium.orgfbd31eb2011-03-01 00:19:02239 }
wafflese5d0b0e2017-03-24 21:18:19240 // If the above fails (e.g. because we are in a sandbox), just try the heap.
241 if (!mem && base::UncheckedMalloc(bytes, reinterpret_cast<void**>(&mem))) {
242 mem[0] = static_cast<uint8_t>(HEAP_ALLOCATION);
243 }
Lukasz Anforowiczab019862020-05-29 02:44:24244 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : nullptr;
tommi@chromium.orgfbd31eb2011-03-01 00:19:02245 }
246
247 pointer allocate(size_type count, const void* hint) {
248 return allocate(count);
249 }
250
251 void construct(pointer ptr, const T& value) {
252 ::new(ptr) T(value);
253 }
254
255 void destroy(pointer ptr) {
256 ptr->~T();
257 }
258
cblume0bf02bd52017-03-20 22:50:29259 size_type max_size() const {
tommi@chromium.orgfbd31eb2011-03-01 00:19:02260 size_type count = static_cast<size_type>(-1) / sizeof(T);
261 return (0 < count ? count : 1);
262 }
263};
264
Xiaohan Wang64dc5702022-01-22 01:04:01265#else // BUILDFLAG(IS_WIN)
tommi@chromium.orgfbd31eb2011-03-01 00:19:02266
tommi@chromium.org43a9e242011-04-06 17:42:45267// On Mac, Linux, we use a bare bones implementation that only does
268// heap allocations.
tommi@chromium.orgfbd31eb2011-03-01 00:19:02269template<class T>
tommi@chromium.org43a9e242011-04-06 17:42:45270class MemoryAllocator {
tommi@chromium.orgfbd31eb2011-03-01 00:19:02271 public:
tommi@chromium.org43a9e242011-04-06 17:42:45272 typedef T value_type;
273 typedef value_type* pointer;
274 typedef value_type& reference;
275 typedef const value_type* const_pointer;
276 typedef const value_type& const_reference;
277 typedef size_t size_type;
278 typedef ptrdiff_t difference_type;
279
280 template<class OtherT>
281 struct rebind {
282 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
283 typedef MemoryAllocator<OtherT> other;
284 };
285
286 MemoryAllocator() {
287 }
288
289 explicit MemoryAllocator(const MemoryAllocator<T>& other) {
290 }
291
292 template<class OtherT>
293 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
294 }
295
296 ~MemoryAllocator() {
297 }
298
grt825b91f2015-11-06 23:06:57299 void deallocate(pointer ptr, size_type size) { free(ptr); }
tommi@chromium.org43a9e242011-04-06 17:42:45300
301 pointer allocate(size_type count) {
302 if (count > max_size())
Lukasz Anforowiczab019862020-05-29 02:44:24303 return nullptr;
grt825b91f2015-11-06 23:06:57304 pointer result = nullptr;
305 return base::UncheckedMalloc(count * sizeof(T),
306 reinterpret_cast<void**>(&result))
307 ? result
308 : nullptr;
tommi@chromium.org43a9e242011-04-06 17:42:45309 }
310
311 pointer allocate(size_type count, const void* hint) {
312 return allocate(count);
313 }
314
315 void construct(pointer ptr, const T& value) {
316 ::new(ptr) T(value);
317 }
318
319 void destroy(pointer ptr) {
320 ptr->~T();
321 }
322
323 size_type max_size() const {
324 size_type count = static_cast<size_type>(-1) / sizeof(T);
325 return (0 < count ? count : 1);
326 }
tommi@chromium.orgfbd31eb2011-03-01 00:19:02327};
328
Xiaohan Wang64dc5702022-01-22 01:04:01329#endif // BUILDFLAG(IS_WIN)
tommi@chromium.orgfbd31eb2011-03-01 00:19:02330
tommi@chromium.org43a9e242011-04-06 17:42:45331// Manages a growable buffer. The buffer allocation is done by the
332// MemoryAllocator class. This class will not throw exceptions so call sites
333// must be prepared to handle memory allocation failures.
334// The interface is STL inspired to avoid having to make too many changes
335// to code that previously was using STL.
336template<typename T, class Allocator = MemoryAllocator<T> >
337class NoThrowBuffer {
338 public:
339 typedef T value_type;
340 static const size_t kAllocationFailure = 0xffffffff;
341 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T);
342
Lukasz Anforowiczab019862020-05-29 02:44:24343 NoThrowBuffer() : buffer_(nullptr), size_(0), alloc_size_(0) {}
tommi@chromium.org43a9e242011-04-06 17:42:45344
345 ~NoThrowBuffer() {
346 clear();
347 }
348
349 void clear() {
350 if (buffer_) {
351 alloc_.deallocate(buffer_, alloc_size_);
Lukasz Anforowiczab019862020-05-29 02:44:24352 buffer_ = nullptr;
tommi@chromium.org43a9e242011-04-06 17:42:45353 size_ = 0;
354 alloc_size_ = 0;
355 }
356 }
357
358 bool empty() const {
359 return size_ == 0;
360 }
361
Daniel Cheng8021cefa2022-01-14 14:39:55362 [[nodiscard]] CheckBool reserve(size_t size) {
tommi@chromium.org43a9e242011-04-06 17:42:45363 if (failed())
364 return false;
365
366 if (size <= alloc_size_)
367 return true;
368
369 if (size < kStartSize)
370 size = kStartSize;
371
372 T* new_buffer = alloc_.allocate(size);
373 if (!new_buffer) {
374 clear();
375 alloc_size_ = kAllocationFailure;
376 } else {
377 if (buffer_) {
378 memcpy(new_buffer, buffer_, size_ * sizeof(T));
379 alloc_.deallocate(buffer_, alloc_size_);
380 }
381 buffer_ = new_buffer;
382 alloc_size_ = size;
383 }
384
385 return !failed();
386 }
387
Daniel Cheng8021cefa2022-01-14 14:39:55388 [[nodiscard]] CheckBool append(const T* data, size_t size) {
tommi@chromium.org43a9e242011-04-06 17:42:45389 if (failed())
390 return false;
391
392 if (size > alloc_.max_size() - size_)
393 return false;
394
395 if (!size)
396 return true;
397
huangs57264dc2015-07-20 21:55:04398 // Disallow source range from overlapping with buffer_, since in this case
399 // reallocation would cause use-after-free.
400 DCHECK(data + size <= buffer_ || data >= buffer_ + alloc_size_);
401
tommi@chromium.org43a9e242011-04-06 17:42:45402 if ((alloc_size_ - size_) < size) {
403 const size_t max_size = alloc_.max_size();
404 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize;
405 while (new_size < size_ + size) {
406 if (new_size < max_size - new_size) {
407 new_size *= 2;
408 } else {
409 new_size = max_size;
410 }
411 }
412 if (!reserve(new_size))
413 return false;
414 }
415
416 memcpy(buffer_ + size_, data, size * sizeof(T));
417 size_ += size;
418
419 return true;
420 }
421
Daniel Cheng8021cefa2022-01-14 14:39:55422 [[nodiscard]] CheckBool resize(size_t size, const T& init_value) {
tommi@chromium.org43a9e242011-04-06 17:42:45423 if (size > size_) {
424 if (!reserve(size))
425 return false;
426 for (size_t i = size_; i < size; ++i)
427 buffer_[i] = init_value;
428 } else if (size < size_) {
429 // TODO(tommi): Should we allocate a new, smaller buffer?
430 // It might be faster for us to simply change the size.
431 }
432
433 size_ = size;
434
435 return true;
436 }
437
Daniel Cheng8021cefa2022-01-14 14:39:55438 [[nodiscard]] CheckBool push_back(const T& item) { return append(&item, 1); }
tommi@chromium.org43a9e242011-04-06 17:42:45439
440 const T& back() const {
441 return buffer_[size_ - 1];
442 }
443
444 T& back() {
445 return buffer_[size_ - 1];
446 }
447
448 const T* begin() const {
449 if (!size_)
Lukasz Anforowiczab019862020-05-29 02:44:24450 return nullptr;
huangs57264dc2015-07-20 21:55:04451 return buffer_;
tommi@chromium.org43a9e242011-04-06 17:42:45452 }
453
454 T* begin() {
455 if (!size_)
Lukasz Anforowiczab019862020-05-29 02:44:24456 return nullptr;
huangs57264dc2015-07-20 21:55:04457 return buffer_;
tommi@chromium.org43a9e242011-04-06 17:42:45458 }
459
460 const T* end() const {
461 if (!size_)
Lukasz Anforowiczab019862020-05-29 02:44:24462 return nullptr;
huangs57264dc2015-07-20 21:55:04463 return buffer_ + size_;
tommi@chromium.org43a9e242011-04-06 17:42:45464 }
465
466 T* end() {
467 if (!size_)
Lukasz Anforowiczab019862020-05-29 02:44:24468 return nullptr;
huangs57264dc2015-07-20 21:55:04469 return buffer_ + size_;
tommi@chromium.org43a9e242011-04-06 17:42:45470 }
471
472 const T& operator[](size_t index) const {
473 DCHECK(index < size_);
474 return buffer_[index];
475 }
476
477 T& operator[](size_t index) {
478 DCHECK(index < size_);
479 return buffer_[index];
480 }
481
482 size_t size() const {
483 return size_;
484 }
485
huangs869de822015-11-19 20:14:02486 size_t capacity() const {
487 return alloc_size_;
488 }
489
tommi@chromium.org43a9e242011-04-06 17:42:45490 T* data() const {
491 return buffer_;
492 }
493
494 // Returns true if an allocation failure has ever occurred for this object.
495 bool failed() const {
496 return alloc_size_ == kAllocationFailure;
497 }
498
499 protected:
Tom Sepez09c200ae2023-02-15 19:08:11500 raw_ptr<T, DanglingUntriaged | AllowPtrArithmetic> buffer_;
tommi@chromium.org43a9e242011-04-06 17:42:45501 size_t size_; // how much of the buffer we're using.
502 size_t alloc_size_; // how much space we have allocated.
503 Allocator alloc_;
504};
505
tommi@chromium.orgfbd31eb2011-03-01 00:19:02506} // namespace courgette
507
508#endif // COURGETTE_MEMORY_ALLOCATOR_H_