Austin Schuh | 3c9f92c | 2024-04-30 17:56:42 -0700 | [diff] [blame^] | 1 | #include "aos/flatbuffers/aligned_allocator.h" |
| 2 | |
| 3 | namespace aos::fbs { |
| 4 | |
| 5 | AlignedVectorAllocator::~AlignedVectorAllocator() { |
| 6 | CHECK(buffer_.empty()) |
| 7 | << ": Must deallocate before destroying the AlignedVectorAllocator."; |
| 8 | } |
| 9 | |
| 10 | std::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 | |
| 22 | std::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 | |
| 63 | std::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 | |
| 78 | void 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 | |
| 87 | aos::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 |