blob: b94f8cc583754c03b6b881680627107a8b8a7c03 [file] [log] [blame]
Brian Silvermanf51499a2020-09-21 12:49:08 -07001#ifndef AOS_CONTAINERS_RESIZEABLE_BUFFER_H_
2#define AOS_CONTAINERS_RESIZEABLE_BUFFER_H_
3
4#include <stdlib.h>
5
6#include <memory>
7
8#include "glog/logging.h"
9
10namespace aos {
11
12// Kind of like a subset of vector<uint8_t>, but with less destructor calls.
13// When building unoptimized, especially with sanitizers, the vector<uint8_t>
14// version ends up being really slow in tests.
15class ResizeableBuffer {
16 public:
17 ResizeableBuffer() = default;
18
19 ResizeableBuffer(const ResizeableBuffer &other) { *this = other; }
20 ResizeableBuffer(ResizeableBuffer &&other) { *this = std::move(other); }
21 ResizeableBuffer &operator=(const ResizeableBuffer &other) {
22 resize(other.size());
23 memcpy(storage_.get(), other.storage_.get(), size());
24 return *this;
25 }
26 ResizeableBuffer &operator=(ResizeableBuffer &&other) {
27 std::swap(storage_, other.storage_);
28 std::swap(size_, other.size_);
29 std::swap(capacity_, other.capacity_);
30 return *this;
31 }
32
33 uint8_t *data() { return static_cast<uint8_t *>(storage_.get()); }
34 const uint8_t *data() const {
35 return static_cast<const uint8_t *>(storage_.get());
36 }
37
38 uint8_t *begin() { return data(); }
39 const uint8_t *begin() const { return data(); }
40 uint8_t *end() { return data() + size(); }
41 const uint8_t *end() const { return data() + size(); }
42
43 size_t size() const { return size_; }
44 size_t capacity() const { return capacity_; }
45
46 void resize(size_t new_size) {
47 if (new_size > capacity_) {
48 Allocate(new_size);
49 }
50 size_ = new_size;
51 }
52
53 void erase_front(size_t count) {
54 if (count == 0) {
55 return;
56 }
57 CHECK_LE(count, size_);
58 memmove(static_cast<void *>(data()), static_cast<void *>(data() + count),
59 size_ - count);
60 size_ -= count;
61 }
62
63 void push_back(uint8_t x) {
64 if (size_ == capacity_) {
65 Allocate(std::max<size_t>(16, size_ * 2));
66 }
67 *end() = x;
68 ++size_;
69 CHECK_LE(size_, capacity_);
70 }
71
72 private:
73 // We need this silly function because C++ is bad, and extensions to it which
74 // we work with make it a true nightmare.
75 //
76 // (a) You can't easily write out the signature of free because it depends on
77 // whether exceptions are enabled or not. You could use decltype, but see (b).
78 //
79 // (b) You can't easily write &free because CUDA overloads it with a
80 // __device__ version. You could cast to the appropriate version, but see (a).
81 //
82 // There's probably some kind of SFINAE thing which could find the matching
83 // signature from a set of choices, and then we could just
84 // static_cast<TheSignature>(&free). However, that sounds like a nightmare,
85 // especially because it has to conditionally enable the part mentioning CUDA
86 // identifiers in the preprocessor. This little function is way simpler.
87 static void DoFree(void *p) { free(p); }
88
89 void Allocate(size_t new_capacity) {
90 void *const old = storage_.release();
91 storage_.reset(CHECK_NOTNULL(realloc(old, new_capacity)));
92 capacity_ = new_capacity;
93 }
94
95 std::unique_ptr<void, decltype(&DoFree)> storage_{nullptr, &DoFree};
96 size_t size_ = 0, capacity_ = 0;
97};
98
99} // namespace aos
100
101#endif // AOS_CONTAINERS_RESIZEABLE_BUFFER_H_