Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/boostorg/json
9 : //
10 :
11 : #ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
12 : #define BOOST_JSON_MONOTONIC_RESOURCE_HPP
13 :
14 : #include <boost/container/pmr/memory_resource.hpp>
15 : #include <boost/json/detail/config.hpp>
16 : #include <boost/json/memory_resource.hpp>
17 : #include <boost/json/storage_ptr.hpp>
18 : #include <cstddef>
19 : #include <utility>
20 :
21 : namespace boost {
22 : namespace json {
23 :
24 : #ifdef _MSC_VER
25 : #pragma warning(push)
26 : #pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
27 : #pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
28 : #endif
29 :
30 : //----------------------------------------------------------
31 :
32 : /** A dynamically allocating resource with a trivial deallocate
33 :
34 : This memory resource is a special-purpose resource
35 : that releases allocated memory only when the resource
36 : is destroyed (or when @ref release is called).
37 : It has a trivial deallocate function; that is, the
38 : metafunction @ref is_deallocate_trivial returns `true`.
39 : \n
40 : The resource can be constructed with an initial buffer.
41 : If there is no initial buffer, or if the buffer is
42 : exhausted, subsequent dynamic allocations are made from
43 : the system heap. The size of buffers obtained in this
44 : fashion follow a geometric progression.
45 : \n
46 : The purpose of this resource is to optimize the use
47 : case for performing many allocations, followed by
48 : deallocating everything at once. This is precisely the
49 : pattern of memory allocation which occurs when parsing:
50 : allocation is performed for each parsed element, and
51 : when the the resulting @ref value is no longer needed,
52 : the entire structure is destroyed. However, it is not
53 : suited for modifying the value after parsing is
54 : complete; reallocations waste memory, since the
55 : older buffer is not reclaimed until the resource
56 : is destroyed.
57 :
58 : @par Example
59 :
60 : This parses a JSON text into a value which uses a local
61 : stack buffer, then prints the result.
62 :
63 : @code
64 :
65 : unsigned char buf[ 4000 ];
66 : monotonic_resource mr( buf );
67 :
68 : // Parse the string, using our memory resource
69 : auto const jv = parse( "[1,2,3]", &mr );
70 :
71 : // Print the JSON
72 : std::cout << jv;
73 :
74 : @endcode
75 :
76 : @note The total amount of memory dynamically
77 : allocated is monotonically increasing; That is,
78 : it never decreases.
79 :
80 : @par Thread Safety
81 : Members of the same instance may not be
82 : called concurrently.
83 :
84 : @see
85 : https://en.wikipedia.org/wiki/Region-based_memory_management
86 : */
87 : class
88 : BOOST_JSON_DECL
89 : BOOST_SYMBOL_VISIBLE
90 : monotonic_resource final
91 : : public container::pmr::memory_resource
92 : {
93 : struct block;
94 : struct block_base
95 : {
96 : void* p;
97 : std::size_t avail;
98 : std::size_t size;
99 : block_base* next;
100 : };
101 :
102 : block_base buffer_;
103 : block_base* head_ = &buffer_;
104 : std::size_t next_size_ = 1024;
105 : storage_ptr upstream_;
106 :
107 : static constexpr std::size_t min_size_ = 1024;
108 : inline static constexpr std::size_t max_size();
109 : inline static std::size_t round_pow2(
110 : std::size_t n) noexcept;
111 : inline static std::size_t next_pow2(
112 : std::size_t n) noexcept;
113 :
114 : public:
115 : /// Copy constructor (deleted)
116 : monotonic_resource(
117 : monotonic_resource const&) = delete;
118 :
119 : /// Copy assignment (deleted)
120 : monotonic_resource& operator=(
121 : monotonic_resource const&) = delete;
122 :
123 : /** Destructor
124 :
125 : Deallocates all the memory owned by this resource.
126 :
127 : @par Effects
128 : @code
129 : this->release();
130 : @endcode
131 :
132 : @par Complexity
133 : Linear in the number of deallocations performed.
134 :
135 : @par Exception Safety
136 : No-throw guarantee.
137 : */
138 : ~monotonic_resource();
139 :
140 : /** Constructor
141 :
142 : This constructs the resource and indicates
143 : that the first internal dynamic allocation
144 : shall be at least `initial_size` bytes.
145 : \n
146 : This constructor is guaranteed not to perform
147 : any dynamic allocations.
148 :
149 : @par Complexity
150 : Constant.
151 :
152 : @par Exception Safety
153 : No-throw guarantee.
154 :
155 : @param initial_size The size of the first
156 : internal dynamic allocation. If this is lower
157 : than the implementation-defined lower limit, then
158 : the lower limit is used instead.
159 :
160 : @param upstream An optional upstream memory resource
161 : to use for performing internal dynamic allocations.
162 : If this parameter is omitted, the default resource
163 : is used.
164 : */
165 : explicit
166 : monotonic_resource(
167 : std::size_t initial_size = 1024,
168 : storage_ptr upstream = {}) noexcept;
169 :
170 : /** Constructor
171 :
172 : This constructs the resource and indicates that
173 : subsequent allocations should use the specified
174 : caller-owned buffer.
175 : When this buffer is exhausted, dynamic allocations
176 : from the upstream resource are made.
177 : \n
178 : This constructor is guaranteed not to perform
179 : any dynamic allocations.
180 :
181 : @par Complexity
182 : Constant.
183 :
184 : @par Exception Safety
185 : No-throw guarantee.
186 :
187 : @param buffer The buffer to use.
188 : Ownership is not transferred; the caller is
189 : responsible for ensuring that the lifetime of
190 : the buffer extends until the resource is destroyed.
191 :
192 : @param size The number of valid bytes pointed
193 : to by `buffer`.
194 :
195 : @param upstream An optional upstream memory resource
196 : to use for performing internal dynamic allocations.
197 : If this parameter is omitted, the default resource
198 : is used.
199 : */
200 : /** @{ */
201 : monotonic_resource(
202 : unsigned char* buffer,
203 : std::size_t size,
204 : storage_ptr upstream = {}) noexcept;
205 :
206 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
207 : monotonic_resource(
208 : std::byte* buffer,
209 : std::size_t size,
210 : storage_ptr upstream) noexcept
211 : : monotonic_resource(reinterpret_cast<
212 : unsigned char*>(buffer), size,
213 : std::move(upstream))
214 : {
215 : }
216 : #endif
217 : /** @} */
218 :
219 : /** Constructor
220 :
221 : This constructs the resource and indicates that
222 : subsequent allocations should use the specified
223 : caller-owned buffer.
224 : When this buffer is exhausted, dynamic allocations
225 : from the upstream resource are made.
226 : \n
227 : This constructor is guaranteed not to perform
228 : any dynamic allocations.
229 :
230 : @par Complexity
231 : Constant.
232 :
233 : @par Exception Safety
234 : No-throw guarantee.
235 :
236 : @param buffer The buffer to use.
237 : Ownership is not transferred; the caller is
238 : responsible for ensuring that the lifetime of
239 : the buffer extends until the resource is destroyed.
240 :
241 : @param upstream An optional upstream memory resource
242 : to use for performing internal dynamic allocations.
243 : If this parameter is omitted, the default resource
244 : is used.
245 : */
246 : /** @{ */
247 : template<std::size_t N>
248 : explicit
249 2 : monotonic_resource(
250 : unsigned char(&buffer)[N],
251 : storage_ptr upstream = {}) noexcept
252 : : monotonic_resource(&buffer[0],
253 2 : N, std::move(upstream))
254 : {
255 2 : }
256 :
257 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
258 : template<std::size_t N>
259 : explicit
260 : monotonic_resource(
261 : std::byte(&buffer)[N],
262 : storage_ptr upstream = {}) noexcept
263 : : monotonic_resource(&buffer[0],
264 : N, std::move(upstream))
265 : {
266 : }
267 : #endif
268 : /** @} */
269 :
270 : #ifndef BOOST_JSON_DOCS
271 : // Safety net for accidental buffer overflows
272 : template<std::size_t N>
273 : monotonic_resource(
274 : unsigned char(&buffer)[N],
275 : std::size_t n,
276 : storage_ptr upstream = {}) noexcept
277 : : monotonic_resource(&buffer[0],
278 : n, std::move(upstream))
279 : {
280 : // If this goes off, check your parameters
281 : // closely, chances are you passed an array
282 : // thinking it was a pointer.
283 : BOOST_ASSERT(n <= N);
284 : }
285 :
286 : #ifdef __cpp_lib_byte
287 : // Safety net for accidental buffer overflows
288 : template<std::size_t N>
289 : monotonic_resource(
290 : std::byte(&buffer)[N],
291 : std::size_t n,
292 : storage_ptr upstream = {}) noexcept
293 : : monotonic_resource(&buffer[0],
294 : n, std::move(upstream))
295 : {
296 : // If this goes off, check your parameters
297 : // closely, chances are you passed an array
298 : // thinking it was a pointer.
299 : BOOST_ASSERT(n <= N);
300 : }
301 : #endif
302 : #endif
303 :
304 : /** Release all allocated memory.
305 :
306 : This function deallocates all allocated memory.
307 : If an initial buffer was provided upon construction,
308 : then all of the bytes will be available again for
309 : allocation. Allocated memory is deallocated even
310 : if deallocate has not been called for some of
311 : the allocated blocks.
312 :
313 : @par Complexity
314 : Linear in the number of deallocations performed.
315 :
316 : @par Exception Safety
317 : No-throw guarantee.
318 : */
319 : void
320 : release() noexcept;
321 :
322 : protected:
323 : #ifndef BOOST_JSON_DOCS
324 : void*
325 : do_allocate(
326 : std::size_t n,
327 : std::size_t align) override;
328 :
329 : void
330 : do_deallocate(
331 : void* p,
332 : std::size_t n,
333 : std::size_t align) override;
334 :
335 : bool
336 : do_is_equal(
337 : memory_resource const& mr) const noexcept override;
338 : #endif
339 : };
340 :
341 : #ifdef _MSC_VER
342 : #pragma warning(pop)
343 : #endif
344 :
345 : template<>
346 : struct is_deallocate_trivial<
347 : monotonic_resource>
348 : {
349 : static constexpr bool value = true;
350 : };
351 :
352 : } // namespace json
353 : } // namespace boost
354 :
355 : #endif
|