blob: 569c6938870990104738f433437b0e5b2fc2b11e [file] [log] [blame]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include <memory>
#include <optional>
#include "pw_allocator/allocator.h"
#include "pw_bytes/span.h"
#include "pw_multibuf/chunk.h"
namespace pw::multibuf {
/// A ``ChunkRegionTracker`` which stores its ``Chunk`` and region metadata
/// in a ``allocator::Allocator`` allocation alongside the data.
///
/// This is useful when testing and when there is no need for asynchronous
/// allocation.
class HeaderChunkRegionTracker final : public ChunkRegionTracker {
public:
/// Allocates a new ``Chunk`` region of ``size`` bytes in ``alloc``.
///
/// The underlyiing allocation will also store the
/// ``HeaderChunkRegionTracker`` itself. The allocated memory must not outlive
/// the provided allocator ``alloc``.
///
/// Returns the newly-created ``OwnedChunk`` if successful.
static std::optional<OwnedChunk> AllocateRegionAsChunk(
allocator::Allocator& alloc, size_t size) {
HeaderChunkRegionTracker* tracker = AllocateRegion(alloc, size);
if (tracker == nullptr) {
return std::nullopt;
}
std::optional<OwnedChunk> chunk = tracker->CreateFirstChunk();
if (!chunk.has_value()) {
tracker->Destroy();
return std::nullopt;
}
return chunk;
}
/// Allocates a new region of ``size`` bytes in ``alloc``.
///
/// The underlyiing allocation will also store the
/// ``HeaderChunkRegionTracker`` itself. The allocated memory must not outlive
/// the provided allocator ``alloc``.
///
/// Returns a pointer to the newly-created ``HeaderChunkRegionTracker``
/// or ``nullptr`` if the allocation failed.
static HeaderChunkRegionTracker* AllocateRegion(allocator::Allocator& alloc,
size_t size) {
auto layout =
allocator::Layout::Of<HeaderChunkRegionTracker>().Extend(size);
void* ptr = alloc.Allocate(layout);
if (ptr == nullptr) {
return nullptr;
}
std::byte* data =
reinterpret_cast<std::byte*>(ptr) + sizeof(HeaderChunkRegionTracker);
return new (ptr) HeaderChunkRegionTracker(ByteSpan(data, size), alloc);
}
ByteSpan Region() const final { return region_; }
~HeaderChunkRegionTracker() final {}
protected:
void Destroy() final {
std::byte* ptr = reinterpret_cast<std::byte*>(this);
auto alloc = alloc_;
std::destroy_at(this);
alloc->Deallocate(ptr);
}
void* AllocateChunkClass() final {
return alloc_->Allocate(allocator::Layout::Of<Chunk>());
}
void DeallocateChunkClass(void* ptr) final { alloc_->Deallocate(ptr); }
private:
ByteSpan region_;
allocator::Allocator* alloc_;
// NOTE: `region` must directly follow this `FakeChunkRegionTracker`
// in memory allocated by allocated by `alloc`.
HeaderChunkRegionTracker(ByteSpan region, allocator::Allocator& alloc)
: region_(region), alloc_(&alloc) {}
};
} // namespace pw::multibuf