607 lines
19 KiB
C++
607 lines
19 KiB
C++
/**
|
|
* @file llbuffer.h
|
|
* @author Phoenix
|
|
* @date 2005-09-20
|
|
* @brief Declaration of buffer and buffer arrays primarily used in I/O.
|
|
*
|
|
* $LicenseInfo:firstyear=2005&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2005-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#ifndef LL_LLBUFFER_H
|
|
#define LL_LLBUFFER_H
|
|
|
|
/**
|
|
* Declaration of classes used for minimizing calls to new[],
|
|
* memcpy(), and delete[]. Typically, you would create an LLBufferArray,
|
|
* feed it data, modify and add segments as you process it, and feed
|
|
* it to a sink.
|
|
*/
|
|
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
/**
|
|
* @class LLChannelDescriptors
|
|
* @brief A way simple interface to accesss channels inside a buffer
|
|
*/
|
|
class LLChannelDescriptors
|
|
{
|
|
public:
|
|
// enumeration for segmenting the channel information
|
|
enum { E_CHANNEL_COUNT = 3 };
|
|
LLChannelDescriptors() : mBaseChannel(0) {}
|
|
explicit LLChannelDescriptors(S32 base) : mBaseChannel(base) {}
|
|
S32 in() const { return mBaseChannel; }
|
|
S32 out() const { return mBaseChannel + 1; }
|
|
//S32 err() const { return mBaseChannel + 2; }
|
|
protected:
|
|
S32 mBaseChannel;
|
|
};
|
|
|
|
|
|
/**
|
|
* @class LLSegment
|
|
* @brief A segment is a single, contiguous chunk of memory in a buffer
|
|
*
|
|
* Each segment represents a contiguous addressable piece of memory
|
|
* which is located inside a buffer. The segment is not responsible
|
|
* for allocation or deallcoation of the data. Each segment is a light
|
|
* weight object, and simple enough to copy around, use, and generate
|
|
* as necessary.
|
|
* This is the preferred interface for working with memory blocks,
|
|
* since it is the only way to safely, inexpensively, and directly
|
|
* access linear blocks of memory.
|
|
*/
|
|
class LLSegment
|
|
{
|
|
public:
|
|
LLSegment();
|
|
LLSegment(S32 channel, U8* data, S32 data_len);
|
|
~LLSegment();
|
|
|
|
/**
|
|
* @brief Check if this segment is on the given channel.
|
|
*
|
|
*/
|
|
bool isOnChannel(S32 channel) const;
|
|
|
|
/**
|
|
* @brief Get the channel
|
|
*/
|
|
S32 getChannel() const;
|
|
|
|
/**
|
|
* @brief Set the channel
|
|
*/
|
|
void setChannel(S32 channel);
|
|
|
|
/**
|
|
* @brief Return a raw pointer to the current data set.
|
|
*
|
|
* The pointer returned can be used for reading or even adjustment
|
|
* if you are a bit crazy up to size() bytes into memory.
|
|
* @return A potentially NULL pointer to the raw buffer data
|
|
*/
|
|
U8* data() const;
|
|
|
|
/**
|
|
* @brief Return the size of the segment
|
|
*/
|
|
S32 size() const;
|
|
|
|
/**
|
|
* @brief Check if two segments are the same.
|
|
*
|
|
* Two segments are considered equal if they are on the same
|
|
* channel and cover the exact same address range.
|
|
* @param rhs the segment to compare with this segment.
|
|
* @return Returns true if they are equal.
|
|
*/
|
|
bool operator==(const LLSegment& rhs) const;
|
|
|
|
protected:
|
|
S32 mChannel;
|
|
U8* mData;
|
|
S32 mSize;
|
|
};
|
|
|
|
/**
|
|
* @class LLBuffer
|
|
* @brief Abstract base class for buffers
|
|
*
|
|
* This class declares the interface necessary for buffer arrays. A
|
|
* buffer is not necessarily a single contiguous memory chunk, so
|
|
* please do not circumvent the segment API.
|
|
*/
|
|
class LLBuffer
|
|
{
|
|
public:
|
|
/**
|
|
* @brief The buffer base class should have no responsibilities
|
|
* other than an interface.
|
|
*/
|
|
virtual ~LLBuffer() {}
|
|
|
|
/**
|
|
* @brief Generate a segment for this buffer.
|
|
*
|
|
* The segment returned is always contiguous memory. This call can
|
|
* fail if no contiguous memory is available, eg, offset is past
|
|
* the end. The segment returned may be smaller than the requested
|
|
* size. The segment will never be larger than the requested size.
|
|
* @param channel The channel for the segment.
|
|
* @param offset The offset from zero in the buffer.
|
|
* @param size The requested size of the segment.
|
|
* @param segment[out] The out-value from the operation
|
|
* @return Returns true if a segment was found.
|
|
*/
|
|
virtual bool createSegment(S32 channel, S32 size, LLSegment& segment) = 0;
|
|
|
|
/**
|
|
* @brief Reclaim a segment from this buffer.
|
|
*
|
|
* This method is called on a buffer object when a caller is done
|
|
* with a contiguous segment of memory inside this buffer. Since
|
|
* segments can be cut arbitrarily outside of the control of the
|
|
* buffer, this segment may not match any segment returned from
|
|
* <code>createSegment()</code>.
|
|
* @param segment The contiguous buffer segment to reclaim.
|
|
* @return Returns true if the call was successful.
|
|
*/
|
|
virtual bool reclaimSegment(const LLSegment& segment) = 0;
|
|
|
|
/**
|
|
* @brief Test if a segment is inside this buffer.
|
|
*
|
|
* @param segment The contiguous buffer segment to test.
|
|
* @return Returns true if the segment is in the bufffer.
|
|
*/
|
|
virtual bool containsSegment(const LLSegment& segment) const = 0;
|
|
|
|
/**
|
|
* @brief Return the current number of bytes allocated.
|
|
*
|
|
* This was implemented as a debugging tool, and it is not
|
|
* necessarily a good idea to use it for anything else.
|
|
*/
|
|
virtual S32 capacity() const = 0;
|
|
};
|
|
|
|
/**
|
|
* @class LLHeapBuffer
|
|
* @brief A large contiguous buffer allocated on the heap with new[].
|
|
*
|
|
* This class is a simple buffer implementation which allocates chunks
|
|
* off the heap. Once a buffer is constructed, it's buffer has a fixed
|
|
* length.
|
|
*/
|
|
class LLHeapBuffer : public LLBuffer
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Construct a heap buffer with a reasonable default size.
|
|
*/
|
|
LLHeapBuffer();
|
|
|
|
/**
|
|
* @brief Construct a heap buffer with a specified size.
|
|
*
|
|
* @param size The minimum size of the buffer.
|
|
*/
|
|
explicit LLHeapBuffer(S32 size);
|
|
|
|
/**
|
|
* @brief Construct a heap buffer of minimum size len, and copy from src.
|
|
*
|
|
* @param src The source of the data to be copied.
|
|
* @param len The minimum size of the buffer.
|
|
*/
|
|
LLHeapBuffer(const U8* src, S32 len);
|
|
|
|
/**
|
|
* @brief Simple destruction.
|
|
*/
|
|
virtual ~LLHeapBuffer();
|
|
|
|
/**
|
|
* @brief Get the number of bytes left in the buffer.
|
|
*
|
|
* Note that this is not a virtual function, and only available in
|
|
* the LLHeapBuffer as a debugging aid.
|
|
* @return Returns the number of bytes left.
|
|
*/
|
|
S32 bytesLeft() const;
|
|
|
|
/**
|
|
* @brief Generate a segment for this buffer.
|
|
*
|
|
* The segment returned is always contiguous memory. This call can
|
|
* fail if no contiguous memory is available, eg, offset is past
|
|
* the end. The segment returned may be smaller than the requested
|
|
* size. It is up to the caller to delete the segment returned.
|
|
* @param channel The channel for the segment.
|
|
* @param offset The offset from zero in the buffer
|
|
* @param size The requested size of the segment
|
|
* @param segment[out] The out-value from the operation
|
|
* @return Returns true if a segment was found.
|
|
*/
|
|
virtual bool createSegment(S32 channel, S32 size, LLSegment& segment);
|
|
|
|
/**
|
|
* @brief reclaim a segment from this buffer.
|
|
*
|
|
* This method is called on a buffer object when a caller is done
|
|
* with a contiguous segment of memory inside this buffer. Since
|
|
* segments can be cut arbitrarily outside of the control of the
|
|
* buffer, this segment may not match any segment returned from
|
|
* <code>createSegment()</code>.
|
|
* This call will fail if the segment passed in is note completely
|
|
* inside the buffer, eg, if the segment starts before this buffer
|
|
* in memory or ends after it.
|
|
* @param segment The contiguous buffer segment to reclaim.
|
|
* @return Returns true if the call was successful.
|
|
*/
|
|
virtual bool reclaimSegment(const LLSegment& segment);
|
|
|
|
/**
|
|
* @brief Test if a segment is inside this buffer.
|
|
*
|
|
* @param segment The contiguous buffer segment to test.
|
|
* @return Returns true if the segment is in the bufffer.
|
|
*/
|
|
virtual bool containsSegment(const LLSegment& segment) const;
|
|
|
|
/**
|
|
* @brief Return the current number of bytes allocated.
|
|
*/
|
|
virtual S32 capacity() const { return mSize; }
|
|
|
|
protected:
|
|
U8* mBuffer;
|
|
S32 mSize;
|
|
U8* mNextFree;
|
|
S32 mReclaimedBytes;
|
|
|
|
private:
|
|
/**
|
|
* @brief Helper method to allocate a buffer and correctly set
|
|
* intertnal state of this buffer.
|
|
*/
|
|
void allocate(S32 size);
|
|
};
|
|
|
|
/**
|
|
* @class LLBufferArray
|
|
* @brief Class to represent scattered memory buffers and in-order segments
|
|
* of that buffered data.
|
|
*
|
|
* *NOTE: This class needs to have an iovec interface
|
|
*/
|
|
class LLBufferArray
|
|
{
|
|
public:
|
|
typedef std::vector<LLBuffer*> buffer_list_t;
|
|
typedef buffer_list_t::iterator buffer_iterator_t;
|
|
typedef buffer_list_t::const_iterator const_buffer_iterator_t;
|
|
typedef std::list<LLSegment> segment_list_t;
|
|
typedef segment_list_t::const_iterator const_segment_iterator_t;
|
|
typedef segment_list_t::iterator segment_iterator_t;
|
|
enum { npos = 0xffffffff };
|
|
|
|
LLBufferArray();
|
|
~LLBufferArray();
|
|
|
|
/* @name Channel methods
|
|
*/
|
|
//@{
|
|
/**
|
|
* @brief Generate the a channel descriptor which consumes the
|
|
* output for the channel passed in.
|
|
*/
|
|
static LLChannelDescriptors makeChannelConsumer(
|
|
const LLChannelDescriptors& channels);
|
|
|
|
/**
|
|
* @brief Generate the next channel descriptor for this buffer array.
|
|
*
|
|
* The channel descriptor interface is how the buffer array
|
|
* clients can know where to read and write data. Use this
|
|
* interface to get the 'next' channel set for usage. This is a
|
|
* bit of a simple hack until it's utility indicates it should be
|
|
* extended.
|
|
* @return Returns a valid channel descriptor set for input and output.
|
|
*/
|
|
LLChannelDescriptors nextChannel();
|
|
//@}
|
|
|
|
/* @name Data methods
|
|
*/
|
|
//@{
|
|
|
|
/**
|
|
* @brief Return the sum of all allocated bytes.
|
|
*/
|
|
S32 capacity() const;
|
|
|
|
// These methods will be useful once there is any kind of buffer
|
|
// besides a heap buffer.
|
|
//bool append(EBufferChannel channel, LLBuffer* data);
|
|
//bool prepend(EBufferChannel channel, LLBuffer* data);
|
|
//bool insertAfter(
|
|
// segment_iterator_t segment,
|
|
// EBufferChannel channel,
|
|
// LLBuffer* data);
|
|
|
|
/**
|
|
* @brief Put data on a channel at the end of this buffer array.
|
|
*
|
|
* The data is copied from src into the buffer array. At least one
|
|
* new segment is created and put on the end of the array. This
|
|
* object will internally allocate new buffers if necessary.
|
|
* @param channel The channel for this data
|
|
* @param src The start of memory for the data to be copied
|
|
* @param len The number of bytes of data to copy
|
|
* @return Returns true if the method worked.
|
|
*/
|
|
bool append(S32 channel, const U8* src, S32 len);
|
|
|
|
/**
|
|
* @brief Put data on a channel at the front of this buffer array.
|
|
*
|
|
* The data is copied from src into the buffer array. At least one
|
|
* new segment is created and put in the front of the array. This
|
|
* object will internally allocate new buffers if necessary.
|
|
* @param channel The channel for this data
|
|
* @param src The start of memory for the data to be copied
|
|
* @param len The number of bytes of data to copy
|
|
* @return Returns true if the method worked.
|
|
*/
|
|
bool prepend(S32 channel, const U8* src, S32 len);
|
|
|
|
/**
|
|
* @brief Insert data into a buffer array after a particular segment.
|
|
*
|
|
* The data is copied from src into the buffer array. At least one
|
|
* new segment is created and put in the array. This object will
|
|
* internally allocate new buffers if necessary.
|
|
* @param segment The segment in front of the new segments location
|
|
* @param channel The channel for this data
|
|
* @param src The start of memory for the data to be copied
|
|
* @param len The number of bytes of data to copy
|
|
* @return Returns true if the method worked.
|
|
*/
|
|
bool insertAfter(
|
|
segment_iterator_t segment,
|
|
S32 channel,
|
|
const U8* src,
|
|
S32 len);
|
|
|
|
/**
|
|
* @brief Count bytes in the buffer array on the specified channel
|
|
*
|
|
* @param channel The channel to count.
|
|
* @param start The start address in the array for counting. You
|
|
* can specify NULL to start at the beginning.
|
|
* @return Returns the number of bytes in the channel after start
|
|
*/
|
|
S32 countAfter(S32 channel, U8* start) const;
|
|
|
|
/**
|
|
* @brief Count all bytes on channel.
|
|
*
|
|
* Helper method which just calls countAfter().
|
|
* @param channel The channel to count.
|
|
* @return Returns the number of bytes in the channel.
|
|
*/
|
|
S32 count(S32 channel) const
|
|
{
|
|
return countAfter(channel, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Read bytes in the buffer array on the specified channel
|
|
*
|
|
* You should prefer iterating over segments is possible since
|
|
* this method requires you to allocate large buffers - precisely
|
|
* what this class is trying to prevent. This method will skip
|
|
* any segments which are not on the given channel, so this method
|
|
* would usually be used to read a channel and copy that to a log
|
|
* or a socket buffer or something.
|
|
* @param channel The channel to read.
|
|
* @param start The start address in the array for reading. You
|
|
* can specify NULL to start at the beginning.
|
|
* @param dest The destination of the data read. This must be at
|
|
* least len bytes long.
|
|
* @param len[in,out] <b>in</b> How many bytes to read. <b>out</b> How
|
|
* many bytes were read.
|
|
* @return Returns the address of the last read byte.
|
|
*/
|
|
U8* readAfter(S32 channel, U8* start, U8* dest, S32& len) const;
|
|
|
|
/**
|
|
* @brief Find an address in a buffer array
|
|
*
|
|
* @param channel The channel to seek in.
|
|
* @param start The start address in the array for the seek
|
|
* operation. You can specify NULL to start the seek at the
|
|
* beginning, or pass in npos to start at the end.
|
|
* @param delta How many bytes to seek through the array.
|
|
* @return Returns the address of the last read byte.
|
|
*/
|
|
U8* seek(S32 channel, U8* start, S32 delta) const;
|
|
//@}
|
|
|
|
/* @name Buffer interaction
|
|
*/
|
|
//@{
|
|
/**
|
|
* @brief Take the contents of another buffer array
|
|
*
|
|
* This method simply strips the contents out of the source
|
|
* buffery array - segments, buffers, etc, and appends them to
|
|
* this instance. After this operation, the source is empty and
|
|
* ready for reuse.
|
|
* @param source The source buffer
|
|
* @return Returns true if the operation succeeded.
|
|
*/
|
|
bool takeContents(LLBufferArray& source);
|
|
//@}
|
|
|
|
/* @name Segment methods
|
|
*/
|
|
//@{
|
|
/**
|
|
* @brief Split a segments so that address is the last address of
|
|
* one segment, and the rest of the original segment becomes
|
|
* another segment on the same channel.
|
|
*
|
|
* After this method call,
|
|
* <code>getLastSegmentAddress(*getSegment(address)) ==
|
|
* address</code> should be true. This call will only create a new
|
|
* segment if the statement above is false before the call. Since
|
|
* you usually call splitAfter() to change a segment property, use
|
|
* getSegment() to perform those operations.
|
|
* @param address The address which will become the last address
|
|
* of the segment it is in.
|
|
* @return Returns an iterator to the segment which contains
|
|
* <code>address</code> which is <code>endSegment()</code> on
|
|
* failure.
|
|
*/
|
|
segment_iterator_t splitAfter(U8* address);
|
|
|
|
/**
|
|
* @brief Get the first segment in the buffer array.
|
|
*
|
|
* @return Returns the segment if there is one.
|
|
*/
|
|
segment_iterator_t beginSegment();
|
|
|
|
/**
|
|
* @brief Get the one-past-the-end segment in the buffer array
|
|
*
|
|
* @return Returns the iterator for an invalid segment location.
|
|
*/
|
|
segment_iterator_t endSegment();
|
|
|
|
/**
|
|
* @brief Get the segment which holds the given address.
|
|
*
|
|
* As opposed to some methods, passing a NULL will result in
|
|
* returning the end segment.
|
|
* @param address An address in the middle of the sought segment.
|
|
* @return Returns the iterator for the segment or endSegment() on
|
|
* failure.
|
|
*/
|
|
const_segment_iterator_t getSegment(U8* address) const;
|
|
|
|
/**
|
|
* @brief Get the segment which holds the given address.
|
|
*
|
|
* As opposed to some methods, passing a NULL will result in
|
|
* returning the end segment.
|
|
* @param address An address in the middle of the sought segment.
|
|
* @return Returns the iterator for the segment or endSegment() on
|
|
* failure.
|
|
*/
|
|
segment_iterator_t getSegment(U8* address);
|
|
|
|
/**
|
|
* @brief Get a segment iterator after address, and a constructed
|
|
* segment to represent the next linear block of memory.
|
|
*
|
|
* This method is a helper by giving you the largest segment
|
|
* possible in the out-value param after the address provided. The
|
|
* iterator will be useful for iteration, while the segment can be
|
|
* used for direct access to memory after address if the return
|
|
* values isnot end. Passing in NULL will return beginSegment()
|
|
* which may be endSegment(). The segment returned will only be
|
|
* zero length if the return value equals end.
|
|
* This is really just a helper method, since all the information
|
|
* returned could be constructed through other methods.
|
|
* @param address An address in the middle of the sought segment.
|
|
* @param segment[out] segment to be used for reading or writing
|
|
* @return Returns an iterator which contains at least segment or
|
|
* endSegment() on failure.
|
|
*/
|
|
segment_iterator_t constructSegmentAfter(U8* address, LLSegment& segment);
|
|
|
|
/**
|
|
* @brief Make a new segment at the end of buffer array
|
|
*
|
|
* This method will attempt to create a new and empty segment of
|
|
* the specified length. The segment created may be shorter than
|
|
* requested.
|
|
* @param channel[in] The channel for the newly created segment.
|
|
* @param length[in] The requested length of the segment.
|
|
* @return Returns an iterator which contains at least segment or
|
|
* endSegment() on failure.
|
|
*/
|
|
segment_iterator_t makeSegment(S32 channel, S32 length);
|
|
|
|
/**
|
|
* @brief Erase the segment if it is in the buffer array.
|
|
*
|
|
* @param iter An iterator referring to the segment to erase.
|
|
* @return Returns true on success.
|
|
*/
|
|
bool eraseSegment(const segment_iterator_t& iter);
|
|
//@}
|
|
|
|
protected:
|
|
/**
|
|
* @brief Optimally put data in buffers, and reutrn segments.
|
|
*
|
|
* This is an internal function used to create buffers as
|
|
* necessary, and sequence the segments appropriately for the
|
|
* various ways to copy data from src into this.
|
|
* If this method fails, it may actually leak some space inside
|
|
* buffers, but I am not too worried about the slim possibility
|
|
* that we may have some 'dead' space which will be recovered when
|
|
* the buffer (which we will not lose) is deleted. Addressing this
|
|
* weakness will make the buffers almost as complex as a general
|
|
* memory management system.
|
|
* @param channel The channel for this data
|
|
* @param src The start of memory for the data to be copied
|
|
* @param len The number of bytes of data to copy
|
|
* @param segments Out-value for the segments created.
|
|
* @return Returns true if the method worked.
|
|
*/
|
|
bool copyIntoBuffers(
|
|
S32 channel,
|
|
const U8* src,
|
|
S32 len,
|
|
std::vector<LLSegment>& segments);
|
|
|
|
protected:
|
|
S32 mNextBaseChannel;
|
|
buffer_list_t mBuffers;
|
|
segment_list_t mSegments;
|
|
};
|
|
|
|
#endif // LL_LLBUFFER_H
|