cpp-msgpack-light 0.3.0
A light library to serialize MessagePack.
Loading...
Searching...
No Matches
monotonic_allocator.h
Go to the documentation of this file.
1/*
2 * Copyright 2024 MusicScience37 (Kenta Kabashima)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
20#pragma once
21
22#include <algorithm>
23#include <cassert>
24#include <cstdlib>
25#include <memory>
26#include <new>
27#include <utility>
28
29namespace msgpack_light {
30
36public:
38 static constexpr std::size_t initial_buffer_size = 1024U;
39
41 static constexpr std::size_t max_allocation_from_buffer = 512U;
42
47
53 monotonic_allocator(const monotonic_allocator& /*other*/) noexcept
55
62 : current_buffer_size_(other.current_buffer_size_),
63 current_buffer_(std::exchange(other.current_buffer_, nullptr)),
65 std::exchange(other.next_allocation_point_, nullptr)),
66 remaining_buffer_(other.remaining_buffer_),
68 std::exchange(other.last_direct_allocation_, nullptr)) {}
69
78 const monotonic_allocator& /*other*/) noexcept {
79 return *this;
80 }
81
89 swap(other);
90 return *this;
91 }
92
98 void swap(monotonic_allocator& other) noexcept {
99 std::swap(current_buffer_size_, other.current_buffer_size_);
100 std::swap(current_buffer_, other.current_buffer_);
101 std::swap(next_allocation_point_, other.next_allocation_point_);
102 std::swap(remaining_buffer_, other.remaining_buffer_);
103 std::swap(last_direct_allocation_, other.last_direct_allocation_);
104 }
105
110 while (current_buffer_ != nullptr) {
111 void** head = static_cast<void**>(current_buffer_);
112 void* prev_buffer = *head;
113 std::free(current_buffer_);
114 current_buffer_ = prev_buffer;
115 }
116 while (last_direct_allocation_ != nullptr) {
117 void** head = static_cast<void**>(last_direct_allocation_);
118 void* prev_buffer = *head;
119 std::free(last_direct_allocation_);
120 last_direct_allocation_ = prev_buffer;
121 }
122 }
123
131 [[nodiscard]] void* allocate(std::size_t size, std::size_t alignment) {
132 if (size == 0U) {
133 // Any pointer other than nullptr can be returned.
135 }
136 if (size > max_allocation_from_buffer) {
137 return allocate_directly(size, alignment);
138 }
139 void* result = try_allocate_from_buffer(size, alignment);
140 if (result != nullptr) {
141 return result;
142 }
144 return try_allocate_from_buffer(size, alignment);
145 }
146
154 void deallocate(void* ptr) noexcept { // NOLINT
155 // No operation in this class.
156 (void)ptr;
157 }
158
159private:
166 [[nodiscard]] static void* allocate_buffer(std::size_t size) {
167 void* ptr = std::malloc(size);
168 if (ptr == nullptr) {
169 throw std::bad_alloc();
170 }
171 return ptr;
172 }
173
178 constexpr std::size_t next_buffer_size_rate = 2U;
179 constexpr std::size_t max_buffer_size = static_cast<std::size_t>(1)
180 << 20U;
181 if (current_buffer_size_ < max_buffer_size) {
182 current_buffer_size_ *= next_buffer_size_rate;
183 }
185 }
186
194 void* prev_buffer = current_buffer_;
196 void** head = static_cast<void**>(current_buffer_);
197 *head = prev_buffer;
198 next_allocation_point_ = head + 1;
199 remaining_buffer_ = current_buffer_size_ - sizeof(void*);
200 }
201
209 [[nodiscard]] void* try_allocate_from_buffer(
210 std::size_t size, std::size_t alignment) {
211 if (std::align(alignment, size, next_allocation_point_,
212 remaining_buffer_) == nullptr) {
213 return nullptr;
214 }
215 void* result = next_allocation_point_;
217 static_cast<char*>(next_allocation_point_) + size;
218 remaining_buffer_ -= size;
219 return result;
220 }
221
229 [[nodiscard]] void* allocate_directly(
230 std::size_t size, std::size_t alignment) {
231 void* prev_allocation = last_direct_allocation_;
232 std::size_t size_with_header_and_padding_space =
233 size + std::max(sizeof(void*), alignment);
235 allocate_buffer(size_with_header_and_padding_space);
236 void** header = static_cast<void**>(last_direct_allocation_);
237 *header = prev_allocation;
238 void* ptr_after_header = header + 1;
239 void* result = std::align(alignment, size, ptr_after_header,
240 size_with_header_and_padding_space);
241 assert(result != nullptr);
242 (void)result; // prevent error in release build
243 return result;
244 }
245
248
250 void* current_buffer_{nullptr};
251
254
257
260};
261
269 msgpack_light::monotonic_allocator& instance2) noexcept {
270 instance1.swap(instance2);
271}
272
273} // namespace msgpack_light
Class of an allocator which releases memory only when the allocator is destroyed.
static constexpr std::size_t initial_buffer_size
Size of the initial buffer.
std::size_t remaining_buffer_
Remaining size of the current buffer.
monotonic_allocator(monotonic_allocator &&other) noexcept
Move constructor.
void * last_direct_allocation_
Last pointer to the memory allocated directly using malloc function.
void swap(monotonic_allocator &other) noexcept
Swap with another instance.
std::size_t current_buffer_size_
Size of the current buffer.
void * allocate_directly(std::size_t size, std::size_t alignment)
Allocate memory directly using malloc function.
void deallocate(void *ptr) noexcept
Deallocate memory.
static constexpr std::size_t max_allocation_from_buffer
Maximum size of memory allocated from buffer.
static void * allocate_buffer(std::size_t size)
Allocate a buffer.
monotonic_allocator & operator=(monotonic_allocator &&other) noexcept
Move assignment operator.
monotonic_allocator & operator=(const monotonic_allocator &) noexcept
Copy assignment operator.
monotonic_allocator(const monotonic_allocator &) noexcept
Copy constructor.
void change_buffer()
Prepare the next buffer.
void * next_allocation_point_
Pointer to the byte from which the next allocation starts.
void * allocate(std::size_t size, std::size_t alignment)
Allocate memory.
void * try_allocate_from_buffer(std::size_t size, std::size_t alignment)
Try to allocate from the current buffer.
Namespace of this project.
Definition binary.h:33
void swap(msgpack_light::monotonic_allocator &instance1, msgpack_light::monotonic_allocator &instance2) noexcept
Swap two instances.
void swap(msgpack_light::details::basic_binary_buffer &instance1, msgpack_light::details::basic_binary_buffer &instance2) noexcept
Implementation of std::swap for msgpack_light::details::basic_binary_buffer.