123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /**
- * @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>
- #include "llpreprocessor.h"
- #include "llthread.h"
- /**
- * @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)
- {
- }
- LL_INLINE S32 in() const { return mBaseChannel; }
- LL_INLINE S32 out() const { return mBaseChannel + 1; }
- #if 0
- LL_INLINE S32 err() const { return mBaseChannel + 2; }
- #endif
- 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()
- : mChannel(0),
- mData(NULL),
- mSize(0)
- {
- }
- LLSegment(S32 channel, U8* data, S32 data_len)
- : mChannel(channel),
- mData(data),
- mSize(data_len)
- {
- }
- ~LLSegment() {}
- /**
- * @brief Check if this segment is on the given channel.
- *
- */
- LL_INLINE bool isOnChannel(S32 channel) const { return mChannel == channel; }
- /**
- * @brief Get the channel
- */
- LL_INLINE S32 getChannel() const { return mChannel; }
- /**
- * @brief Set the channel
- */
- LL_INLINE void setChannel(S32 channel) { mChannel = 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
- */
- LL_INLINE U8* data() const { return mData; }
- /**
- * @brief Return the size of the segment
- */
- LL_INLINE S32 size() const { return mSize; }
- /**
- * @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.
- */
- LL_INLINE bool operator==(const LLSegment& rhs) const
- {
- if (mData != rhs.mData || mSize != rhs.mSize ||
- mChannel != rhs.mChannel)
- {
- return false;
- }
- return true;
- }
- 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
- {
- protected:
- LOG_CLASS(LLHeapBuffer);
- 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.
- */
- LL_INLINE 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;
- #if 0 // 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);
- #endif
- /**
- * @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.
- */
- LL_INLINE 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);
- /**
- * @brief Lock the mutex if it exists
- * This method locks mMutexp to make accessing LLBufferArray thread-safe
- */
- LL_INLINE void lock() { if (mMutexp) mMutexp->lock(); }
- /**
- * @brief Unlock the mutex if it exists
- */
- LL_INLINE void unlock() { if (mMutexp) mMutexp->unlock(); }
- /**
- * @brief Return mMutexp
- */
- LL_INLINE LLMutex* getMutex() { return mMutexp; }
- /**
- * @brief Set LLBufferArray to be shared across threads or not
- * This method is to create mMutexp if is threaded.
- * @param threaded Indicates this LLBufferArray instance is shared across
- * threads if true.
- */
- void setThreaded(bool threaded);
- //@}
- 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;
- LLMutex* mMutexp;
- };
- #endif // LL_LLBUFFER_H
|