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