Files
scylladb/core/temporary_buffer.hh
2015-06-14 23:52:25 +03:00

202 lines
8.0 KiB
C++

/*
* This file is open source software, licensed to you under the terms
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://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.
*/
/*
* Copyright (C) 2014 Cloudius Systems, Ltd.
*/
#ifndef TEMPORARY_BUFFER_HH_
#define TEMPORARY_BUFFER_HH_
#include "deleter.hh"
#include "util/eclipse.hh"
#include <malloc.h>
/// \addtogroup memory-module
/// @{
/// Temporary, self-managed byte buffer.
///
/// A \c temporary_buffer is similar to an \c std::string or a \c std::unique_ptr<char[]>,
/// but provides more flexible memory management. A \c temporary_buffer can own the memory
/// it points to, or it can be shared with another \c temporary_buffer, or point at a substring
/// of a buffer. It uses a \ref deleter to manage its memory.
///
/// A \c temporary_buffer should not be held indefinitely. It can be held while a request
/// is processed, or for a similar duration, but not longer, as it can tie up more memory
/// that its size indicates.
///
/// A buffer can be shared: two \c temporary_buffer objects will point to the same data,
/// or a subset of it. See the \ref temporary_buffer::share() method.
///
/// Unless you created a \c temporary_buffer yourself, do not modify its contents, as they
/// may be shared with another user that does not expect the data to change.
///
/// Use cases for a \c temporary_buffer include:
/// - passing a substring of a tcp packet for the user to consume (zero-copy
/// tcp input)
/// - passing a refcounted blob held in memory to tcp, ensuring that when the TCP ACK
/// is received, the blob is released (by decrementing its reference count) (zero-copy
/// tcp output)
///
/// \tparam CharType underlying character type (must be a variant of \c char).
template <typename CharType>
class temporary_buffer {
static_assert(sizeof(CharType) == 1, "must buffer stream of bytes");
CharType* _buffer;
size_t _size;
deleter _deleter;
public:
/// Creates a \c temporary_buffer of a specified size. The buffer is not shared
/// with anyone, and is not initialized.
///
/// \param size buffer size, in bytes
explicit temporary_buffer(size_t size)
: _buffer(static_cast<CharType*>(malloc(size * sizeof(CharType)))), _size(size)
, _deleter(make_free_deleter(_buffer)) {
if (size && !_buffer) {
throw std::bad_alloc();
}
}
//explicit temporary_buffer(CharType* borrow, size_t size) : _buffer(borrow), _size(size) {}
/// Creates an empty \c temporary_buffer that does not point at anything.
temporary_buffer()
: _buffer(nullptr)
, _size(0) {}
temporary_buffer(const temporary_buffer&) = delete;
/// Moves a \c temporary_buffer.
temporary_buffer(temporary_buffer&& x) : _buffer(x._buffer), _size(x._size), _deleter(std::move(x._deleter)) {
x._buffer = nullptr;
x._size = 0;
}
/// Creates a \c temporary_buffer with a specific deleter.
///
/// \param buf beginning of the buffer held by this \c temporary_buffer
/// \param size size of the buffer
/// \param d deleter controlling destruction of the buffer. The deleter
/// will be destroyed when there are no longer any users for the buffer.
temporary_buffer(CharType* buf, size_t size, deleter d)
: _buffer(buf), _size(size), _deleter(std::move(d)) {}
void operator=(const temporary_buffer&) = delete;
/// Moves a \c temporary_buffer.
temporary_buffer& operator=(temporary_buffer&& x) {
if (this != &x) {
_buffer = x._buffer;
_size = x._size;
_deleter = std::move(x._deleter);
x._buffer = nullptr;
x._size = 0;
}
return *this;
}
/// Gets a pointer to the beginning of the buffer.
const CharType* get() const { return _buffer; }
/// Gets a writable pointer to the beginning of the buffer. Use only
/// when you are certain no user expects the buffer data not to change.
CharType* get_write() { return _buffer; }
/// Gets the buffer size.
size_t size() const { return _size; }
/// Gets a pointer to the beginning of the buffer.
const CharType* begin() { return _buffer; }
/// Gets a pointer to the end of the buffer.
const CharType* end() { return _buffer + _size; }
/// Returns the buffer, but with a reduced size. The original
/// buffer is consumed by this call and can no longer be used.
///
/// \param size New size; must be smaller than current size.
/// \return the same buffer, with a prefix removed.
temporary_buffer prefix(size_t size) && {
auto ret = std::move(*this);
ret._size = size;
return ret;
}
/// Reads a character from a specific position in the buffer.
///
/// \param pos position to read character from; must be less than size.
CharType operator[](size_t pos) const {
return _buffer[pos];
}
/// Checks whether the buffer is empty.
bool empty() const { return !size(); }
/// Checks whether the buffer is not empty.
operator bool() { return size(); }
/// Create a new \c temporary_buffer object referring to the same
/// underlying data. The underlying \ref deleter will not be destroyed
/// until both the original and the clone have been destroyed.
///
/// \return a clone of the buffer object.
temporary_buffer share() {
return temporary_buffer(_buffer, _size, _deleter.share());
}
/// Create a new \c temporary_buffer object referring to a substring of the
/// same underlying data. The underlying \ref deleter will not be destroyed
/// until both the original and the clone have been destroyed.
///
/// \param pos Position of the first character to share.
/// \param len Length of substring to share.
/// \return a clone of the buffer object, referring to a substring.
temporary_buffer share(size_t pos, size_t len) {
auto ret = share();
ret._buffer += pos;
ret._size = len;
return ret;
}
/// Remove a prefix from the buffer. The underlying data
/// is not modified.
///
/// \param pos Position of first character to retain.
void trim_front(size_t pos) {
_buffer += pos;
_size -= pos;
}
/// Remove a suffix from the buffer. The underlying data
/// is not modified.
///
/// \param pos Position of first character to drop.
void trim(size_t pos) {
_size = pos;
}
/// Stops automatic memory management. When the \c temporary_buffer
/// object is destroyed, the underlying \ref deleter will not be called.
/// Instead, it is the caller's responsibility to destroy the deleter object
/// when the data is no longer needed.
///
/// \return \ref deleter object managing the data's lifetime.
deleter release() {
return std::move(_deleter);
}
/// Creates a \c temporary_buffer object with a specified size, with
/// memory aligned to a specific boundary.
///
/// \param alignment Required alignment; must be a power of two.
/// \param size Required size.
/// \return a new \c temporary_buffer object.
static temporary_buffer aligned(size_t alignment, size_t size) {
void *ptr = nullptr;
auto ret = ::posix_memalign(&ptr, alignment, size * sizeof(CharType));
auto buf = static_cast<CharType*>(ptr);
if (ret) {
throw std::bad_alloc();
}
return temporary_buffer(buf, size, make_free_deleter(buf));
}
};
/// @}
#endif /* TEMPORARY_BUFFER_HH_ */