]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | /* | |
19 | * Copyright (C) 2014 Cloudius Systems, Ltd. | |
20 | */ | |
21 | ||
22 | #pragma once | |
23 | ||
24 | #include <seastar/core/deleter.hh> | |
25 | #include <seastar/util/eclipse.hh> | |
f67539c2 | 26 | #include <seastar/util/std-compat.hh> |
11fdf7f2 TL |
27 | #include <malloc.h> |
28 | #include <algorithm> | |
9f95a23c | 29 | #include <cstddef> |
11fdf7f2 TL |
30 | |
31 | namespace seastar { | |
32 | ||
33 | /// \addtogroup memory-module | |
34 | /// @{ | |
35 | ||
36 | /// Temporary, self-managed byte buffer. | |
37 | /// | |
38 | /// A \c temporary_buffer is similar to an \c std::string or a \c std::unique_ptr<char[]>, | |
39 | /// but provides more flexible memory management. A \c temporary_buffer can own the memory | |
40 | /// it points to, or it can be shared with another \c temporary_buffer, or point at a substring | |
41 | /// of a buffer. It uses a \ref deleter to manage its memory. | |
42 | /// | |
43 | /// A \c temporary_buffer should not be held indefinitely. It can be held while a request | |
44 | /// is processed, or for a similar duration, but not longer, as it can tie up more memory | |
45 | /// that its size indicates. | |
46 | /// | |
47 | /// A buffer can be shared: two \c temporary_buffer objects will point to the same data, | |
48 | /// or a subset of it. See the \ref temporary_buffer::share() method. | |
49 | /// | |
50 | /// Unless you created a \c temporary_buffer yourself, do not modify its contents, as they | |
51 | /// may be shared with another user that does not expect the data to change. | |
52 | /// | |
53 | /// Use cases for a \c temporary_buffer include: | |
54 | /// - passing a substring of a tcp packet for the user to consume (zero-copy | |
55 | /// tcp input) | |
56 | /// - passing a refcounted blob held in memory to tcp, ensuring that when the TCP ACK | |
57 | /// is received, the blob is released (by decrementing its reference count) (zero-copy | |
58 | /// tcp output) | |
59 | /// | |
60 | /// \tparam CharType underlying character type (must be a variant of \c char). | |
61 | template <typename CharType> | |
62 | class temporary_buffer { | |
63 | static_assert(sizeof(CharType) == 1, "must buffer stream of bytes"); | |
64 | CharType* _buffer; | |
65 | size_t _size; | |
66 | deleter _deleter; | |
67 | public: | |
68 | /// Creates a \c temporary_buffer of a specified size. The buffer is not shared | |
69 | /// with anyone, and is not initialized. | |
70 | /// | |
71 | /// \param size buffer size, in bytes | |
72 | explicit temporary_buffer(size_t size) | |
73 | : _buffer(static_cast<CharType*>(malloc(size * sizeof(CharType)))), _size(size) | |
74 | , _deleter(make_free_deleter(_buffer)) { | |
75 | if (size && !_buffer) { | |
76 | throw std::bad_alloc(); | |
77 | } | |
78 | } | |
79 | //explicit temporary_buffer(CharType* borrow, size_t size) : _buffer(borrow), _size(size) {} | |
80 | /// Creates an empty \c temporary_buffer that does not point at anything. | |
f67539c2 | 81 | temporary_buffer() noexcept |
11fdf7f2 TL |
82 | : _buffer(nullptr) |
83 | , _size(0) {} | |
84 | temporary_buffer(const temporary_buffer&) = delete; | |
9f95a23c | 85 | |
11fdf7f2 TL |
86 | /// Moves a \c temporary_buffer. |
87 | temporary_buffer(temporary_buffer&& x) noexcept : _buffer(x._buffer), _size(x._size), _deleter(std::move(x._deleter)) { | |
88 | x._buffer = nullptr; | |
89 | x._size = 0; | |
90 | } | |
9f95a23c | 91 | |
11fdf7f2 TL |
92 | /// Creates a \c temporary_buffer with a specific deleter. |
93 | /// | |
94 | /// \param buf beginning of the buffer held by this \c temporary_buffer | |
95 | /// \param size size of the buffer | |
96 | /// \param d deleter controlling destruction of the buffer. The deleter | |
97 | /// will be destroyed when there are no longer any users for the buffer. | |
20effc67 | 98 | temporary_buffer(CharType* buf, size_t size, deleter d) noexcept |
11fdf7f2 TL |
99 | : _buffer(buf), _size(size), _deleter(std::move(d)) {} |
100 | /// Creates a `temporary_buffer` containing a copy of the provided data | |
101 | /// | |
102 | /// \param src data buffer to be copied | |
103 | /// \param size size of data buffer in `src` | |
104 | temporary_buffer(const CharType* src, size_t size) : temporary_buffer(size) { | |
105 | std::copy_n(src, size, _buffer); | |
106 | } | |
107 | void operator=(const temporary_buffer&) = delete; | |
108 | /// Moves a \c temporary_buffer. | |
109 | temporary_buffer& operator=(temporary_buffer&& x) noexcept { | |
110 | if (this != &x) { | |
111 | _buffer = x._buffer; | |
112 | _size = x._size; | |
113 | _deleter = std::move(x._deleter); | |
114 | x._buffer = nullptr; | |
115 | x._size = 0; | |
116 | } | |
117 | return *this; | |
118 | } | |
119 | /// Gets a pointer to the beginning of the buffer. | |
20effc67 | 120 | const CharType* get() const noexcept { return _buffer; } |
11fdf7f2 TL |
121 | /// Gets a writable pointer to the beginning of the buffer. Use only |
122 | /// when you are certain no user expects the buffer data not to change. | |
20effc67 | 123 | CharType* get_write() noexcept { return _buffer; } |
11fdf7f2 | 124 | /// Gets the buffer size. |
20effc67 | 125 | size_t size() const noexcept { return _size; } |
11fdf7f2 | 126 | /// Gets a pointer to the beginning of the buffer. |
20effc67 | 127 | const CharType* begin() const noexcept { return _buffer; } |
11fdf7f2 | 128 | /// Gets a pointer to the end of the buffer. |
20effc67 | 129 | const CharType* end() const noexcept { return _buffer + _size; } |
11fdf7f2 TL |
130 | /// Returns the buffer, but with a reduced size. The original |
131 | /// buffer is consumed by this call and can no longer be used. | |
132 | /// | |
133 | /// \param size New size; must be smaller than current size. | |
134 | /// \return the same buffer, with a prefix removed. | |
20effc67 | 135 | temporary_buffer prefix(size_t size) && noexcept { |
11fdf7f2 TL |
136 | auto ret = std::move(*this); |
137 | ret._size = size; | |
138 | return ret; | |
139 | } | |
140 | /// Reads a character from a specific position in the buffer. | |
141 | /// | |
142 | /// \param pos position to read character from; must be less than size. | |
20effc67 | 143 | CharType operator[](size_t pos) const noexcept { |
11fdf7f2 TL |
144 | return _buffer[pos]; |
145 | } | |
146 | /// Checks whether the buffer is empty. | |
20effc67 | 147 | bool empty() const noexcept { return !size(); } |
11fdf7f2 | 148 | /// Checks whether the buffer is not empty. |
20effc67 | 149 | explicit operator bool() const noexcept { return size(); } |
11fdf7f2 TL |
150 | /// Create a new \c temporary_buffer object referring to the same |
151 | /// underlying data. The underlying \ref deleter will not be destroyed | |
152 | /// until both the original and the clone have been destroyed. | |
153 | /// | |
154 | /// \return a clone of the buffer object. | |
155 | temporary_buffer share() { | |
156 | return temporary_buffer(_buffer, _size, _deleter.share()); | |
157 | } | |
158 | /// Create a new \c temporary_buffer object referring to a substring of the | |
159 | /// same underlying data. The underlying \ref deleter will not be destroyed | |
160 | /// until both the original and the clone have been destroyed. | |
161 | /// | |
162 | /// \param pos Position of the first character to share. | |
163 | /// \param len Length of substring to share. | |
164 | /// \return a clone of the buffer object, referring to a substring. | |
165 | temporary_buffer share(size_t pos, size_t len) { | |
166 | auto ret = share(); | |
167 | ret._buffer += pos; | |
168 | ret._size = len; | |
169 | return ret; | |
170 | } | |
171 | /// Clone the current \c temporary_buffer object into a new one. | |
172 | /// This creates a temporary buffer with the same length and data but not | |
173 | /// pointing to the memory of the original object. | |
174 | temporary_buffer clone() const { | |
175 | return {_buffer, _size}; | |
176 | } | |
177 | /// Remove a prefix from the buffer. The underlying data | |
178 | /// is not modified. | |
179 | /// | |
180 | /// \param pos Position of first character to retain. | |
20effc67 | 181 | void trim_front(size_t pos) noexcept { |
11fdf7f2 TL |
182 | _buffer += pos; |
183 | _size -= pos; | |
184 | } | |
185 | /// Remove a suffix from the buffer. The underlying data | |
186 | /// is not modified. | |
187 | /// | |
188 | /// \param pos Position of first character to drop. | |
20effc67 | 189 | void trim(size_t pos) noexcept { |
11fdf7f2 TL |
190 | _size = pos; |
191 | } | |
192 | /// Stops automatic memory management. When the \c temporary_buffer | |
193 | /// object is destroyed, the underlying \ref deleter will not be called. | |
194 | /// Instead, it is the caller's responsibility to destroy the deleter object | |
195 | /// when the data is no longer needed. | |
196 | /// | |
197 | /// \return \ref deleter object managing the data's lifetime. | |
20effc67 | 198 | deleter release() noexcept { |
11fdf7f2 TL |
199 | return std::move(_deleter); |
200 | } | |
201 | /// Creates a \c temporary_buffer object with a specified size, with | |
202 | /// memory aligned to a specific boundary. | |
203 | /// | |
9f95a23c TL |
204 | /// \param alignment Required alignment; must be a power of two and a multiple of sizeof(void *). |
205 | /// \param size Required size; must be a multiple of alignment. | |
11fdf7f2 TL |
206 | /// \return a new \c temporary_buffer object. |
207 | static temporary_buffer aligned(size_t alignment, size_t size) { | |
208 | void *ptr = nullptr; | |
209 | auto ret = ::posix_memalign(&ptr, alignment, size * sizeof(CharType)); | |
210 | auto buf = static_cast<CharType*>(ptr); | |
211 | if (ret) { | |
212 | throw std::bad_alloc(); | |
213 | } | |
214 | return temporary_buffer(buf, size, make_free_deleter(buf)); | |
215 | } | |
216 | ||
f67539c2 TL |
217 | static temporary_buffer copy_of(std::string_view view) { |
218 | void* ptr = ::malloc(view.size()); | |
219 | if (!ptr) { | |
220 | throw std::bad_alloc(); | |
221 | } | |
222 | auto buf = static_cast<CharType*>(ptr); | |
223 | memcpy(buf, view.data(), view.size()); | |
224 | return temporary_buffer(buf, view.size(), make_free_deleter(buf)); | |
225 | } | |
226 | ||
11fdf7f2 TL |
227 | /// Compare contents of this buffer with another buffer for equality |
228 | /// | |
229 | /// \param o buffer to compare with | |
230 | /// \return true if and only if contents are the same | |
20effc67 | 231 | bool operator==(const temporary_buffer& o) const noexcept { |
11fdf7f2 TL |
232 | return size() == o.size() && std::equal(begin(), end(), o.begin()); |
233 | } | |
234 | ||
235 | /// Compare contents of this buffer with another buffer for inequality | |
236 | /// | |
237 | /// \param o buffer to compare with | |
238 | /// \return true if and only if contents are not the same | |
20effc67 | 239 | bool operator!=(const temporary_buffer& o) const noexcept { |
11fdf7f2 TL |
240 | return !(*this == o); |
241 | } | |
242 | }; | |
243 | ||
244 | /// @} | |
245 | ||
246 | } |