microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
scratch_allocator.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2// SPDX-License-Identifier: BSD-2-Clause
3
4#pragma once
5
6#include "detail/compat.hpp"
7#include "reloco.hpp"
8#include <cstddef>
9#include <cstdint>
10#include <new>
11#include <type_traits>
12#include <utility>
13
14// Alignment and bump allocation require audited pointer arithmetic inside this
15// checked allocation boundary.
16RELOCO_BEGIN_UNSAFE_BUFFER_USAGE
17
18namespace microfmt {
19
28public:
33 explicit constexpr scratch_allocator(
34 span<std::byte> buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
35 : m_buf(buffer) {}
36
41 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
42 // checked here.
43 RELOCO_BEGIN_UNSAFE_CODE
44 explicit scratch_allocator(span<char> buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
45 : m_buf(reinterpret_cast<std::byte *>(buffer.data()), buffer.size()) {}
47
54 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
55 // checked here.
56 RELOCO_BEGIN_UNSAFE_CODE
57 template <typename T>
58 [[nodiscard]] RELOCO_ASSUME_ALIGNED(alignof(T)) T *allocate(size_t count = 1) noexcept RELOCO_LIFETIMEBOUND {
59 if (count == 0 || count > available() / sizeof(T)) {
60 return nullptr;
61 }
62
63 const size_t size = count * sizeof(T);
64 std::byte *current_ptr = m_buf.data() + m_pos;
65 const uintptr_t addr = reinterpret_cast<uintptr_t>(current_ptr);
66 const size_t alignment_offset = static_cast<size_t>((alignof(T) - (addr % alignof(T))) % alignof(T));
67
68 if (alignment_offset > available() || size > available() - alignment_offset) {
69 return nullptr;
70 }
71
72 m_pos += alignment_offset + size;
73 return static_cast<T *>(static_cast<void *>(current_ptr + alignment_offset));
74 }
76
88 template <typename T, typename... Args>
89 [[nodiscard]] RELOCO_ASSUME_ALIGNED(alignof(T)) T *create(Args &&...args) noexcept RELOCO_LIFETIMEBOUND {
90 static_assert(std::is_trivially_destructible_v<T>, "scratch_allocator only permits trivially destructible types");
91
92 T *storage = allocate<T>();
93 if (storage == nullptr) {
94 return nullptr;
95 }
96 return ::new (static_cast<void *>(storage)) T(std::forward<Args>(args)...);
97 }
98
106 [[nodiscard]] constexpr scratch_allocator rest() const noexcept RELOCO_LIFETIMEBOUND {
107 return scratch_allocator{remaining_span()};
108 }
109
113 [[nodiscard]] constexpr span<std::byte> remaining_span() const noexcept RELOCO_LIFETIMEBOUND {
114 if (m_pos >= m_buf.size()) {
115 return {};
116 }
117 return {m_buf.data() + m_pos, m_buf.size() - m_pos};
118 }
119
123 constexpr void reset() noexcept { m_pos = 0; }
124
125 [[nodiscard]] constexpr size_t size() const noexcept { return m_pos; }
126 [[nodiscard]] constexpr size_t capacity() const noexcept { return m_buf.size(); }
127 [[nodiscard]] constexpr size_t available() const noexcept {
128 return (m_pos < m_buf.size()) ? (m_buf.size() - m_pos) : 0;
129 }
130
131private:
132 span<std::byte> m_buf;
133 size_t m_pos{0};
134};
135
136} // namespace microfmt
137
138RELOCO_END_UNSAFE_BUFFER_USAGE
Zero-allocation bump allocator for scratch buffers.
Definition scratch_allocator.hpp:27
constexpr scratch_allocator(span< std::byte > buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a scratch allocator over a provided byte span.
Definition scratch_allocator.hpp:33
RELOCO_END_UNSAFE_CODE RELOCO_BEGIN_UNSAFE_CODE RELOCO_ASSUME_ALIGNED(alignof(T)) T *allocate(size_t count
Reserves aligned storage for objects without constructing them.
constexpr size_t size() const noexcept
Definition scratch_allocator.hpp:125
constexpr void reset() noexcept
Resets the allocator back to the beginning of the buffer.
Definition scratch_allocator.hpp:123
constexpr span< std::byte > remaining_span() const noexcept RELOCO_LIFETIMEBOUND
Returns a span over the remaining unallocated space.
Definition scratch_allocator.hpp:113
RELOCO_END_UNSAFE_CODE RELOCO_BEGIN_UNSAFE_CODE typename Args RELOCO_ASSUME_ALIGNED(alignof(T)) T *create(Args &&...args) noexcept RELOCO_LIFETIMEBOUND
Definition scratch_allocator.hpp:89
constexpr scratch_allocator rest() const noexcept RELOCO_LIFETIMEBOUND
Creates and returns a new scratch_allocator owning the remaining unallocated space.
Definition scratch_allocator.hpp:106
constexpr size_t available() const noexcept
Definition scratch_allocator.hpp:127
constexpr size_t capacity() const noexcept
Definition scratch_allocator.hpp:126
RELOCO_BEGIN_UNSAFE_CODE scratch_allocator(span< char > buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a scratch allocator over a provided character span.
Definition scratch_allocator.hpp:44
Compiler, language-version, and optional-library feature detection.
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
Single re-export of every `reloco` type and annotation microfmt builds on.