1 // Copyright 2024 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 <array> 17 #include <atomic> 18 #include <cstring> 19 #include <optional> 20 21 #include "pw_assert/assert.h" 22 #include "pw_bytes/span.h" 23 #include "pw_multibuf/chunk.h" 24 25 namespace pw::multibuf { 26 27 /// A `ChunkRegionTracker` that uses inline memory to create a single `Chunk` 28 /// with the only caveat that the provided `Chunk` cannot be split. All attempts 29 /// will result in `std::nullopt`. 30 class SingleChunkRegionTracker : public ChunkRegionTracker { 31 public: 32 /// Constructs a region tracker with a single `Chunk` that maps to `region`, 33 /// which must outlive this tracker and any `OwnedChunk` it creates. SingleChunkRegionTracker(ByteSpan region)34 explicit SingleChunkRegionTracker(ByteSpan region) : region_(region) {} ~SingleChunkRegionTracker()35 ~SingleChunkRegionTracker() override { Destroy(); } 36 37 /// Gets a `Chunk` of a given size, which must be less than or equal to the 38 /// provided region. 39 /// 40 /// Returns: 41 /// An `OwnedChunk` if the `Chunk` is free, otherwise `std::nullopt`, in 42 /// which case `GetChunk()` can be called again. GetChunk(size_t size)43 std::optional<OwnedChunk> GetChunk(size_t size) { 44 PW_DASSERT(size <= region_.size()); 45 // Since this is a single `Chunk` region, re-create the first `Chunk` is 46 // allowed if freed. 47 std::optional<OwnedChunk> chunk = CreateFirstChunk(); 48 if (chunk.has_value() && size < region_.size()) { 49 (*chunk)->Truncate(size); 50 } 51 return chunk; 52 } 53 Destroy()54 void Destroy() final { 55 // Nothing to release here. 56 PW_ASSERT(!chunk_in_use_); 57 } 58 Region()59 ByteSpan Region() const final { return region_; } 60 AllocateChunkClass()61 void* AllocateChunkClass() final { 62 bool in_use = false; 63 if (!chunk_in_use_.compare_exchange_strong(in_use, true)) { 64 return nullptr; 65 } 66 return &chunk_storage_; 67 } 68 DeallocateChunkClass(void * chunk)69 void DeallocateChunkClass(void* chunk) final { 70 PW_ASSERT(chunk == chunk_storage_.data()); 71 // Mark the `Chunk` as not in use and zero-out the region and chunk storage. 72 std::memset(chunk_storage_.data(), 0, chunk_storage_.size()); 73 std::memset(region_.data(), 0, region_.size()); 74 chunk_in_use_ = false; 75 } 76 77 private: 78 ByteSpan region_; 79 std::atomic<bool> chunk_in_use_ = false; 80 alignas(Chunk) std::array<std::byte, sizeof(Chunk)> chunk_storage_; 81 }; 82 83 } // namespace pw::multibuf 84