1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <memory> 18 #include <optional> 19 20 #include "pw_allocator/allocator.h" 21 #include "pw_bytes/span.h" 22 #include "pw_multibuf/chunk.h" 23 24 namespace pw::multibuf { 25 26 /// A ``ChunkRegionTracker`` which stores its ``Chunk`` and region metadata 27 /// in a ``allocator::Allocator`` allocation alongside the data. 28 /// 29 /// This is useful when testing and when there is no need for asynchronous 30 /// allocation. 31 class HeaderChunkRegionTracker final : public ChunkRegionTracker { 32 public: 33 /// Allocates a new ``Chunk`` region of ``size`` bytes in ``alloc``. 34 /// 35 /// The underlyiing allocation will also store the 36 /// ``HeaderChunkRegionTracker`` itself. The allocated memory must not outlive 37 /// the provided allocator ``alloc``. 38 /// 39 /// Returns the newly-created ``OwnedChunk`` if successful. AllocateRegionAsChunk(allocator::Allocator & alloc,size_t size)40 static std::optional<OwnedChunk> AllocateRegionAsChunk( 41 allocator::Allocator& alloc, size_t size) { 42 HeaderChunkRegionTracker* tracker = AllocateRegion(alloc, size); 43 if (tracker == nullptr) { 44 return std::nullopt; 45 } 46 std::optional<OwnedChunk> chunk = tracker->CreateFirstChunk(); 47 if (!chunk.has_value()) { 48 tracker->Destroy(); 49 return std::nullopt; 50 } 51 return chunk; 52 } 53 54 /// Allocates a new region of ``size`` bytes in ``alloc``. 55 /// 56 /// The underlyiing allocation will also store the 57 /// ``HeaderChunkRegionTracker`` itself. The allocated memory must not outlive 58 /// the provided allocator ``alloc``. 59 /// 60 /// Returns a pointer to the newly-created ``HeaderChunkRegionTracker`` 61 /// or ``nullptr`` if the allocation failed. AllocateRegion(allocator::Allocator & alloc,size_t size)62 static HeaderChunkRegionTracker* AllocateRegion(allocator::Allocator& alloc, 63 size_t size) { 64 auto layout = 65 allocator::Layout::Of<HeaderChunkRegionTracker>().Extend(size); 66 void* ptr = alloc.Allocate(layout); 67 if (ptr == nullptr) { 68 return nullptr; 69 } 70 std::byte* data = 71 reinterpret_cast<std::byte*>(ptr) + sizeof(HeaderChunkRegionTracker); 72 return new (ptr) HeaderChunkRegionTracker(ByteSpan(data, size), alloc); 73 } 74 Region()75 ByteSpan Region() const final { return region_; } ~HeaderChunkRegionTracker()76 ~HeaderChunkRegionTracker() final {} 77 78 protected: Destroy()79 void Destroy() final { 80 std::byte* ptr = reinterpret_cast<std::byte*>(this); 81 auto alloc = alloc_; 82 std::destroy_at(this); 83 alloc->Deallocate(ptr); 84 } 85 AllocateChunkClass()86 void* AllocateChunkClass() final { 87 return alloc_->Allocate(allocator::Layout::Of<Chunk>()); 88 } 89 DeallocateChunkClass(void * ptr)90 void DeallocateChunkClass(void* ptr) final { alloc_->Deallocate(ptr); } 91 92 private: 93 ByteSpan region_; 94 allocator::Allocator* alloc_; 95 96 // NOTE: `region` must directly follow this `FakeChunkRegionTracker` 97 // in memory allocated by allocated by `alloc`. HeaderChunkRegionTracker(ByteSpan region,allocator::Allocator & alloc)98 HeaderChunkRegionTracker(ByteSpan region, allocator::Allocator& alloc) 99 : region_(region), alloc_(&alloc) {} 100 }; 101 102 } // namespace pw::multibuf 103