2020-05-20 12:11:23 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2021-08-09 19:46:21 +02:00
|
|
|
* Copyright (C) 2016,2018-2021 Tom Zander <tom@flowee.org>
|
2020-05-20 12:11:23 +02:00
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#ifndef BUFFER_POOL_H
|
|
|
|
|
#define BUFFER_POOL_H
|
|
|
|
|
|
|
|
|
|
#include "ConstBuffer.h"
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
|
|
namespace Streaming {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The buffer pool is a utility class that allows you to allocate a big chunk of memory and
|
|
|
|
|
* create any number of buffers from it in order to avoid expensive calls to malloc.
|
|
|
|
|
*
|
|
|
|
|
* The easiest usage is the following pattern;
|
|
|
|
|
@code
|
|
|
|
|
BufferPool pool;
|
|
|
|
|
pool.reserve(myString.size()); //make sure the pool has enough space
|
|
|
|
|
strcpy(pool.data(), myString);
|
|
|
|
|
pool.markUsed(myString.size());
|
|
|
|
|
ConstBuffer buf = pool.commit();
|
|
|
|
|
@endcode
|
|
|
|
|
* Do notice that you likely want to make your pool a member so its a long-lived class and
|
|
|
|
|
* it can create buffers for a long time.
|
|
|
|
|
*
|
|
|
|
|
* The data internally in both the BufferPool and the ConstBuffer uses shared pointers so
|
|
|
|
|
* all memory management is automatically taken care of.
|
|
|
|
|
*/
|
|
|
|
|
class BufferPool
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
BufferPool(int m_defaultSize = 256 * 1024);
|
|
|
|
|
BufferPool(BufferPool&& other);
|
2021-08-09 19:46:21 +02:00
|
|
|
/**
|
|
|
|
|
* Create a buffer pool wrapping an existing data pool.
|
|
|
|
|
* @param data the pre-allocated buffer.
|
|
|
|
|
* @param length number of bytes that this pre-allocated buffer is in size.
|
|
|
|
|
* @param staticBuf if true this bufferpool will not create any new internal buffer when this one if full.
|
|
|
|
|
* In such a case, running out of space will result in a runtime exception.
|
|
|
|
|
*/
|
2020-05-20 12:11:23 +02:00
|
|
|
BufferPool(std::shared_ptr<char> &data, int length, bool staticBuf);
|
|
|
|
|
BufferPool& operator=(BufferPool&& other);
|
|
|
|
|
|
|
|
|
|
/// Ensures that there are at least that \a bytes available for writing
|
|
|
|
|
void reserve(int bytes);
|
|
|
|
|
|
|
|
|
|
/// Return the available space for writing - NOT the same as size()
|
|
|
|
|
int capacity() const;
|
|
|
|
|
|
|
|
|
|
/// usage of storage (by writing to data()) should be registered
|
|
|
|
|
/// so a future call to commit() returns the right data.
|
|
|
|
|
inline void markUsed(int bytecount)
|
|
|
|
|
{
|
|
|
|
|
assert(bytecount >= 0);
|
|
|
|
|
m_writePointer += bytecount;
|
|
|
|
|
assert(m_writePointer <= m_buffer.get() + m_size);
|
|
|
|
|
assert(m_writePointer >= m_readPointer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This method will create a ConstBuffer of a more flexible size than commit().
|
|
|
|
|
* The start and end pointers have to be within the reserved space of
|
|
|
|
|
* the internal_buffer, which means that after data was marked as possible to
|
|
|
|
|
* forget with forget(), you should not try to create a buffer any longer as
|
|
|
|
|
* that may fail (trigger an assert in debug builds).
|
|
|
|
|
*/
|
|
|
|
|
ConstBuffer createBufferSlice(char const* start, char const* stop) const;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* After a buffer has been used you should make the pool forget about it so the data can be
|
|
|
|
|
* garbage collected.
|
|
|
|
|
* This probably doesn't have to be called directly, you want to use commit() instead.
|
|
|
|
|
*/
|
|
|
|
|
void forget(int read_bytes);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Commit the used part into a ConstBuffer and preparing the pool for a new buffer.
|
|
|
|
|
* This is equivalent to;
|
|
|
|
|
@code
|
|
|
|
|
markUsed(usedBytes); // move 'end()'
|
|
|
|
|
ConstBuffer buf = createBufferSlice(begin(), end());
|
|
|
|
|
forget(buf.size()); // move 'begin()'
|
|
|
|
|
return buf;
|
|
|
|
|
@endcode
|
|
|
|
|
*/
|
|
|
|
|
ConstBuffer commit(int usedBytes = 0);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Iterator to access the already 'used' part
|
|
|
|
|
* Begin points to the start of the uncommitted memory area.
|
|
|
|
|
* Directly after a commit, but before a markUsed() both begin and end are the same.
|
|
|
|
|
* \see markUsed()
|
|
|
|
|
* \see end()
|
|
|
|
|
*/
|
|
|
|
|
char const* begin() const {
|
|
|
|
|
return m_readPointer;
|
|
|
|
|
}
|
|
|
|
|
/// non-const convenience method.
|
|
|
|
|
inline char* begin() {
|
|
|
|
|
return m_readPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Iterator to access the not yet 'used' part
|
|
|
|
|
* End points directly after the end of the uncommitted, but 'used' memory area.
|
|
|
|
|
* Directly after a commit, but before a markUsed() both begin and end are the same.
|
|
|
|
|
* \see markUsed()
|
|
|
|
|
* \see begin()
|
|
|
|
|
* \see data()
|
|
|
|
|
*/
|
|
|
|
|
char const* end() const {
|
|
|
|
|
return m_writePointer;
|
|
|
|
|
}
|
|
|
|
|
/// non-const convenience method.
|
|
|
|
|
char* end() {
|
|
|
|
|
return m_writePointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// indexing operator - assumes begin() + idx < end()
|
|
|
|
|
char operator[](size_t idx) const;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a pointer to the not yet used memory area.
|
|
|
|
|
* This is always equal to end()
|
|
|
|
|
* \see markUsed()
|
|
|
|
|
*/
|
|
|
|
|
inline char* data() {
|
|
|
|
|
return m_writePointer;
|
|
|
|
|
}
|
|
|
|
|
/// This is the size available for reading
|
|
|
|
|
/// same as end() - begin()
|
|
|
|
|
int size() const;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove all internal storage usage and reset size to the default (initial) size.
|
|
|
|
|
* A pool holds on to a buffer, sometimes a rather large one which may be not useful.
|
|
|
|
|
* Calling clear will do a reset() on the shared pointer and set the begin/end pointers
|
|
|
|
|
* to nullptr.
|
|
|
|
|
* It is essential for the next usage to call reserve() (but that was always the case anyway)
|
|
|
|
|
* before putting anything new in the buffer.
|
|
|
|
|
*/
|
|
|
|
|
void clear();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief writeInt32 allows you to write 4 bytes at begin() and mark them as used.
|
|
|
|
|
* @param data the 4-byte integer value to write.
|
|
|
|
|
*/
|
|
|
|
|
void writeInt32(unsigned int data);
|
|
|
|
|
|
|
|
|
|
void writeHex(const char *string);
|
|
|
|
|
|
|
|
|
|
/// Stream interface for compatibility with the legacy serialization
|
|
|
|
|
inline void write(const char *buf, size_t size) {
|
|
|
|
|
memcpy(data(), buf, size);
|
|
|
|
|
markUsed(static_cast<int>(size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int offset() const;
|
|
|
|
|
|
|
|
|
|
/// return the shared pointer to the buffer, useful to upgrade the refcount.
|
|
|
|
|
std::shared_ptr<char> internal_buffer() const;
|
|
|
|
|
private:
|
|
|
|
|
void change_capacity(int bytes);
|
|
|
|
|
BufferPool(BufferPool const&) = delete;
|
|
|
|
|
BufferPool& operator=(BufferPool const&) = delete;
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<char> m_buffer;
|
|
|
|
|
char *m_readPointer;
|
|
|
|
|
char *m_writePointer;
|
|
|
|
|
int m_defaultSize;
|
|
|
|
|
int m_size;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|