blob: d2c47e55aca6c995334963022b3f2f01a8c6578b [file] [log] [blame]
Austin Schuh3c9f92c2024-04-30 17:56:42 -07001#include "aos/flatbuffers/aligned_allocator.h"
2
3namespace aos::fbs {
4
5AlignedVectorAllocator::~AlignedVectorAllocator() {
6 CHECK(buffer_.empty())
7 << ": Must deallocate before destroying the AlignedVectorAllocator.";
8}
9
10std::optional<std::span<uint8_t>> AlignedVectorAllocator::Allocate(
11 size_t size, size_t /*alignment*/, fbs::SetZero set_zero) {
12 CHECK(buffer_.empty()) << ": Must deallocate before calling Allocate().";
13 buffer_.resize(((size + kAlignment - 1) / kAlignment) * kAlignment);
14 allocated_size_ = size;
15 if (set_zero == fbs::SetZero::kYes) {
16 memset(buffer_.data(), 0, buffer_.size());
17 }
18
19 return std::span<uint8_t>{data(), allocated_size_};
20}
21
22std::optional<std::span<uint8_t>> AlignedVectorAllocator::InsertBytes(
23 void *insertion_point, size_t bytes, size_t /*alignment*/,
24 fbs::SetZero set_zero) {
25 DCHECK_GE(reinterpret_cast<const uint8_t *>(insertion_point), data());
26 DCHECK_LE(reinterpret_cast<const uint8_t *>(insertion_point),
27 data() + allocated_size_);
28 const size_t buffer_offset =
29 reinterpret_cast<const uint8_t *>(insertion_point) - data();
30 // TODO(austin): This has an extra memcpy in it that isn't strictly needed
31 // when we resize. Remove it if performance is a concern.
32 const size_t absolute_buffer_offset =
33 reinterpret_cast<const uint8_t *>(insertion_point) - buffer_.data();
34 const size_t previous_size = buffer_.size();
35
36 buffer_.resize(((allocated_size_ + bytes + kAlignment - 1) / kAlignment) *
37 kAlignment);
38
39 // Now, we've got space both before and after the block of data. Move the
40 // data after to the end, and the data before to the start.
41
42 const size_t new_space_after = buffer_.size() - previous_size;
43
44 // Move the rest of the data to be end aligned. If the buffer wasn't resized,
45 // this will be a nop.
46 memmove(buffer_.data() + absolute_buffer_offset + new_space_after,
47 buffer_.data() + absolute_buffer_offset,
48 previous_size - absolute_buffer_offset);
49
50 // Now, move the data at the front to be aligned too.
51 memmove(buffer_.data() + buffer_.size() - (allocated_size_ + bytes),
52 buffer_.data() + previous_size - allocated_size_,
53 allocated_size_ - (previous_size - absolute_buffer_offset));
54
55 if (set_zero == fbs::SetZero::kYes) {
56 memset(data() - bytes + buffer_offset, 0, bytes);
57 }
58 allocated_size_ += bytes;
59
60 return std::span<uint8_t>{data(), allocated_size_};
61}
62
63std::span<uint8_t> AlignedVectorAllocator::RemoveBytes(
64 std::span<uint8_t> remove_bytes) {
65 const ssize_t removal_index = remove_bytes.data() - buffer_.data();
66 const size_t old_start_index = buffer_.size() - allocated_size_;
67 CHECK_LE(static_cast<ssize_t>(old_start_index), removal_index);
68 CHECK_LE(removal_index, static_cast<ssize_t>(buffer_.size()));
69 CHECK_LE(removal_index + remove_bytes.size(), buffer_.size());
70 uint8_t *old_buffer_start = buffer_.data() + old_start_index;
71 memmove(old_buffer_start + remove_bytes.size(), old_buffer_start,
72 removal_index - old_start_index);
73 allocated_size_ -= remove_bytes.size();
74
75 return std::span<uint8_t>{data(), allocated_size_};
76}
77
78void AlignedVectorAllocator::Deallocate(std::span<uint8_t>) {
79 if (!released_) {
80 CHECK(!buffer_.empty())
81 << ": Called Deallocate() without a prior allocation.";
82 }
83 released_ = false;
84 buffer_.resize(0);
85}
86
87aos::SharedSpan AlignedVectorAllocator::Release() {
88 absl::Span<uint8_t> span{data(), allocated_size_};
89 std::shared_ptr<SharedSpanHolder> result = std::make_shared<SharedSpanHolder>(
90 std::move(buffer_), absl::Span<const uint8_t>());
91 result->span = span;
92 released_ = true;
93 return aos::SharedSpan(result, &(result->span));
94}
95
96} // namespace aos::fbs