summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcommon
Print done when done.
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/bitpack.cpp148
-rw-r--r--indra/llcommon/bitpack.h190
-rw-r--r--indra/llcommon/ctype_workaround.h36
-rw-r--r--indra/llcommon/doublelinkedlist.h1379
-rw-r--r--indra/llcommon/imageids.h79
-rw-r--r--indra/llcommon/indra_constants.h338
-rw-r--r--indra/llcommon/linden_common.h53
-rw-r--r--indra/llcommon/linked_lists.h919
-rw-r--r--indra/llcommon/llagentconstants.h141
-rw-r--r--indra/llcommon/llapp.cpp616
-rw-r--r--indra/llcommon/llapp.h259
-rw-r--r--indra/llcommon/llapr.cpp201
-rw-r--r--indra/llcommon/llapr.h129
-rw-r--r--indra/llcommon/llassettype.cpp219
-rw-r--r--indra/llcommon/llassettype.h149
-rw-r--r--indra/llcommon/llassoclist.h278
-rw-r--r--indra/llcommon/llavatarconstants.h23
-rw-r--r--indra/llcommon/llboost.h25
-rw-r--r--indra/llcommon/llchat.h69
-rw-r--r--indra/llcommon/llclickaction.h20
-rw-r--r--indra/llcommon/llcommon.cpp43
-rw-r--r--indra/llcommon/llcommon.h28
-rw-r--r--indra/llcommon/llcriticaldamp.cpp72
-rw-r--r--indra/llcommon/llcriticaldamp.h35
-rw-r--r--indra/llcommon/lldarray.h192
-rw-r--r--indra/llcommon/lldarrayptr.h18
-rw-r--r--indra/llcommon/lldate.cpp174
-rw-r--r--indra/llcommon/lldate.h102
-rw-r--r--indra/llcommon/lldefs.h185
-rw-r--r--indra/llcommon/lldepthstack.h81
-rw-r--r--indra/llcommon/lldlinked.h77
-rw-r--r--indra/llcommon/lldqueueptr.h334
-rw-r--r--indra/llcommon/llendianswizzle.h76
-rw-r--r--indra/llcommon/llenum.h60
-rw-r--r--indra/llcommon/llerror.cpp40
-rw-r--r--indra/llcommon/llerror.h218
-rw-r--r--indra/llcommon/llerrorthread.cpp189
-rw-r--r--indra/llcommon/llerrorthread.h28
-rw-r--r--indra/llcommon/llevent.cpp285
-rw-r--r--indra/llcommon/llevent.h178
-rw-r--r--indra/llcommon/lleventemitter.h84
-rw-r--r--indra/llcommon/llfasttimer.h187
-rw-r--r--indra/llcommon/llfile.cpp251
-rw-r--r--indra/llcommon/llfile.h151
-rw-r--r--indra/llcommon/llfixedbuffer.cpp71
-rw-r--r--indra/llcommon/llfixedbuffer.h44
-rw-r--r--indra/llcommon/llframetimer.cpp69
-rw-r--r--indra/llcommon/llframetimer.h122
-rw-r--r--indra/llcommon/llhash.h45
-rw-r--r--indra/llcommon/llindexedqueue.h137
-rw-r--r--indra/llcommon/lllinkedqueue.h291
-rw-r--r--indra/llcommon/lllivefile.cpp74
-rw-r--r--indra/llcommon/lllivefile.h34
-rw-r--r--indra/llcommon/lllocalidhashmap.h877
-rw-r--r--indra/llcommon/lllslconstants.h139
-rw-r--r--indra/llcommon/llmap.h231
-rw-r--r--indra/llcommon/llmemory.cpp293
-rw-r--r--indra/llcommon/llmemory.h300
-rw-r--r--indra/llcommon/llmemorystream.cpp52
-rw-r--r--indra/llcommon/llmemorystream.h63
-rw-r--r--indra/llcommon/llmemtype.h135
-rw-r--r--indra/llcommon/llmortician.cpp51
-rw-r--r--indra/llcommon/llmortician.h33
-rw-r--r--indra/llcommon/llnametable.h87
-rw-r--r--indra/llcommon/llpreprocessor.h99
-rw-r--r--indra/llcommon/llpriqueuemap.h127
-rw-r--r--indra/llcommon/llprocessor.cpp2125
-rw-r--r--indra/llcommon/llprocessor.h158
-rw-r--r--indra/llcommon/llptrskiplist.h704
-rw-r--r--indra/llcommon/llptrskipmap.h1219
-rw-r--r--indra/llcommon/llqueuedthread.cpp491
-rw-r--r--indra/llcommon/llqueuedthread.h202
-rw-r--r--indra/llcommon/llrun.cpp156
-rw-r--r--indra/llcommon/llrun.h144
-rw-r--r--indra/llcommon/llsd.cpp731
-rw-r--r--indra/llcommon/llsd.h366
-rw-r--r--indra/llcommon/llsdserialize.cpp1621
-rw-r--r--indra/llcommon/llsdserialize.h592
-rw-r--r--indra/llcommon/llsdserialize_xml.cpp706
-rw-r--r--indra/llcommon/llsdserialize_xml.h17
-rw-r--r--indra/llcommon/llsdutil.cpp177
-rw-r--r--indra/llcommon/llsdutil.h50
-rw-r--r--indra/llcommon/llsecondlifeurls.cpp63
-rw-r--r--indra/llcommon/llsecondlifeurls.h64
-rw-r--r--indra/llcommon/llsimplehash.h137
-rw-r--r--indra/llcommon/llskiplist.h502
-rw-r--r--indra/llcommon/llskipmap.h1004
-rw-r--r--indra/llcommon/llstack.h30
-rw-r--r--indra/llcommon/llstat.cpp803
-rw-r--r--indra/llcommon/llstat.h182
-rw-r--r--indra/llcommon/llstatenums.h40
-rw-r--r--indra/llcommon/llstl.h452
-rw-r--r--indra/llcommon/llstreamtools.cpp551
-rw-r--r--indra/llcommon/llstreamtools.h98
-rw-r--r--indra/llcommon/llstrider.h38
-rw-r--r--indra/llcommon/llstring.cpp835
-rw-r--r--indra/llcommon/llstring.h1281
-rw-r--r--indra/llcommon/llstringtable.cpp323
-rw-r--r--indra/llcommon/llstringtable.h208
-rw-r--r--indra/llcommon/llsys.cpp534
-rw-r--r--indra/llcommon/llsys.h91
-rw-r--r--indra/llcommon/llthread.cpp330
-rw-r--r--indra/llcommon/llthread.h164
-rw-r--r--indra/llcommon/lltimer.cpp517
-rw-r--r--indra/llcommon/lltimer.h142
-rw-r--r--indra/llcommon/lluri.cpp579
-rw-r--r--indra/llcommon/lluri.h72
-rw-r--r--indra/llcommon/lluuidhashmap.h557
-rw-r--r--indra/llcommon/llworkerthread.cpp284
-rw-r--r--indra/llcommon/llworkerthread.h168
-rw-r--r--indra/llcommon/metaclass.cpp60
-rw-r--r--indra/llcommon/metaclass.h64
-rw-r--r--indra/llcommon/metaclasst.h42
-rw-r--r--indra/llcommon/metaproperty.cpp35
-rw-r--r--indra/llcommon/metaproperty.h55
-rw-r--r--indra/llcommon/metapropertyt.h165
-rw-r--r--indra/llcommon/reflective.cpp20
-rw-r--r--indra/llcommon/reflective.h24
-rw-r--r--indra/llcommon/reflectivet.h30
-rw-r--r--indra/llcommon/roles_constants.h168
-rw-r--r--indra/llcommon/stdenums.h115
-rw-r--r--indra/llcommon/stdtypes.h89
-rw-r--r--indra/llcommon/string_table.h8
-rw-r--r--indra/llcommon/timer.h8
-rw-r--r--indra/llcommon/timing.cpp7
-rw-r--r--indra/llcommon/timing.h26
-rw-r--r--indra/llcommon/u64.cpp91
-rw-r--r--indra/llcommon/u64.h19
128 files changed, 32267 insertions, 0 deletions
diff --git a/indra/llcommon/bitpack.cpp b/indra/llcommon/bitpack.cpp
new file mode 100644
index 0000000000..4acd533600
--- /dev/null
+++ b/indra/llcommon/bitpack.cpp
@@ -0,0 +1,148 @@
+/**
+ * @file bitpack.cpp
+ * @brief Convert data to packed bit stream
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "bitpack.h"
+
+#if 0
+#include <stdio.h>
+#include <stdlib.h>
+#include "stdtypes.h"
+
+U8 gLoad, gUnLoad;
+U32 gLoadSize, gUnLoadSize, gTotalBits;
+
+const U32 gMaxDataBits = 8;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+void bit_pack(U8 *outbase, U32 *outptr, U8 *total_data, U32 total_dsize)
+{
+ U32 max_data_bits = gMaxDataBits;
+ U32 load_size = gLoadSize, total_bits = gTotalBits;
+ U32 dsize;
+
+ U8 data;
+ U8 load = gLoad;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > max_data_bits)
+ {
+ dsize = max_data_bits;
+ total_dsize -= max_data_bits;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ data <<= (max_data_bits - dsize);
+ while (dsize > 0)
+ {
+ if (load_size == max_data_bits)
+ {
+ *(outbase + (*outptr)++) = load;
+ load_size = 0;
+ load = 0x00;
+ }
+ load <<= 1;
+ load |= (data >> (max_data_bits - 1));
+ data <<= 1;
+ load_size++;
+ total_bits++;
+ dsize--;
+ }
+ }
+
+ gLoad = load;
+ gLoadSize = load_size;
+ gTotalBits = total_bits;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void bit_pack_reset()
+{
+ gLoad = 0x0;
+ gLoadSize = 0;
+ gTotalBits = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void bit_pack_flush(U8 *outbase, U32 *outptr)
+{
+ if (gLoadSize)
+ {
+ gTotalBits += gLoadSize;
+ gLoad <<= (gMaxDataBits - gLoadSize);
+ outbase[(*outptr)++] = gLoad;
+ gLoadSize = 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+void bit_unpack(U8 *total_retval, U32 total_dsize, U8 *indata, U32 *inptr)
+{
+ U32 max_data_bits = gMaxDataBits;
+ U32 unload_size = gUnLoadSize;
+ U32 dsize;
+ U8 *retval;
+ U8 unload = gUnLoad;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > max_data_bits)
+ {
+ dsize = max_data_bits;
+ total_dsize -= max_data_bits;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ retval = total_data++;
+ *retval = 0x00;
+ while (dsize > 0)
+ {
+ if (unload_size == 0)
+ {
+ unload = indata[(*inptr)++];
+ unload_size = max_data_bits;
+ }
+ *retval <<= 1;
+ *retval |= (unload >> (max_data_bits - 1));
+ unload_size--;
+ unload <<= 1;
+ dsize--;
+ }
+ }
+
+ gUnLoad = unload;
+ gUnLoadSize = unload_size;
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void bit_unpack_reset()
+{
+ gUnLoad = 0;
+ gUnLoadSize = 0;
+}
+#endif
diff --git a/indra/llcommon/bitpack.h b/indra/llcommon/bitpack.h
new file mode 100644
index 0000000000..2303aad8ea
--- /dev/null
+++ b/indra/llcommon/bitpack.h
@@ -0,0 +1,190 @@
+/**
+ * @file bitpack.h
+ * @brief Convert data to packed bit stream
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_BITPACK_H
+#define LL_BITPACK_H
+
+#include "llerror.h"
+
+const U32 MAX_DATA_BITS = 8;
+
+
+class LLBitPack
+{
+public:
+ LLBitPack(U8 *buffer, U32 max_size) : mBuffer(buffer), mBufferSize(0), mLoad(0), mLoadSize(0), mTotalBits(0), mMaxSize(max_size)
+ {
+ }
+
+ ~LLBitPack()
+ {
+ }
+
+ void resetBitPacking()
+ {
+ mLoad = 0;
+ mLoadSize = 0;
+ mTotalBits = 0;
+ mBufferSize = 0;
+ }
+
+ U32 bitPack(U8 *total_data, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 data;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ data <<= (MAX_DATA_BITS - dsize);
+ while (dsize > 0)
+ {
+ if (mLoadSize == MAX_DATA_BITS)
+ {
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ mLoad = 0x00;
+ }
+ mLoad <<= 1;
+ mLoad |= (data >> (MAX_DATA_BITS - 1));
+ data <<= 1;
+ mLoadSize++;
+ mTotalBits++;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 bitCopy(U8 *total_data, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 data;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ while (dsize > 0)
+ {
+ if (mLoadSize == MAX_DATA_BITS)
+ {
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ mLoad = 0x00;
+ }
+ mLoad <<= 1;
+ mLoad |= (data >> (MAX_DATA_BITS - 1));
+ data <<= 1;
+ mLoadSize++;
+ mTotalBits++;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 bitUnpack(U8 *total_retval, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 *retval;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ retval = total_retval++;
+ *retval = 0x00;
+ while (dsize > 0)
+ {
+ if (mLoadSize == 0)
+ {
+#ifdef _DEBUG
+ if (mBufferSize > mMaxSize)
+ {
+ llerrs << "mBufferSize exceeding mMaxSize" << llendl;
+ llerrs << mBufferSize << " > " << mMaxSize << llendl;
+ }
+#endif
+ mLoad = *(mBuffer + mBufferSize++);
+ mLoadSize = MAX_DATA_BITS;
+ }
+ *retval <<= 1;
+ *retval |= (mLoad >> (MAX_DATA_BITS - 1));
+ mLoadSize--;
+ mLoad <<= 1;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 flushBitPack()
+ {
+ if (mLoadSize)
+ {
+ mLoad <<= (MAX_DATA_BITS - mLoadSize);
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ }
+ return mBufferSize;
+ }
+
+ U8 *mBuffer;
+ U32 mBufferSize;
+ U8 mLoad;
+ U32 mLoadSize;
+ U32 mTotalBits;
+ U32 mMaxSize;
+};
+
+#endif
diff --git a/indra/llcommon/ctype_workaround.h b/indra/llcommon/ctype_workaround.h
new file mode 100644
index 0000000000..8c52b0290c
--- /dev/null
+++ b/indra/llcommon/ctype_workaround.h
@@ -0,0 +1,36 @@
+/**
+ * @file ctype_workaround.h
+ * @brief The workaround is to create some legacy symbols that point
+ * to the correct symbols, which avoids link errors.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef _CTYPE_WORKAROUND_H_
+#define _CTYPE_WORKAROUND_H_
+
+/**
+ * the CTYPE_WORKAROUND is needed for linux dev stations that don't
+ * have the broken libc6 packages needed by our out-of-date static
+ * libs (such as libcrypto and libcurl).
+ *
+ * -- Leviathan 20060113
+*/
+
+#include <ctype.h>
+
+__const unsigned short int *__ctype_b;
+__const __int32_t *__ctype_tolower;
+__const __int32_t *__ctype_toupper;
+
+// call this function at the beginning of main()
+void ctype_workaround()
+{
+ __ctype_b = *(__ctype_b_loc());
+ __ctype_toupper = *(__ctype_toupper_loc());
+ __ctype_tolower = *(__ctype_tolower_loc());
+}
+
+#endif
+
diff --git a/indra/llcommon/doublelinkedlist.h b/indra/llcommon/doublelinkedlist.h
new file mode 100644
index 0000000000..2546d621d0
--- /dev/null
+++ b/indra/llcommon/doublelinkedlist.h
@@ -0,0 +1,1379 @@
+/**
+ * @file doublelinkedlist.h
+ * @brief Provides a standard doubly linked list for fun and profit.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_DOUBLELINKEDLIST_H
+#define LL_DOUBLELINKEDLIST_H
+
+#include "llerror.h"
+#include "llrand.h"
+
+// node that actually contains the data
+template <class DATA_TYPE> class LLDoubleLinkedNode
+{
+public:
+ DATA_TYPE *mDatap;
+ LLDoubleLinkedNode *mNextp;
+ LLDoubleLinkedNode *mPrevp;
+
+
+public:
+ // assign the mDatap pointer
+ LLDoubleLinkedNode(DATA_TYPE *data);
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLDoubleLinkedNode();
+
+ // delete associated data and NULL out pointer
+ void deleteData();
+
+ // remove associated data and NULL out pointer
+ void removeData();
+};
+
+
+const U32 LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH = 4;
+
+template <class DATA_TYPE> class LLDoubleLinkedList
+{
+private:
+ LLDoubleLinkedNode<DATA_TYPE> mHead; // head node
+ LLDoubleLinkedNode<DATA_TYPE> mTail; // tail node
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuep; // The node in the batter's box
+ LLDoubleLinkedNode<DATA_TYPE> *mCurrentp; // The node we're talking about
+
+ // The state stack allows nested exploration of the LLDoubleLinkedList
+ // but should be used with great care
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuepStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH];
+ LLDoubleLinkedNode<DATA_TYPE> *mCurrentpStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH];
+ U32 mStateStackDepth;
+ U32 mCount;
+
+ // mInsertBefore is a pointer to a user-set function that returns
+ // TRUE if "first" should be located before "second"
+ // NOTE: mInsertBefore() should never return TRUE when ("first" == "second")
+ // or never-ending loops can occur
+ BOOL (*mInsertBefore)(DATA_TYPE *first, DATA_TYPE *second);
+
+public:
+ LLDoubleLinkedList();
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLDoubleLinkedList();
+
+ // put data into a node and stick it at the front of the list
+ // set mCurrentp to mQueuep
+ void addData(DATA_TYPE *data);
+
+ // put data into a node and stick it at the end of the list
+ // set mCurrentp to mQueuep
+ void addDataAtEnd(DATA_TYPE *data);
+
+ S32 getLength() const;
+ // search the list starting at mHead.mNextp and remove the link with mDatap == data
+ // set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL removeData(const DATA_TYPE *data);
+
+ // search the list starting at mHead.mNextp and delete the link with mDatap == data
+ // set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL deleteData(DATA_TYPE *data);
+
+ // remove all nodes from the list and delete the associated data
+ void deleteAllData();
+
+ // remove all nodes from the list but do not delete data
+ void removeAllNodes();
+
+ BOOL isEmpty();
+
+ // check to see if data is in list
+ // set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL checkData(const DATA_TYPE *data);
+
+ // NOTE: This next two funtions are only included here
+ // for those too familiar with the LLLinkedList template class.
+ // They are depreciated. resetList() is unecessary while
+ // getCurrentData() is identical to getNextData() and has
+ // a misleading name.
+ //
+ // The recommended way to loop through a list is as follows:
+ //
+ // datap = list.getFirstData();
+ // while (datap)
+ // {
+ // /* do stuff */
+ // datap = list.getNextData();
+ // }
+
+ // place mQueuep on mHead node
+ void resetList();
+
+ // return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ // NOTE: this function is identical to getNextData()
+ DATA_TYPE *getCurrentData();
+
+
+ // reset the list and return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ DATA_TYPE *getFirstData();
+
+
+ // reset the list and return the data at position n, set mCurentp
+ // to that node and bump mQueuep down the list
+ // Note: n=0 will behave like getFirstData()
+ DATA_TYPE *getNthData(U32 n);
+
+ // reset the list and return the last data in it,
+ // set mCurrentp to that node and bump mQueuep up the list
+ DATA_TYPE *getLastData();
+
+ // return data in mQueuep,
+ // set mCurrentp mQueuep and bump mQueuep down the list
+ DATA_TYPE *getNextData();
+
+ // return the data in mQueuep,
+ // set mCurrentp to mQueuep and bump mQueuep up the list
+ DATA_TYPE *getPreviousData();
+
+ // remove the Node at mCurrentp
+ // set mCurrentp to mQueuep
+ void removeCurrentData();
+
+ // delete the Node at mCurrentp
+ // set mCurrentp to mQueuep
+ void deleteCurrentData();
+
+ // remove the Node at mCurrentp and insert it into newlist
+ // set mCurrentp to mQueuep
+ void moveCurrentData(LLDoubleLinkedList<DATA_TYPE> *newlist);
+
+ // insert the node in front of mCurrentp
+ // set mCurrentp to mQueuep
+ void insertNode(LLDoubleLinkedNode<DATA_TYPE> *node);
+
+ // insert the data in front of mCurrentp
+ // set mCurrentp to mQueuep
+ void insertData(DATA_TYPE *data);
+
+ // if mCurrentp has a previous node then :
+ // * swaps mCurrentp with its previous
+ // * set mCurrentp to mQueuep
+ // (convenient for forward bubble-sort)
+ // otherwise does nothing
+ void swapCurrentWithPrevious();
+
+ // if mCurrentp has a next node then :
+ // * swaps mCurrentp with its next
+ // * set mCurrentp to mQueuep
+ // (convenient for backwards bubble-sort)
+ // otherwise does nothing
+ void swapCurrentWithNext();
+
+ // move mCurrentp to the front of the list
+ // set mCurrentp to mQueuep
+ void moveCurrentToFront();
+
+ // move mCurrentp to the end of the list
+ // set mCurrentp to mQueuep
+ void moveCurrentToEnd();
+
+ // set mInsertBefore
+ void setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second));
+
+ // add data in front of first node for which mInsertBefore(datap, node->mDatap) returns TRUE
+ // set mCurrentp to mQueuep
+ BOOL addDataSorted(DATA_TYPE *datap);
+
+ // sort the list using bubble-sort
+ // Yes, this is a different name than the same function in LLLinkedList.
+ // When it comes time for a name consolidation hopefully this one will win.
+ BOOL bubbleSort();
+
+ // does a single bubble sort pass on the list
+ BOOL lazyBubbleSort();
+
+ // returns TRUE if state successfully pushed (state stack not full)
+ BOOL pushState();
+
+ // returns TRUE if state successfully popped (state stack not empty)
+ BOOL popState();
+
+ // empties the state stack
+ void clearStateStack();
+
+ // randomly move the the links in the list for debug or (Discordian) purposes
+ // sets mCurrentp and mQueuep to top of list
+ void scramble();
+
+private:
+ // add node to beginning of list
+ // set mCurrentp to mQueuep
+ void addNode(LLDoubleLinkedNode<DATA_TYPE> *node);
+
+ // add node to end of list
+ // set mCurrentp to mQueuep
+ void addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *node);
+};
+
+//#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// doublelinkedlist.cpp
+// LLDoubleLinkedList template class implementation file.
+// Provides a standard doubly linked list for fun and profit.
+//
+// Copyright 2001, Linden Research, Inc.
+
+//#include "llerror.h"
+//#include "doublelinkedlist.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLDoubleLinkedNode
+//////////////////////////////////////////////////////////////////////////////////////////
+
+
+// assign the mDatap pointer
+template <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::LLDoubleLinkedNode(DATA_TYPE *data) :
+ mDatap(data), mNextp(NULL), mPrevp(NULL)
+{
+}
+
+
+// destructor does not, by default, destroy associated data
+// however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+template <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::~LLDoubleLinkedNode()
+{
+ if (mDatap)
+ {
+ llerror("Attempting to call LLDoubleLinkedNode destructor with a non-null mDatap!", 1);
+ }
+}
+
+
+// delete associated data and NULL out pointer
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::deleteData()
+{
+ delete mDatap;
+ mDatap = NULL;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::removeData()
+{
+ mDatap = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// LLDoubleLinkedList
+//////////////////////////////////////////////////////////////////////////////////////
+
+// <------- up -------
+//
+// mCurrentp
+// mQueuep |
+// | |
+// | |
+// .------. .------. .------. .------.
+// | |---->| |---->| |----->| |-----> NULL
+// NULL <-----| |<----| |<----| |<-----| |
+// _'------' '------' '------' '------:_
+// .------. /| | | |\ .------.
+// NULL <-----|mHead |/ | mQueuep \|mTail |-----> NULL
+// | | mCurrentp | |
+// '------' '------'
+// -------- down --------->
+
+template <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::LLDoubleLinkedList()
+: mHead(NULL), mTail(NULL), mQueuep(NULL)
+{
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+ mInsertBefore = NULL;
+}
+
+
+// destructor destroys list and nodes, but not data in nodes
+template <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::~LLDoubleLinkedList()
+{
+ removeAllNodes();
+}
+
+
+// put data into a node and stick it at the front of the list
+// doesn't change mCurrentp nor mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addData(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0);
+ }
+
+ // make the new node
+ LLDoubleLinkedNode<DATA_TYPE> *temp = new LLDoubleLinkedNode<DATA_TYPE> (data);
+
+ // add the node to the front of the list
+ temp->mPrevp = NULL;
+ temp->mNextp = mHead.mNextp;
+ mHead.mNextp = temp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevp = temp;
+ }
+ // otherwise, fix the tail of the list
+ else
+ {
+ mTail.mPrevp = temp;
+ }
+
+ mCount++;
+}
+
+
+// put data into a node and stick it at the end of the list
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addDataAtEnd(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0);
+ }
+
+ // make the new node
+ LLDoubleLinkedNode<DATA_TYPE> *nodep = new LLDoubleLinkedNode<DATA_TYPE>(data);
+
+ addNodeAtEnd(nodep);
+ mCount++;
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::removeData(const DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::removeData()", 0);
+ }
+
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ // reset the list back to where it was
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+
+ return b_found;
+}
+
+
+// search the list starting at mHead.mNextp and delete the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::deleteData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::deleteData()", 0);
+ }
+
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ // reset the list back to where it was
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+
+ return b_found;
+}
+
+
+// remove all nodes from the list and delete the associated data
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::deleteAllData()
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ mQueuep = mCurrentp->mNextp;
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = mQueuep;
+ }
+
+ // reset mHead and mQueuep
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+}
+
+
+// remove all nodes from the list but do not delete associated data
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::removeAllNodes()
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ mQueuep = mCurrentp->mNextp;
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = mQueuep;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+}
+
+template <class DATA_TYPE>
+S32 LLDoubleLinkedList<DATA_TYPE>::getLength() const
+{
+// U32 length = 0;
+// for (LLDoubleLinkedNode<DATA_TYPE>* temp = mHead.mNextp; temp != NULL; temp = temp->mNextp)
+// {
+// length++;
+// }
+ return mCount;
+}
+
+// check to see if data is in list
+// set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::checkData(const DATA_TYPE *data)
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ mQueuep = mCurrentp;
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ mCurrentp = mQueuep;
+ return FALSE;
+}
+
+// NOTE: This next two funtions are only included here
+// for those too familiar with the LLLinkedList template class.
+// They are depreciated. resetList() is unecessary while
+// getCurrentData() is identical to getNextData() and has
+// a misleading name.
+//
+// The recommended way to loop through a list is as follows:
+//
+// datap = list.getFirstData();
+// while (datap)
+// {
+// /* do stuff */
+// datap = list.getNextData();
+// }
+
+ // place mCurrentp and mQueuep on first node
+ template <class DATA_TYPE>
+ void LLDoubleLinkedList<DATA_TYPE>::resetList()
+ {
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ }
+
+
+ // return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ template <class DATA_TYPE>
+ DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getCurrentData()
+ {
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+
+// reset the list and return the data currently pointed to,
+// set mCurrentp to that node and bump mQueuep down the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getFirstData()
+{
+ mQueuep = mHead.mNextp;
+ mCurrentp = mQueuep;
+ if (mQueuep)
+ {
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// reset the list and return the data at position n, set mCurentp
+// to that node and bump mQueuep down the list
+// Note: n=0 will behave like getFirstData()
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getNthData(U32 n)
+{
+ mCurrentp = mHead.mNextp;
+
+ if (mCurrentp)
+ {
+ for (U32 i=0; i<n; i++)
+ {
+ mCurrentp = mCurrentp->mNextp;
+ if (!mCurrentp)
+ {
+ break;
+ }
+ }
+ }
+
+ if (mCurrentp)
+ {
+ // bump mQueuep down the list
+ mQueuep = mCurrentp->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ mQueuep = NULL;
+ return NULL;
+ }
+}
+
+
+// reset the list and return the last data in it,
+// set mCurrentp to that node and bump mQueuep up the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getLastData()
+{
+ mQueuep = mTail.mPrevp;
+ mCurrentp = mQueuep;
+ if (mQueuep)
+ {
+ mQueuep = mQueuep->mPrevp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// return the data in mQueuep,
+// set mCurrentp to mQueuep and bump mQueuep down the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getNextData()
+{
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// return the data in mQueuep,
+// set mCurrentp to mQueuep and bump mQueuep up the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getPreviousData()
+{
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mPrevp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// remove the Node at mCurrentp
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::removeCurrentData()
+{
+ if (mCurrentp)
+ {
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCount--;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// delete the Node at mCurrentp
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::deleteCurrentData()
+{
+ if (mCurrentp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the LLDoubleLinkedNode
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCount--;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// remove the Node at mCurrentp and insert it into newlist
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentData(LLDoubleLinkedList<DATA_TYPE> *newlist)
+{
+ if (mCurrentp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // move the node to the new list
+ newlist->addNode(mCurrentp);
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// Inserts the node previous to mCurrentp
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertNode(LLDoubleLinkedNode<DATA_TYPE> *nodep)
+{
+ // don't allow pointer to NULL to be passed
+ if (!nodep)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+ if (!nodep->mDatap)
+ {
+ llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+
+ if (mCurrentp)
+ {
+ if (mCurrentp->mPrevp)
+ {
+ nodep->mPrevp = mCurrentp->mPrevp;
+ nodep->mNextp = mCurrentp;
+ mCurrentp->mPrevp->mNextp = nodep;
+ mCurrentp->mPrevp = nodep;
+ }
+ else // at beginning of list
+ {
+ nodep->mPrevp = NULL;
+ nodep->mNextp = mCurrentp;
+ mHead.mNextp = nodep;
+ mCurrentp->mPrevp = nodep;
+ }
+ mCurrentp = mQueuep;
+ }
+ else // add to front of list
+ {
+ addNode(nodep);
+ }
+}
+
+
+// insert the data in front of mCurrentp
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertData(DATA_TYPE *data)
+{
+ if (!data)
+ {
+ llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+ LLDoubleLinkedNode<DATA_TYPE> *node = new LLDoubleLinkedNode<DATA_TYPE>(data);
+ insertNode(node);
+ mCount++;
+}
+
+
+// if mCurrentp has a previous node then :
+// * swaps mCurrentp with its previous
+// * set mCurrentp to mQueuep
+// otherwise does nothing
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::swapCurrentWithPrevious()
+{
+ if (mCurrentp)
+ {
+ if (mCurrentp->mPrevp)
+ {
+ // Pull mCurrentp out of list
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // mCurrentp was at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // Fix mCurrentp's pointers
+ mCurrentp->mNextp = mCurrentp->mPrevp;
+ mCurrentp->mPrevp = mCurrentp->mNextp->mPrevp;
+ mCurrentp->mNextp->mPrevp = mCurrentp;
+
+ if (mCurrentp->mPrevp)
+ {
+ // Fix the backward pointer of mCurrentp's new previous
+ mCurrentp->mPrevp->mNextp = mCurrentp;
+ }
+ else // mCurrentp is now at beginning of list
+ {
+ mHead.mNextp = mCurrentp;
+ }
+
+ // Set the list back to the way it was
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// if mCurrentp has a next node then :
+// * swaps mCurrentp with its next
+// * set mCurrentp to mQueuep
+// otherwise does nothing
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::swapCurrentWithNext()
+{
+ if (mCurrentp)
+ {
+ if (mCurrentp->mNextp)
+ {
+ // Pull mCurrentp out of list
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // mCurrentp was at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // Fix mCurrentp's pointers
+ mCurrentp->mPrevp = mCurrentp->mNextp;
+ mCurrentp->mNextp = mCurrentp->mPrevp->mNextp;
+ mCurrentp->mPrevp->mNextp = mCurrentp;
+
+ if (mCurrentp->mNextp)
+ {
+ // Fix the back pointer of mCurrentp's new next
+ mCurrentp->mNextp->mPrevp = mCurrentp;
+ }
+ else // mCurrentp is now at end of list
+ {
+ mTail.mPrevp = mCurrentp;
+ }
+
+ // Set the list back to the way it was
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+// move mCurrentp to the front of the list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentToFront()
+{
+ if (mCurrentp)
+ {
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ return;
+ }
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // add mCurrentp to beginning of list
+ mCurrentp->mNextp = mHead.mNextp;
+ mHead.mNextp->mPrevp = mCurrentp; // mHead.mNextp MUST be valid,
+ // or the list had only one node
+ // and we would have returned already
+ mCurrentp->mPrevp = NULL;
+ mHead.mNextp = mCurrentp;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+
+}
+
+// move mCurrentp to the end of the list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentToEnd()
+{
+ if (mCurrentp)
+ {
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list and we're done
+ {
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ return;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // add mCurrentp to end of list
+ mCurrentp->mPrevp = mTail.mPrevp;
+ mTail.mPrevp->mNextp = mCurrentp; // mTail.mPrevp MUST be valid,
+ // or the list had only one node
+ // and we would have returned already
+ mCurrentp->mNextp = NULL;
+ mTail.mPrevp = mCurrentp;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second) )
+{
+ mInsertBefore = insert_before;
+}
+
+
+// add data in front of the first node for which mInsertBefore(datap, node->mDatap) returns TRUE
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::addDataSorted(DATA_TYPE *datap)
+{
+ // don't allow NULL to be passed to addData()
+ if (!datap)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addDataSorted()", 0);
+ }
+
+ // has mInsertBefore not been set?
+ if (!mInsertBefore)
+ {
+ addData(datap);
+ return FALSE;
+ }
+
+ // is the list empty?
+ if (!mHead.mNextp)
+ {
+ addData(datap);
+ return TRUE;
+ }
+
+ // Note: this step has been added so that the behavior of LLDoubleLinkedList
+ // is as rigorous as the LLLinkedList class about adding duplicate nodes.
+ // Duplicate nodes can cause a problem when sorting if mInsertBefore(foo, foo)
+ // returns TRUE. However, if mInsertBefore(foo, foo) returns FALSE, then there
+ // shouldn't be any reason to exclude duplicate nodes (as we do here).
+ if (checkData(datap))
+ {
+ return FALSE;
+ }
+
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp)
+ {
+ // check to see if datap is already in the list
+ if (datap == mCurrentp->mDatap)
+ {
+ return FALSE;
+ }
+ else if (mInsertBefore(datap, mCurrentp->mDatap))
+ {
+ insertData(datap);
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ addDataAtEnd(datap);
+ return TRUE;
+}
+
+
+// bubble-sort until sorted and return TRUE if anything was sorted
+// leaves mQueuep pointing at last node that was swapped with its mNextp
+//
+// NOTE: if you find this function looping for really long times, then you
+// probably need to check your implementation of mInsertBefore(a,b) and make
+// sure it does not return TRUE when (a == b)!
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::bubbleSort()
+{
+ BOOL b_swapped = FALSE;
+ U32 count = 0;
+ while (lazyBubbleSort())
+ {
+ b_swapped = TRUE;
+ if (count++ > 0x7FFFFFFF)
+ {
+ llwarning("LLDoubleLinkedList::bubbleSort() : too many passes...", 1);
+ llwarning(" make sure the mInsertBefore(a, b) does not return TRUE for a == b", 1);
+ break;
+ }
+ }
+ return b_swapped;
+}
+
+
+// do a single bubble-sort pass and return TRUE if anything was sorted
+// leaves mQueuep pointing at last node that was swapped with its mNextp
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::lazyBubbleSort()
+{
+ // has mInsertBefore been set?
+ if (!mInsertBefore)
+ {
+ return FALSE;
+ }
+
+ // is list empty?
+ mCurrentp = mHead.mNextp;
+ if (!mCurrentp)
+ {
+ return FALSE;
+ }
+
+ BOOL b_swapped = FALSE;
+
+ // the sort will exit after 0x7FFFFFFF nodes or the end of the list, whichever is first
+ S32 length = 0x7FFFFFFF;
+ S32 count = 0;
+
+ while (mCurrentp && mCurrentp->mNextp && count<length)
+ {
+ if (mInsertBefore(mCurrentp->mNextp->mDatap, mCurrentp->mDatap))
+ {
+ b_swapped = TRUE;
+ mQueuep = mCurrentp;
+ swapCurrentWithNext(); // sets mCurrentp to mQueuep
+ }
+ count++;
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ return b_swapped;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::pushState()
+{
+ if (mStateStackDepth < LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH)
+ {
+ *(mQueuepStack + mStateStackDepth) = mQueuep;
+ *(mCurrentpStack + mStateStackDepth) = mCurrentp;
+ mStateStackDepth++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::popState()
+{
+ if (mStateStackDepth > 0)
+ {
+ mStateStackDepth--;
+ mQueuep = *(mQueuepStack + mStateStackDepth);
+ mCurrentp = *(mCurrentpStack + mStateStackDepth);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::clearStateStack()
+{
+ mStateStackDepth = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// private members
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// add node to beginning of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNode(LLDoubleLinkedNode<DATA_TYPE> *nodep)
+{
+ // add the node to the front of the list
+ nodep->mPrevp = NULL;
+ nodep->mNextp = mHead.mNextp;
+ mHead.mNextp = nodep;
+
+ // if there's something in the list, fix its back pointer
+ if (nodep->mNextp)
+ {
+ nodep->mNextp->mPrevp = nodep;
+ }
+ else // otherwise fix the tail node
+ {
+ mTail.mPrevp = nodep;
+ }
+
+ mCurrentp = mQueuep;
+}
+
+
+// add node to end of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *node)
+{
+ // add the node to the end of the list
+ node->mNextp = NULL;
+ node->mPrevp = mTail.mPrevp;
+ mTail.mPrevp = node;
+
+ // if there's something in the list, fix its back pointer
+ if (node->mPrevp)
+ {
+ node->mPrevp->mNextp = node;
+ }
+ else // otherwise fix the head node
+ {
+ mHead.mNextp = node;
+ }
+
+ mCurrentp = mQueuep;
+}
+
+
+// randomly move nodes in the list for DEBUG (or Discordian) purposes
+// sets mCurrentp and mQueuep to top of list
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::scramble()
+{
+ S32 random_number;
+ DATA_TYPE *datap = getFirstData();
+ while(datap)
+ {
+ random_number = gLindenLabRandomNumber.llrand() % 5;
+
+ if (0 == random_number)
+ {
+ removeCurrentData();
+ addData(datap);
+ }
+ else if (1 == random_number)
+ {
+ removeCurrentData();
+ addDataAtEnd(datap);
+ }
+ else if (2 == random_number)
+ {
+ swapCurrentWithPrevious();
+ }
+ else if (3 == random_number)
+ {
+ swapCurrentWithNext();
+ }
+ datap = getNextData();
+ }
+ mQueuep = mHead.mNextp;
+ mCurrentp = mQueuep;
+}
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::isEmpty()
+{
+ return (mCount == 0);
+}
+
+
+#endif
diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h
new file mode 100644
index 0000000000..8147183bec
--- /dev/null
+++ b/indra/llcommon/imageids.h
@@ -0,0 +1,79 @@
+/**
+ * @file imageids.h
+ * @brief Temporary holder for image IDs
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_IMAGEIDS_H
+#define LL_IMAGEIDS_H
+
+#include "lluuid.h"
+
+//
+// USE OF THIS FILE IS DEPRECATED
+//
+// Please use viewerart.ini and the standard
+// art import path. // indicates if file is only
+ // on dataserver, or also
+ // pre-cached on viewer
+
+// Grass Images
+//const LLUUID IMG_GRASS1 ("990c4086-46ce-49bd-8cae-afcc23a08f4e"); // dataserver
+//const LLUUID IMG_GRASS2 ("869e2dcf-21b9-402d-a36d-9a23365cf723"); // dataserver
+//const LLUUID IMG_GRASS3 ("8f97e7a7-f664-4967-9e8f-8d9e8039c1b7"); // dataserver
+
+//const LLUUID IMG_GRASS4 ("8a05131d-35b7-4812-bcfc-a989b0f954ef"); // dataserver
+
+//const LLUUID IMG_GRASS5 ("7d092acb-c69a-4122-b09b-f285e009b185"); // dataserver
+
+const LLUUID IMG_CLEAR ("11ee27f5-43c0-414e-afd5-d7f5688c351f"); // VIEWER
+const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
+
+const LLUUID IMG_DEFAULT ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
+
+//const LLUUID IMG_SAND ("0ff70ead-4562-45f9-9e8a-52b1a3286868"); // VIEWER 1.5k
+//const LLUUID IMG_GRASS ("5ab48dd5-05d0-4f1a-ace6-efd4e2fb3508"); // VIEWER 1.2k
+//const LLUUID IMG_ROCK ("402f8b24-5f9d-4905-b5f8-37baff603e88"); // VIEWER 1.2k
+//const LLUUID IMG_ROCKFACE ("9c88539c-fd04-46b8-bea2-ddf1bcffe3bd"); // VIEWER 1.2k
+const LLUUID IMG_SUN ("cce0f112-878f-4586-a2e2-a8f104bba271"); // dataserver
+const LLUUID IMG_MOON ("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver
+const LLUUID IMG_CLOUD_POOF ("fc4b9f0b-d008-45c6-96a4-01dd947ac621"); // dataserver
+const LLUUID IMG_SHOT ("35f217a3-f618-49cf-bbca-c86d486551a9"); // dataserver
+const LLUUID IMG_SPARK ("d2e75ac1-d0fb-4532-820e-a20034ac814d"); // dataserver
+const LLUUID IMG_FIRE ("aca40aa8-44cf-44ca-a0fa-93e1a2986f82"); // dataserver
+//const LLUUID IMG_WATER ("e510b068-d20d-4612-a08d-fde4d5c15789"); // VIEWER
+const LLUUID IMG_FACE_SELECT ("a85ac674-cb75-4af6-9499-df7c5aaf7a28"); // face selector
+
+//const LLUUID IMG_SHADOW ("5e1de0a8-f9f8-4237-9396-d221126a7c4a"); // dataserver
+//const LLUUID IMG_AVATARSHADOW ("c7d8bbf3-21ee-4f6e-9b20-3cf18425af1d"); // dataserver
+//const LLUUID IMG_BOXSHADOW ("8d86b8cc-4889-408a-8b72-c1961bae53d7"); // dataserver
+//const LLUUID IMG_EYE ("5e3551ae-9971-4814-af99-5117591e937b"); // dataserver
+//const LLUUID IMG_BLUE_FLAME ("d8b62059-7b31-4511-a479-1fe45117948f"); // dataserver
+
+const LLUUID IMG_DEFAULT_AVATAR ("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); // dataserver
+//const LLUUID IMG_ENERGY_BEAM ("09e7bc54-11b9-442a-ae3d-f52e599e466a"); // dataserver
+//const LLUUID IMG_ENERGY_BEAM2 ("de651394-f926-48db-b666-e49d83af1bbc"); // dataserver
+
+//const LLUUID IMG_BRICK_PATH ("a9d0019b-3783-4c7f-959c-322d301918bc"); // dataserver
+
+const LLUUID IMG_EXPLOSION ("68edcf47-ccd7-45b8-9f90-1649d7f12806"); // On dataserver
+const LLUUID IMG_EXPLOSION_2 ("21ce046c-83fe-430a-b629-c7660ac78d7c"); // On dataserver
+const LLUUID IMG_EXPLOSION_3 ("fedea30a-1be8-47a6-bc06-337a04a39c4b"); // On dataserver
+const LLUUID IMG_EXPLOSION_4 ("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); // On dataserver
+//const LLUUID IMG_EXPLOSION_5 ("60f2dec7-675b-4950-b614-85b907d552ea"); // On dataserver
+//const LLUUID IMG_SPLASH_SPRITE ("8a101f63-fe45-49e7-9f8a-e64817daa475"); // On dataserver
+const LLUUID IMG_SMOKE_POOF ("1e63e323-5fe0-452e-92f8-b98bd0f764e3"); // On dataserver
+
+const LLUUID IMG_BIG_EXPLOSION_1 ("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); // On dataserver
+const LLUUID IMG_BIG_EXPLOSION_2 ("9c8eca51-53d5-42a7-bb58-cef070395db8"); // On dataserver
+//const LLUUID IMG_BLUE_BLOOD ("8bc2e3f8-097e-4c87-b417-b0d699d07189"); // On dataserver
+
+const LLUUID IMG_BLOOM1 ("3c59f7fe-9dc8-47f9-8aaf-a9dd1fbc3bef");
+//const LLUUID IMG_BLOOM2 ("9fb76e81-eca0-4b6a-96e1-a6c5a685150b");
+//const LLUUID IMG_BLOOM3 ("fb1fecba-9585-415b-ad15-6e6e3d6c5479");
+
+const LLUUID IMG_PTT_SPEAKER ("89e9fc7c-0b16-457d-be4f-136270759c4d"); // On cache
+
+#endif
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
new file mode 100644
index 0000000000..b8df3cbfe7
--- /dev/null
+++ b/indra/llcommon/indra_constants.h
@@ -0,0 +1,338 @@
+/**
+ * @file indra_constants.h
+ * @brief some useful short term constants for Indra
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_INDRA_CONSTANTS_H
+#define LL_INDRA_CONSTANTS_H
+
+#include "lluuid.h"
+
+// Viewer object cache version, change if object update
+// format changes. JC
+const U32 INDRA_OBJECT_CACHE_VERSION = 11;
+
+// At 45 Hz collisions seem stable and objects seem
+// to settle down at a reasonable rate.
+// JC 3/18/2003
+const F32 HAVOK_TIMESTEP = 1.f / 45.f;
+
+const F32 COLLISION_TOLERANCE = 0.1f;
+
+// Time constants
+const U32 HOURS_PER_LINDEN_DAY = 4;
+const U32 DAYS_PER_LINDEN_YEAR = 11;
+
+const U32 SEC_PER_LINDEN_DAY = HOURS_PER_LINDEN_DAY * 60 * 60;
+const U32 SEC_PER_LINDEN_YEAR = DAYS_PER_LINDEN_YEAR * SEC_PER_LINDEN_DAY;
+
+const F32 REGION_WIDTH_METERS = 256.f;
+const S32 REGION_WIDTH_UNITS = 256;
+const U32 REGION_WIDTH_U32 = 256;
+
+// Bits for simulator performance query flags
+enum LAND_STAT_FLAGS
+{
+ STAT_FILTER_BY_PARCEL = 0x00000001,
+ STAT_FILTER_BY_OWNER = 0x00000002,
+ STAT_FILTER_BY_OBJECT = 0x00000004,
+ STAT_REQUEST_LAST_ENTRY = 0x80000000,
+};
+
+enum LAND_STAT_REPORT_TYPE
+{
+ STAT_REPORT_TOP_SCRIPTS = 0,
+ STAT_REPORT_TOP_COLLIDERS
+};
+
+const U32 STAT_FILTER_MASK = 0x1FFFFFFF;
+
+// Default maximum number of tasks/prims per region.
+const U32 MAX_TASKS_PER_REGION = 15000;
+
+const F32 MIN_AGENT_DEPTH = 0.30f;
+const F32 DEFAULT_AGENT_DEPTH = 0.45f;
+const F32 MAX_AGENT_DEPTH = 0.60f;
+
+const F32 MIN_AGENT_WIDTH = 0.40f;
+const F32 DEFAULT_AGENT_WIDTH = 0.60f;
+const F32 MAX_AGENT_WIDTH = 0.80f;
+
+const F32 MIN_AGENT_HEIGHT = 1.3f - 2.0f * COLLISION_TOLERANCE;
+const F32 DEFAULT_AGENT_HEIGHT = 1.9f;
+const F32 MAX_AGENT_HEIGHT = 2.65f - 2.0f * COLLISION_TOLERANCE;
+
+// For linked sets
+const S32 MAX_CHILDREN_PER_TASK = 255;
+const S32 MAX_CHILDREN_PER_PHYSICAL_TASK = 31;
+
+const S32 MAX_JOINTS_PER_OBJECT = 1; // limiting to 1 until Havok 2.x
+
+const char* const DEFAULT_DMZ_SPACE_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_USER_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_DATA_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_ASSET_SERVER = "http://asset.dmz.lindenlab.com:80";
+
+const char* const DEFAULT_AGNI_SPACE_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_USER_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_DATA_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_ASSET_SERVER = "http://asset.agni.lindenlab.com:80";
+
+// Information about what ports are for what services is in the wiki Name Space Ports page
+const char* const DEFAULT_LOCAL_ASSET_SERVER = "http://localhost:12041/asset/tmp";
+const char* const LOCAL_ASSET_URL_FORMAT = "http://%s:12041/asset";
+
+const U32 DEFAULT_LAUNCHER_PORT = 12029;
+const U32 DEFAULT_BIGBOARD_PORT = 12030;
+const U32 DEFAULT_QUERYSIM_PORT = 12031;
+const U32 DEFAULT_DATA_SERVER_PORT = 12032;
+const U32 DEFAULT_SPACE_SERVER_PORT = 12033;
+const U32 DEFAULT_VIEWER_PORT = 12034;
+const U32 DEFAULT_SIMULATOR_PORT = 12035;
+const U32 DEFAULT_USER_SERVER_PORT = 12036;
+const U32 DEFAULT_RPC_SERVER_PORT = 12037;
+const U32 DEFAULT_LOG_DATA_SERVER_PORT = 12039;
+const U32 DEFAULT_BACKBONE_PORT = 12040;
+const U32 DEFAULT_LOCAL_ASSET_PORT = 12041;
+const U32 DEFAULT_BACKBONE_CAP_PORT = 12042;
+
+// For automatic port discovery when running multiple viewers on one host
+const U32 PORT_DISCOVERY_RANGE_MIN = 13000;
+const U32 PORT_DISCOVERY_RANGE_MAX = PORT_DISCOVERY_RANGE_MIN + 50;
+
+const char LAND_LAYER_CODE = 'L';
+const char WATER_LAYER_CODE = 'W';
+const char WIND_LAYER_CODE = '7';
+const char CLOUD_LAYER_CODE = '8';
+
+// keys
+// Bit masks for various keyboard modifier keys.
+const MASK MASK_NONE = 0x0000;
+const MASK MASK_CONTROL = 0x0001;
+const MASK MASK_ALT = 0x0002;
+const MASK MASK_SHIFT = 0x0004;
+
+// Special keys go into >128
+const KEY KEY_SPECIAL = 0x80; // special keys start here
+const KEY KEY_RETURN = 0x81;
+const KEY KEY_LEFT = 0x82;
+const KEY KEY_RIGHT = 0x83;
+const KEY KEY_UP = 0x84;
+const KEY KEY_DOWN = 0x85;
+const KEY KEY_ESCAPE = 0x86;
+const KEY KEY_BACKSPACE =0x87;
+const KEY KEY_DELETE = 0x88;
+const KEY KEY_SHIFT = 0x89;
+const KEY KEY_CONTROL = 0x8A;
+const KEY KEY_ALT = 0x8B;
+const KEY KEY_HOME = 0x8C;
+const KEY KEY_END = 0x8D;
+const KEY KEY_PAGE_UP = 0x8E;
+const KEY KEY_PAGE_DOWN = 0x8F;
+const KEY KEY_HYPHEN = 0x90;
+const KEY KEY_EQUALS = 0x91;
+const KEY KEY_INSERT = 0x92;
+const KEY KEY_CAPSLOCK = 0x93;
+const KEY KEY_TAB = 0x94;
+const KEY KEY_ADD = 0x95;
+const KEY KEY_SUBTRACT =0x96;
+const KEY KEY_MULTIPLY =0x97;
+const KEY KEY_DIVIDE = 0x98;
+const KEY KEY_F1 = 0xA1;
+const KEY KEY_F2 = 0xA2;
+const KEY KEY_F3 = 0xA3;
+const KEY KEY_F4 = 0xA4;
+const KEY KEY_F5 = 0xA5;
+const KEY KEY_F6 = 0xA6;
+const KEY KEY_F7 = 0xA7;
+const KEY KEY_F8 = 0xA8;
+const KEY KEY_F9 = 0xA9;
+const KEY KEY_F10 = 0xAA;
+const KEY KEY_F11 = 0xAB;
+const KEY KEY_F12 = 0xAC;
+
+const KEY KEY_PAD_UP = 0xC0;
+const KEY KEY_PAD_DOWN = 0xC1;
+const KEY KEY_PAD_LEFT = 0xC2;
+const KEY KEY_PAD_RIGHT = 0xC3;
+const KEY KEY_PAD_HOME = 0xC4;
+const KEY KEY_PAD_END = 0xC5;
+const KEY KEY_PAD_PGUP = 0xC6;
+const KEY KEY_PAD_PGDN = 0xC7;
+const KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle
+const KEY KEY_PAD_INS = 0xC9;
+const KEY KEY_PAD_DEL = 0xCA;
+const KEY KEY_PAD_RETURN = 0xCB;
+const KEY KEY_PAD_ADD = 0xCC; // not used
+const KEY KEY_PAD_SUBTRACT = 0xCD; // not used
+const KEY KEY_PAD_MULTIPLY = 0xCE; // not used
+const KEY KEY_PAD_DIVIDE = 0xCF; // not used
+
+const KEY KEY_BUTTON0 = 0xD0;
+const KEY KEY_BUTTON1 = 0xD1;
+const KEY KEY_BUTTON2 = 0xD2;
+const KEY KEY_BUTTON3 = 0xD3;
+const KEY KEY_BUTTON4 = 0xD4;
+const KEY KEY_BUTTON5 = 0xD5;
+const KEY KEY_BUTTON6 = 0xD6;
+const KEY KEY_BUTTON7 = 0xD7;
+const KEY KEY_BUTTON8 = 0xD8;
+const KEY KEY_BUTTON9 = 0xD9;
+const KEY KEY_BUTTON10 = 0xDA;
+const KEY KEY_BUTTON11 = 0xDB;
+const KEY KEY_BUTTON12 = 0xDC;
+const KEY KEY_BUTTON13 = 0xDD;
+const KEY KEY_BUTTON14 = 0xDE;
+const KEY KEY_BUTTON15 = 0xDF;
+
+const KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only.
+
+const S32 KEY_COUNT = 256;
+
+
+const F32 DEFAULT_WATER_HEIGHT = 20.0f;
+
+// Maturity ratings for simulators
+const U8 SIM_ACCESS_MIN = 0;
+const U8 SIM_ACCESS_TRIAL = 7;
+const U8 SIM_ACCESS_PG = 13;
+const U8 SIM_ACCESS_MATURE = 21;
+const U8 SIM_ACCESS_DOWN = 254;
+const U8 SIM_ACCESS_MAX = SIM_ACCESS_MATURE;
+
+// group constants
+const S32 MAX_AGENT_GROUPS = 25;
+
+// god levels
+const U8 GOD_MAINTENANCE = 250;
+const U8 GOD_FULL = 200;
+const U8 GOD_LIAISON = 150;
+const U8 GOD_CUSTOMER_SERVICE = 100;
+const U8 GOD_LIKE = 1;
+const U8 GOD_NOT = 0;
+
+// "agent id" for things that should be done to ALL agents
+const LLUUID LL_UUID_ALL_AGENTS("44e87126-e794-4ded-05b3-7c42da3d5cdb");
+
+// Governor Linden's agent id.
+const LLUUID GOVERNOR_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1");
+const LLUUID REALESTATE_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1");
+// Maintenance's group id.
+const LLUUID MAINTENANCE_GROUP_ID("dc7b21cd-3c89-fcaa-31c8-25f9ffd224cd");
+
+// Flags for kick message
+const U32 KICK_FLAGS_DEFAULT = 0x0;
+const U32 KICK_FLAGS_FREEZE = 1 << 0;
+const U32 KICK_FLAGS_UNFREEZE = 1 << 1;
+
+const U8 UPD_NONE = 0x00;
+const U8 UPD_POSITION = 0x01;
+const U8 UPD_ROTATION = 0x02;
+const U8 UPD_SCALE = 0x04;
+const U8 UPD_LINKED_SETS = 0x08;
+const U8 UPD_UNIFORM = 0x10; // used with UPD_SCALE
+
+// Agent Update Flags (U8)
+const U8 AU_FLAGS_NONE = 0x00;
+const U8 AU_FLAGS_HIDETITLE = 0x01;
+
+// start location constants
+const U32 START_LOCATION_ID_LAST = 0;
+const U32 START_LOCATION_ID_HOME = 1;
+const U32 START_LOCATION_ID_DIRECT = 2; // for direct teleport
+const U32 START_LOCATION_ID_PARCEL = 3; // for teleports to a parcel
+const U32 START_LOCATION_ID_TELEHUB = 4; // for teleports to a spawnpoint
+const U32 START_LOCATION_ID_URL = 5;
+const U32 START_LOCATION_ID_COUNT = 6;
+
+// group constants
+const U32 GROUP_MIN_SIZE = 2;
+
+// radius within which a chat message is fully audible
+const F32 CHAT_WHISPER_RADIUS = 10.f;
+const F32 CHAT_NORMAL_RADIUS = 20.f;
+const F32 CHAT_SHOUT_RADIUS = 100.f;
+const F32 CHAT_MAX_RADIUS = CHAT_SHOUT_RADIUS;
+const F32 CHAT_MAX_RADIUS_BY_TWO = CHAT_MAX_RADIUS / 2.f;
+
+// this times above gives barely audible radius
+const F32 CHAT_BARELY_AUDIBLE_FACTOR = 2.0f;
+
+// distance in front of speaking agent the sphere is centered
+const F32 CHAT_WHISPER_OFFSET = 5.f;
+const F32 CHAT_NORMAL_OFFSET = 10.f;
+const F32 CHAT_SHOUT_OFFSET = 50.f;
+
+// first clean starts at 3 AM
+const S32 SANDBOX_FIRST_CLEAN_HOUR = 3;
+// clean every <n> hours
+const S32 SANDBOX_CLEAN_FREQ = 12;
+
+const F32 WIND_SCALE_HACK = 2.0f; // hack to make wind speeds more realistic
+
+enum ETerrainBrushType
+{
+ // the valid brush numbers cannot be reordered, because they
+ // are used in the binary LSL format as arguments to llModifyLand()
+ E_LANDBRUSH_LEVEL = 0,
+ E_LANDBRUSH_RAISE = 1,
+ E_LANDBRUSH_LOWER = 2,
+ E_LANDBRUSH_SMOOTH = 3,
+ E_LANDBRUSH_NOISE = 4,
+ E_LANDBRUSH_REVERT = 5,
+ E_LANDBRUSH_INVALID = 6
+};
+
+// media commands
+const U32 PARCEL_MEDIA_COMMAND_STOP = 0;
+const U32 PARCEL_MEDIA_COMMAND_PAUSE = 1;
+const U32 PARCEL_MEDIA_COMMAND_PLAY = 2;
+const U32 PARCEL_MEDIA_COMMAND_LOOP = 3;
+const U32 PARCEL_MEDIA_COMMAND_TEXTURE = 4;
+const U32 PARCEL_MEDIA_COMMAND_URL = 5;
+const U32 PARCEL_MEDIA_COMMAND_TIME = 6;
+const U32 PARCEL_MEDIA_COMMAND_AGENT = 7;
+const U32 PARCEL_MEDIA_COMMAND_UNLOAD = 8;
+const U32 PARCEL_MEDIA_COMMAND_AUTO_ALIGN = 9;
+
+// map item types
+const U32 MAP_ITEM_TELEHUB = 0x01;
+const U32 MAP_ITEM_PG_EVENT = 0x02;
+const U32 MAP_ITEM_MATURE_EVENT = 0x03;
+const U32 MAP_ITEM_POPULAR = 0x04;
+const U32 MAP_ITEM_AGENT_COUNT = 0x05;
+const U32 MAP_ITEM_AGENT_LOCATIONS = 0x06;
+const U32 MAP_ITEM_LAND_FOR_SALE = 0x07;
+const U32 MAP_ITEM_CLASSIFIED = 0x08;
+
+// Crash reporter behavior
+const char* const CRASH_SETTINGS_FILE = "crash_settings.xml";
+const char* const CRASH_BEHAVIOR_SETTING = "CrashBehavior";
+const S32 CRASH_BEHAVIOR_ASK = 0;
+const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1;
+const S32 CRASH_BEHAVIOR_NEVER_SEND = 2;
+
+// Export/Import return values
+const S32 EXPORT_SUCCESS = 0;
+const S32 EXPORT_ERROR_PERMISSIONS = -1;
+const S32 EXPORT_ERROR_UNKNOWN = -2;
+
+// This is how long the sim will try to teleport you before giving up.
+const F32 TELEPORT_EXPIRY = 15.0f;
+// Additional time (in seconds) to wait per attachment
+const F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f;
+
+// The maximum size of an object extra parameters binary (packed) block
+#define MAX_OBJECT_PARAMS_SIZE 1024
+
+const S32 CHAT_CHANNEL_DEBUG = S32_MAX;
+
+// PLEASE don't add constants here. Every dev will have to do
+// a complete rebuild. Try to find another shared header file,
+// like llregionflags.h, lllslconstants.h, llagentconstants.h,
+// or create a new one. JC
+
+#endif
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
new file mode 100644
index 0000000000..199d380809
--- /dev/null
+++ b/indra/llcommon/linden_common.h
@@ -0,0 +1,53 @@
+/**
+ * @file linden_common.h
+ * @brief Includes common headers that are always safe to include
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LINDEN_COMMON_H
+#define LL_LINDEN_COMMON_H
+
+#include "llpreprocessor.h"
+
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// Work around stupid Microsoft STL warning
+#ifdef LL_WINDOWS
+#pragma warning (disable : 4702) // warning C4702: unreachable code
+#endif // LL_WINDOWS
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include "llfile.h"
+
+#if LL_WINDOWS
+// Limit Windows API to small and manageable set.
+// If you get undefined symbols, find the appropriate
+// Windows header file and include that in your .cpp file.
+// Please don't take this out -- it helps with library
+// compile times. JC
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#endif // LL_WINDOWS
+
+#include "stdtypes.h"
+#include "lldefs.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "lltimer.h"
+#include "llfasttimer.h"
+#include "llsys.h"
+
+#ifdef LL_WINDOWS
+#pragma warning (3 : 4702) // we like level 3, not 4
+#endif // LL_WINDOWS
+
+#endif // not LL_LINDEN_COMMON_H
diff --git a/indra/llcommon/linked_lists.h b/indra/llcommon/linked_lists.h
new file mode 100644
index 0000000000..279b742e9f
--- /dev/null
+++ b/indra/llcommon/linked_lists.h
@@ -0,0 +1,919 @@
+/**
+ * @file linked_lists.h
+ * @brief LLLinkedList class header amd implementation file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LINKED_LISTS_H
+#define LL_LINKED_LISTS_H
+
+/**
+ * Provides a standard doubly linked list for fun and profit
+ * Utilizes a neat trick off of Flipcode where the back pointer is a
+ * pointer to a pointer, allowing easier transfer of nodes between lists, &c
+ * And a template class, of course
+ */
+
+#include "llerror.h"
+
+
+template <class DATA_TYPE> class LLLinkedList
+{
+public:
+ friend class LLLinkNode;
+ // External interface
+
+ // basic constructor
+ LLLinkedList() : mHead(NULL), mCurrentp(NULL), mInsertBefore(NULL)
+ {
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+ }
+
+ // basic constructor
+ LLLinkedList(BOOL (*insert_before)(DATA_TYPE *data_new, DATA_TYPE *data_tested)) : mHead(NULL), mCurrentp(NULL), mInsertBefore(insert_before)
+ {
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+ }
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLLinkedList()
+ {
+ removeAllNodes();
+ }
+
+ // set mInsertBefore
+ void setInsertBefore(BOOL (*insert_before)(DATA_TYPE *data_new, DATA_TYPE *data_tested))
+ {
+ mInsertBefore = insert_before;
+ }
+
+ //
+ // WARNING!!!!!!!
+ // addData and addDataSorted are NOT O(1) operations, but O(n) because they check
+ // for existence of the data in the linked list first. Why, I don't know - djs
+ // If you don't care about dupes, use addDataNoCheck
+ //
+
+ // put data into a node and stick it at the front of the list
+ inline BOOL addData(DATA_TYPE *data);
+
+ // put data into a node and sort into list by mInsertBefore()
+ // calls normal add if mInsertBefore isn't set
+ inline BOOL addDataSorted(DATA_TYPE *data);
+
+ inline BOOL addDataNoCheck(DATA_TYPE *data);
+
+ // bubbleSortList
+ // does an improved bubble sort of the list . . . works best with almost sorted data
+ // does nothing if mInsertBefore isn't set
+ // Nota Bene: Swaps are accomplished by swapping data pointers
+ inline void bubbleSortList();
+
+ // put data into a node and stick it at the end of the list
+ inline BOOL addDataAtEnd(DATA_TYPE *data);
+
+ // returns number of items in the list
+ inline S32 getLength() const;
+
+ inline BOOL isEmpty();
+
+ // search the list starting at mHead.mNextp and remove the link with mDatap == data
+ // leave mCurrentp and mCurrentOperatingp on the next entry
+ // return TRUE if found, FALSE if not found
+ inline BOOL removeData(DATA_TYPE *data);
+
+ // search the list starting at mHead.mNextp and delete the link with mDatap == data
+ // leave mCurrentp and mCurrentOperatingp on the next entry
+ // return TRUE if found, FALSE if not found
+ inline BOOL deleteData(DATA_TYPE *data);
+
+ // remove all nodes from the list and delete the associated data
+ inline void deleteAllData();
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ // check to see if data is in list
+ // if TRUE then mCurrentp and mCurrentOperatingp point to data
+ inline BOOL checkData(DATA_TYPE *data);
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE *getNextData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getFirstData();
+
+ // reset the list and return the data at position n, set mCurentOperatingp to that node and bump mCurrentp
+ // Note: n is zero-based
+ inline DATA_TYPE *getNthData( U32 n);
+
+ // reset the list and return the last data in it, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getLastData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // remove the Node at mCurentOperatingp and add it to newlist
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void moveCurrentData(LLLinkedList *newlist, BOOL b_sort);
+
+ BOOL moveData(DATA_TYPE *data, LLLinkedList *newlist, BOOL b_sort);
+
+ // delete the Node at mCurentOperatingp
+ // leave mCurrentp anf mCurentOperatingp on the next entry
+ void deleteCurrentData();
+
+private:
+ // node that actually contains the data
+ class LLLinkNode
+ {
+ public:
+ // assign the mDatap pointer
+ LLLinkNode(DATA_TYPE *data) : mDatap(data), mNextp(NULL), mPrevpp(NULL)
+ {
+ }
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLLinkNode()
+ {
+ if (mDatap)
+ {
+ llerror("Attempting to call LLLinkNode destructor with a non-null mDatap!", 1);
+ }
+ }
+
+ // delete associated data and NULL out pointer
+ void deleteData()
+ {
+ delete mDatap;
+ mDatap = NULL;
+ }
+
+ // NULL out pointer
+ void removeData()
+ {
+ mDatap = NULL;
+ }
+
+ DATA_TYPE *mDatap;
+ LLLinkNode *mNextp;
+ LLLinkNode **mPrevpp;
+ };
+
+ // add a node at the front of the list
+ void addData(LLLinkNode *node)
+ {
+ // don't allow NULL to be passed to addData
+ if (!node)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ // add the node to the front of the list
+ node->mPrevpp = &mHead.mNextp;
+ node->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (node->mNextp)
+ {
+ node->mNextp->mPrevpp = &node->mNextp;
+ }
+
+ mHead.mNextp = node;
+ }
+
+ LLLinkNode mHead; // fake head node. . . makes pointer operations faster and easier
+ LLLinkNode *mCurrentp; // mCurrentp is the Node that getCurrentData returns
+ LLLinkNode *mCurrentOperatingp; // this is the node that the various mumbleCurrentData functions act on
+ BOOL (*mInsertBefore)(DATA_TYPE *data_new, DATA_TYPE *data_tested); // user function set to allow sorted lists
+ U32 mCount;
+};
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addData(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ if ( checkData(data))
+ {
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the front of the list
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevpp = &temp->mNextp;
+ }
+
+ mHead.mNextp = temp;
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataNoCheck(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the front of the list
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevpp = &temp->mNextp;
+ }
+
+ mHead.mNextp = temp;
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataSorted(DATA_TYPE *data)
+{
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addDataSorted", 0);
+ }
+
+ if (checkData(data))
+ {
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // mInsertBefore not set?
+ if (!mInsertBefore)
+ {
+ addData(data);
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // empty list?
+ if (!mHead.mNextp)
+ {
+ addData(data);
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return TRUE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // walk the list until mInsertBefore returns true
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp->mNextp)
+ {
+ if (mInsertBefore(data, mCurrentp->mDatap))
+ {
+ // insert before the current one
+ temp->mPrevpp = mCurrentp->mPrevpp;
+ temp->mNextp = mCurrentp;
+ *(temp->mPrevpp) = temp;
+ mCurrentp->mPrevpp = &temp->mNextp;
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+ }
+ else
+ {
+ mCurrentp = mCurrentp->mNextp;
+ }
+ }
+
+ // on the last element, add before?
+ if (mInsertBefore(data, mCurrentp->mDatap))
+ {
+ // insert before the current one
+ temp->mPrevpp = mCurrentp->mPrevpp;
+ temp->mNextp = mCurrentp;
+ *(temp->mPrevpp) = temp;
+ mCurrentp->mPrevpp = &temp->mNextp;
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ }
+ else // insert after
+ {
+ temp->mPrevpp = &mCurrentp->mNextp;
+ temp->mNextp = NULL;
+ mCurrentp->mNextp = temp;
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ }
+ mCount++;
+ return TRUE;
+}
+
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::bubbleSortList()
+{
+ // mInsertBefore not set
+ if (!mInsertBefore)
+ {
+ return;
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ BOOL b_swapped = FALSE;
+ DATA_TYPE *temp;
+
+ // Nota Bene: This will break if more than 0x7FFFFFFF members in list!
+ S32 length = 0x7FFFFFFF;
+ S32 count = 0;
+ do
+ {
+ b_swapped = FALSE;
+ mCurrentp = mHead.mNextp;
+ count = 0;
+ while ( (count + 1 < length)
+ &&(mCurrentp))
+ {
+ if (mCurrentp->mNextp)
+ {
+ if (!mInsertBefore(mCurrentp->mDatap, mCurrentp->mNextp->mDatap))
+ {
+ // swap data pointers!
+ temp = mCurrentp->mDatap;
+ mCurrentp->mDatap = mCurrentp->mNextp->mDatap;
+ mCurrentp->mNextp->mDatap = temp;
+ b_swapped = TRUE;
+ }
+ }
+ else
+ {
+ break;
+ }
+ count++;
+ mCurrentp = mCurrentp->mNextp;
+ }
+ length = count;
+ } while (b_swapped);
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataAtEnd(DATA_TYPE *data)
+{
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ if (checkData(data))
+ {
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the end of the list
+
+ // if empty, add to the front and be done with it
+ if (!mHead.mNextp)
+ {
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = NULL;
+ mHead.mNextp = temp;
+ }
+ else
+ {
+ // otherwise, walk to the end of the list
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp->mNextp)
+ {
+ mCurrentp = mCurrentp->mNextp;
+ }
+ temp->mPrevpp = &mCurrentp->mNextp;
+ temp->mNextp = NULL;
+ mCurrentp->mNextp = temp;
+ }
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+// returns number of items in the list
+template <class DATA_TYPE>
+S32 LLLinkedList<DATA_TYPE>::getLength() const
+{
+// S32 length = 0;
+// for (LLLinkNode* temp = mHead.mNextp; temp != NULL; temp = temp->mNextp)
+// {
+// length++;
+// }
+ return mCount;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::isEmpty()
+{
+ return (mCount == 0);
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// leave mCurrentp and mCurrentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::removeData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+
+ // if we were on the one we want to delete, bump the cached copies
+ if (mCurrentOperatingp == tcurrop)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+ else if (mCurrentOperatingp == tcurr)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->removeData();
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+// search the list starting at mHead.mNextp and delete the link with mDatap == data
+// leave mCurrentp and mCurrentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::deleteData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // delete the LLLinkNode
+ // if we were on the one we want to delete, bump the cached copies
+ if (mCurrentOperatingp == tcurrop)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ // and delete the associated data
+ llassert(mCurrentOperatingp);
+ mCurrentp = mCurrentOperatingp->mNextp;
+ mCurrentOperatingp->deleteData();
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+ // remove all nodes from the list and delete the associated data
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::deleteAllData()
+{
+ LLLinkNode *temp;
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mNextp;
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+}
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::removeAllNodes()
+{
+ LLLinkNode *temp;
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mNextp;
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+}
+
+// check to see if data is in list
+// if TRUE then mCurrentp and mCurrentOperatingp point to data
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::checkData(DATA_TYPE *data)
+{
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ mCurrentOperatingp = mCurrentp;
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+ mCurrentOperatingp = mCurrentp;
+ return FALSE;
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::resetList()
+{
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getFirstData()
+{
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Note: n is zero-based
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getNthData( U32 n )
+{
+ mCurrentOperatingp = mHead.mNextp;
+
+ // if empty, return NULL
+ if (!mCurrentOperatingp)
+ {
+ return NULL;
+ }
+
+ for( U32 i = 0; i < n; i++ )
+ {
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ if( !mCurrentOperatingp )
+ {
+ return NULL;
+ }
+ }
+
+ mCurrentp = mCurrentOperatingp->mNextp;
+ return mCurrentOperatingp->mDatap;
+}
+
+
+// reset the list and return the last data in it, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getLastData()
+{
+ mCurrentOperatingp = mHead.mNextp;
+
+ // if empty, return NULL
+ if (!mCurrentOperatingp)
+ return NULL;
+
+ // walk until we're pointing at the last entry
+ while (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ mCurrentp = mCurrentOperatingp->mNextp;
+ return mCurrentOperatingp->mDatap;
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->removeData();
+ delete mCurrentOperatingp;
+ mCount--;
+ mCurrentOperatingp = mCurrentp;
+ }
+}
+
+// remove the Node at mCurentOperatingp and add it to newlist
+// leave mCurrentp and mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::moveCurrentData(LLLinkedList *newlist, BOOL b_sort)
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+ // move the node to the new list
+ newlist->addData(mCurrentOperatingp);
+ if (b_sort)
+ bubbleSortList();
+ mCurrentOperatingp = mCurrentp;
+ }
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::moveData(DATA_TYPE *data, LLLinkedList *newlist, BOOL b_sort)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // if we were on the one we want to delete, bump the cached copies
+ if ( (mCurrentOperatingp == tcurrop)
+ ||(mCurrentOperatingp == tcurr))
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+ // move the node to the new list
+ newlist->addData(mCurrentOperatingp);
+ if (b_sort)
+ newlist->bubbleSortList();
+ mCurrentOperatingp = mCurrentp;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+// delete the Node at mCurentOperatingp
+// leave mCurrentp anf mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->deleteData();
+ if (mCurrentOperatingp->mDatap)
+ llerror("This is impossible!", 0);
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llagentconstants.h b/indra/llcommon/llagentconstants.h
new file mode 100644
index 0000000000..66b4b564bf
--- /dev/null
+++ b/indra/llcommon/llagentconstants.h
@@ -0,0 +1,141 @@
+/**
+ * @file llagentconstants.h
+ * @author James Cook, Andrew Meadows, Richard Nelson
+ * @brief Shared constants through the system for agents.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTCONSTANTS_H
+#define LL_LLAGENTCONSTANTS_H
+
+const U32 CONTROL_AT_POS_INDEX = 0;
+const U32 CONTROL_AT_NEG_INDEX = 1;
+const U32 CONTROL_LEFT_POS_INDEX = 2;
+const U32 CONTROL_LEFT_NEG_INDEX = 3;
+const U32 CONTROL_UP_POS_INDEX = 4;
+const U32 CONTROL_UP_NEG_INDEX = 5;
+const U32 CONTROL_PITCH_POS_INDEX = 6;
+const U32 CONTROL_PITCH_NEG_INDEX = 7;
+const U32 CONTROL_YAW_POS_INDEX = 8;
+const U32 CONTROL_YAW_NEG_INDEX = 9;
+const U32 CONTROL_FAST_AT_INDEX = 10;
+const U32 CONTROL_FAST_LEFT_INDEX = 11;
+const U32 CONTROL_FAST_UP_INDEX = 12;
+const U32 CONTROL_FLY_INDEX = 13;
+const U32 CONTROL_STOP_INDEX = 14;
+const U32 CONTROL_FINISH_ANIM_INDEX = 15;
+const U32 CONTROL_STAND_UP_INDEX = 16;
+const U32 CONTROL_SIT_ON_GROUND_INDEX = 17;
+const U32 CONTROL_MOUSELOOK_INDEX = 18;
+const U32 CONTROL_NUDGE_AT_POS_INDEX = 19;
+const U32 CONTROL_NUDGE_AT_NEG_INDEX = 20;
+const U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21;
+const U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
+const U32 CONTROL_NUDGE_UP_POS_INDEX = 23;
+const U32 CONTROL_NUDGE_UP_NEG_INDEX = 24;
+const U32 CONTROL_TURN_LEFT_INDEX = 25;
+const U32 CONTROL_TURN_RIGHT_INDEX = 26;
+const U32 CONTROL_AWAY_INDEX = 27;
+const U32 CONTROL_LBUTTON_DOWN_INDEX = 28;
+const U32 CONTROL_LBUTTON_UP_INDEX = 29;
+const U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
+const U32 CONTROL_ML_LBUTTON_UP_INDEX = 31;
+const U32 TOTAL_CONTROLS = 32;
+
+const U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX;
+const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX;
+const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX;
+const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX;
+const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX;
+const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX;
+const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX;
+const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX;
+const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX;
+const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX;
+
+const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX;
+const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX;
+const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX;
+
+const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX;
+const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX;
+const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX;
+const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX;
+const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX;
+const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX;
+
+const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX;
+const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX;
+const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX;
+const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX;
+const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX;
+
+const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX;
+
+const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX;
+const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX;
+const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX;
+const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX;
+
+const U32 AGENT_CONTROL_AT = AGENT_CONTROL_AT_POS
+ | AGENT_CONTROL_AT_NEG
+ | AGENT_CONTROL_NUDGE_AT_POS
+ | AGENT_CONTROL_NUDGE_AT_NEG;
+
+const U32 AGENT_CONTROL_LEFT = AGENT_CONTROL_LEFT_POS
+ | AGENT_CONTROL_LEFT_NEG
+ | AGENT_CONTROL_NUDGE_LEFT_POS
+ | AGENT_CONTROL_NUDGE_LEFT_NEG;
+
+const U32 AGENT_CONTROL_UP = AGENT_CONTROL_UP_POS
+ | AGENT_CONTROL_UP_NEG
+ | AGENT_CONTROL_NUDGE_UP_POS
+ | AGENT_CONTROL_NUDGE_UP_NEG;
+
+const U32 AGENT_CONTROL_HORIZONTAL = AGENT_CONTROL_AT
+ | AGENT_CONTROL_LEFT;
+
+const U32 AGENT_CONTROL_NOT_USED_BY_LSL = AGENT_CONTROL_FLY
+ | AGENT_CONTROL_STOP
+ | AGENT_CONTROL_FINISH_ANIM
+ | AGENT_CONTROL_STAND_UP
+ | AGENT_CONTROL_SIT_ON_GROUND
+ | AGENT_CONTROL_MOUSELOOK
+ | AGENT_CONTROL_AWAY;
+
+const U32 AGENT_CONTROL_MOVEMENT = AGENT_CONTROL_AT
+ | AGENT_CONTROL_LEFT
+ | AGENT_CONTROL_UP;
+
+const U32 AGENT_CONTROL_ROTATION = AGENT_CONTROL_PITCH_POS
+ | AGENT_CONTROL_PITCH_NEG
+ | AGENT_CONTROL_YAW_POS
+ | AGENT_CONTROL_YAW_NEG;
+
+const U32 AGENT_CONTROL_NUDGE = AGENT_CONTROL_NUDGE_AT_POS
+ | AGENT_CONTROL_NUDGE_AT_NEG
+ | AGENT_CONTROL_NUDGE_LEFT_POS
+ | AGENT_CONTROL_NUDGE_LEFT_NEG;
+
+
+// move these up so that we can hide them in "State" for object updates
+// (for now)
+const U32 AGENT_ATTACH_OFFSET = 4;
+const U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET;
+const U32 AGENT_ATTACH_CLEAR = 0x00;
+
+// RN: this method swaps the upper and lower nibbles to maintain backward
+// compatibility with old objects that only used the upper nibble
+#define ATTACHMENT_ID_FROM_STATE(state) ((S32)((((U8)state & AGENT_ATTACH_MASK) >> 4) | (((U8)state & ~AGENT_ATTACH_MASK) << 4)))
+
+// test state for use in testing grabbing the camera
+const U32 AGENT_CAMERA_OBJECT = 0x1 << 3;
+
+const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters?
+
+#endif
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
new file mode 100644
index 0000000000..50648a2d30
--- /dev/null
+++ b/indra/llcommon/llapp.cpp
@@ -0,0 +1,616 @@
+/**
+ * @file llapp.cpp
+ * @brief Implementation of the LLApp class.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapp.h"
+
+#include "llcommon.h"
+#include "llapr.h"
+#include "llerrorthread.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+//
+// Signal handling
+//
+// Windows uses structured exceptions, so it's handled a bit differently.
+//
+#if LL_WINDOWS
+LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
+#else
+#include <unistd.h> // for fork()
+void setup_signals();
+void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+const S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
+#endif
+
+// the static application instance
+LLApp* LLApp::sApplication = NULL;
+
+// Local flag for whether or not to do logging in signal handlers.
+//static
+BOOL LLApp::sLogInSignal = FALSE;
+
+// static
+LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
+LLAppErrorHandler LLApp::sErrorHandler = NULL;
+BOOL LLApp::sErrorThreadRunning = FALSE;
+#if !LL_WINDOWS
+LLApp::child_map LLApp::sChildMap;
+LLAtomicU32* LLApp::sSigChildCount = NULL;
+LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
+#endif
+
+
+LLApp::LLApp() : mThreadErrorp(NULL)
+{
+ // Set our status to running
+ setStatus(APP_STATUS_RUNNING);
+
+ LLCommon::initClass();
+
+#if !LL_WINDOWS
+ // This must be initialized before the error handler.
+ sSigChildCount = new LLAtomicU32(0);
+#endif
+
+ // Setup error handling
+ setupErrorHandling();
+
+ // initialize the options structure. We need to make this an array
+ // because the structured data will not auto-allocate if we
+ // reference an invalid location with the [] operator.
+ mOptions = LLSD::emptyArray();
+ LLSD sd;
+ for(int i = 0; i < PRIORITY_COUNT; ++i)
+ {
+ mOptions.append(sd);
+ }
+
+ // Make sure we clean up APR when we exit
+ // Don't need to do this if we're cleaning up APR in the destructor
+ //atexit(ll_cleanup_apr);
+
+ // Set the application to this instance.
+ sApplication = this;
+}
+
+
+LLApp::~LLApp()
+{
+#if !LL_WINDOWS
+ delete sSigChildCount;
+ sSigChildCount = NULL;
+#endif
+ setStopped();
+ // HACK: wait for the error thread to clean itself
+ ms_sleep(20);
+ if (mThreadErrorp)
+ {
+ delete mThreadErrorp;
+ mThreadErrorp = NULL;
+ }
+
+ LLCommon::cleanupClass();
+}
+
+// static
+LLApp* LLApp::instance()
+{
+ return sApplication;
+}
+
+
+LLSD LLApp::getOption(const std::string& name) const
+{
+ LLSD rv;
+ LLSD::array_const_iterator iter = mOptions.beginArray();
+ LLSD::array_const_iterator end = mOptions.endArray();
+ for(; iter != end; ++iter)
+ {
+ rv = (*iter)[name];
+ if(rv.isDefined()) break;
+ }
+ return rv;
+}
+
+bool LLApp::parseCommandOptions(int argc, char** argv)
+{
+ LLSD commands;
+ std::string name;
+ std::string value;
+ for(int ii = 1; ii < argc; ++ii)
+ {
+ if(argv[ii][0] != '-')
+ {
+ llinfos << "Did not find option identifier while parsing token: "
+ << argv[ii] << llendl;
+ return false;
+ }
+ int offset = 1;
+ if(argv[ii][1] == '-') ++offset;
+ name.assign(&argv[ii][offset]);
+ if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
+ {
+ // we found another option after this one or we have
+ // reached the end. simply record that this option was
+ // found and continue.
+ commands[name] = true;
+ continue;
+ }
+ ++ii;
+ value.assign(argv[ii]);
+ commands[name] = value;
+ }
+ setOptionData(PRIORITY_COMMAND_LINE, commands);
+ return true;
+}
+
+bool LLApp::setOptionData(OptionPriority level, LLSD data)
+{
+ if((level < 0)
+ || (level >= PRIORITY_COUNT)
+ || (data.type() != LLSD::TypeMap))
+ {
+ return false;
+ }
+ mOptions[level] = data;
+ return true;
+}
+
+LLSD LLApp::getOptionData(OptionPriority level)
+{
+ if((level < 0) || (level >= PRIORITY_COUNT))
+ {
+ return LLSD();
+ }
+ return mOptions[level];
+}
+
+void LLApp::stepFrame()
+{
+ // Update the static frame timer.
+ LLFrameTimer::updateFrameTime();
+
+ // Run ready runnables
+ mRunner.run();
+}
+
+
+void LLApp::setupErrorHandling()
+{
+ // Error handling is done by starting up an error handling thread, which just sleeps and
+ // occasionally checks to see if the app is in an error state, and sees if it needs to be run.
+
+#if LL_WINDOWS
+ // Windows doesn't have the same signal handling mechanisms as UNIX, thus APR doesn't provide
+ // a signal handling thread implementation.
+ // What we do is install an unhandled exception handler, which will try to do the right thing
+ // in the case of an error (generate a minidump)
+
+ // Disable this until the viewer gets ported so server crashes can be JIT debugged.
+ //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+ //prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
+#else
+ //
+ // Start up signal handling.
+ //
+ // There are two different classes of signals. Synchronous signals are delivered to a specific
+ // thread, asynchronous signals can be delivered to any thread (in theory)
+ //
+
+ setup_signals();
+
+#endif
+
+ //
+ // Start the error handling thread, which is responsible for taking action
+ // when the app goes into the APP_STATUS_ERROR state
+ //
+ llinfos << "LLApp::setupErrorHandling - Starting error thread" << llendl;
+ mThreadErrorp = new LLErrorThread();
+ mThreadErrorp->setUserData((void *) this);
+ mThreadErrorp->start();
+}
+
+
+void LLApp::setErrorHandler(LLAppErrorHandler handler)
+{
+ LLApp::sErrorHandler = handler;
+}
+
+// static
+void LLApp::runErrorHandler()
+{
+ if (LLApp::sErrorHandler)
+ {
+ LLApp::sErrorHandler();
+ }
+
+ //llinfos << "App status now STOPPED" << llendl;
+ LLApp::setStopped();
+}
+
+
+// static
+void LLApp::setStatus(EAppStatus status)
+{
+ sStatus = status;
+}
+
+
+// static
+void LLApp::setError()
+{
+ setStatus(APP_STATUS_ERROR);
+}
+
+
+// static
+void LLApp::setQuitting()
+{
+ if (!isExiting())
+ {
+ // If we're already exiting, we don't want to reset our state back to quitting.
+ llinfos << "Setting app state to QUITTING" << llendl;
+ setStatus(APP_STATUS_QUITTING);
+ }
+}
+
+
+// static
+void LLApp::setStopped()
+{
+ setStatus(APP_STATUS_STOPPED);
+}
+
+
+// static
+bool LLApp::isStopped()
+{
+ return (APP_STATUS_STOPPED == sStatus);
+}
+
+
+// static
+bool LLApp::isRunning()
+{
+ return (APP_STATUS_RUNNING == sStatus);
+}
+
+
+// static
+bool LLApp::isError()
+{
+ return (APP_STATUS_ERROR == sStatus);
+}
+
+
+// static
+bool LLApp::isQuitting()
+{
+ return (APP_STATUS_QUITTING == sStatus);
+}
+
+bool LLApp::isExiting()
+{
+ return isQuitting() || isError();
+}
+
+#if !LL_WINDOWS
+// static
+U32 LLApp::getSigChildCount()
+{
+ if (sSigChildCount)
+ {
+ return U32(*sSigChildCount);
+ }
+ return 0;
+}
+
+// static
+void LLApp::incSigChildCount()
+{
+ if (sSigChildCount)
+ {
+ (*sSigChildCount)++;
+ }
+}
+
+#endif
+
+
+// static
+int LLApp::getPid()
+{
+#if LL_WINDOWS
+ return 0;
+#else
+ return getpid();
+#endif
+}
+
+#if LL_WINDOWS
+LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop)
+{
+ // Translate the signals/exceptions into cross-platform stuff
+ // Windows implementation
+
+ // Make sure the user sees something to indicate that the app crashed.
+ LONG retval;
+
+ if (LLApp::isError())
+ {
+ llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
+ retval = EXCEPTION_EXECUTE_HANDLER;
+ return retval;
+ }
+
+ // Flag status to error, so thread_error starts its work
+ LLApp::setError();
+
+ // Block in the exception handler until the app has stopped
+ // This is pretty sketchy, but appears to work just fine
+ while (!LLApp::isStopped())
+ {
+ ms_sleep(10);
+ }
+
+ //
+ // Generate a minidump if we can.
+ //
+ // FIXME: This needs to be ported over form the viewer-specific LLWinDebug class
+
+ //
+ // At this point, we always want to exit the app. There's no graceful
+ // recovery for an unhandled exception.
+ //
+ // Just kill the process.
+ retval = EXCEPTION_EXECUTE_HANDLER;
+ return retval;
+}
+
+#else //!LL_WINDOWS
+void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
+{
+ LLChildInfo child_info;
+ child_info.mCallback = callback;
+ LLApp::sChildMap[pid] = child_info;
+}
+
+void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
+{
+ LLApp::sDefaultChildCallback = callback;
+}
+
+pid_t LLApp::fork()
+{
+ pid_t pid = ::fork();
+ if( pid < 0 )
+ {
+ int system_error = errno;
+ llwarns << "Unable to fork! Operating system error code: "
+ << system_error << llendl;
+ }
+ else if (pid == 0)
+ {
+ // Sleep a bit to allow the parent to set up child callbacks.
+ ms_sleep(10);
+
+ // We need to disable signal handling, because we don't have a
+ // signal handling thread anymore.
+ setupErrorHandling();
+ }
+ else
+ {
+ llinfos << "Forked child process " << pid << llendl;
+ }
+ return pid;
+}
+
+void setup_signals()
+{
+ //
+ // Set up signal handlers that may result in program termination
+ //
+ struct sigaction act;
+ act.sa_sigaction = default_unix_signal_handler;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+ sigaction(SIGCHLD, &act, NULL);
+ sigaction(SIGUSR2, &act, NULL);
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+}
+
+void clear_signals()
+{
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+ sigaction(SIGCHLD, &act, NULL);
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(SIGUSR2, &act, NULL);
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+}
+
+
+
+void default_unix_signal_handler(int signum, siginfo_t *info, void *)
+{
+ // Unix implementation of synchronous signal handler
+ // This runs in the thread that threw the signal.
+ // We do the somewhat sketchy operation of blocking in here until the error handler
+ // has gracefully stopped the app.
+
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << llendl;
+ }
+
+
+ switch (signum)
+ {
+ case SIGALRM:
+ case SIGUSR2:
+ // We don't care about these signals, ignore them
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Ignoring this signal" << llendl;
+ }
+ return;
+ case SIGCHLD:
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
+ }
+ // Check result code for all child procs for which we've registered callbacks
+ // THIS WILL NOT WORK IF SIGCHLD IS SENT w/o killing the child (Go, launcher!)
+ // FIXME: Now that we're using SIGACTION, we can actually implement the launcher behavior to determine
+ // who sent the SIGCHLD even if it doesn't result in child termination
+ if (LLApp::sChildMap.count(info->si_pid))
+ {
+ LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
+ }
+
+ LLApp::incSigChildCount();
+
+ return;
+ case SIGABRT:
+ // Abort just results in termination of the app, no funky error handling.
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got SIGABRT, terminating" << llendl;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ case LL_SMACKDOWN_SIGNAL: // Smackdown treated just like any other app termination, for now
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Handling smackdown signal!" << llendl;
+ }
+ else
+ {
+ // Don't log anything, even errors - this is because this signal could happen anywhere.
+ gErrorStream.setLevel(LLErrorStream::NONE);
+ }
+
+ // Change the signal that we reraise to SIGABRT, so we generate a core dump.
+ signum = SIGABRT;
+ case SIGPIPE:
+ case SIGBUS:
+ case SIGSEGV:
+ case SIGQUIT:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Handling fatal signal!" << llendl;
+ }
+ if (LLApp::isError())
+ {
+ // Received second fatal signal while handling first, just die right now
+ // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
+ clear_signals();
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got another fatal signal while in the error handler, die now!" << llendl;
+ }
+ raise(signum);
+ return;
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
+ }
+ // Flag status to ERROR, so thread_error does its work.
+ LLApp::setError();
+ // Block in the signal handler until somebody says that we're done.
+ while (LLApp::sErrorThreadRunning && !LLApp::isStopped())
+ {
+ ms_sleep(10);
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - App is stopped, reraising signal" << llendl;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ case SIGINT:
+ case SIGHUP:
+ case SIGTERM:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << llendl;
+ }
+ // Graceful exit
+ // Just set our state to quitting, not error
+ if (LLApp::isQuitting() || LLApp::isError())
+ {
+ // We're already trying to die, just ignore this signal
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
+ }
+ return;
+ }
+ LLApp::setQuitting();
+ return;
+ default:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Unhandled signal, ignoring!" << llendl;
+ }
+ }
+}
+
+#endif // !WINDOWS
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
new file mode 100644
index 0000000000..da5662c54d
--- /dev/null
+++ b/indra/llcommon/llapp.h
@@ -0,0 +1,259 @@
+/**
+ * @file llapp.h
+ * @brief Declaration of the LLApp class.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPP_H
+#define LL_LLAPP_H
+
+#include <map>
+#include "llapr.h"
+#include "llrun.h"
+#include "llsd.h"
+
+// Forward declarations
+class LLErrorThread;
+class LLApp;
+
+
+typedef void (*LLAppErrorHandler)();
+typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
+
+#if !LL_WINDOWS
+extern const S32 LL_SMACKDOWN_SIGNAL;
+
+// Clear all of the signal handlers (which we want to do for the child process when we fork
+void clear_signals();
+
+class LLChildInfo
+{
+public:
+ LLChildInfo() : mGotSigChild(FALSE), mCallback(NULL) {}
+ BOOL mGotSigChild;
+ LLAppChildCallback mCallback;
+};
+#endif
+
+class LLApp
+{
+ friend class LLErrorThread;
+public:
+ typedef enum e_app_status
+ {
+ APP_STATUS_RUNNING, // The application is currently running - the default status
+ APP_STATUS_QUITTING, // The application is currently quitting - threads should listen for this and clean up
+ APP_STATUS_STOPPED, // The application is no longer running - tells the error thread it can exit
+ APP_STATUS_ERROR // The application had a fatal error occur - tells the error thread to run
+ } EAppStatus;
+
+
+ LLApp();
+ virtual ~LLApp();
+
+ /**
+ * @brief Return the static app instance if one was created.
+ */
+ static LLApp* instance();
+
+ /** @name Runtime options */
+ //@{
+ /**
+ * @brief Enumeration to specify option priorities in highest to
+ * lowest order.
+ */
+ enum OptionPriority
+ {
+ PRIORITY_RUNTIME_OVERRIDE,
+ PRIORITY_COMMAND_LINE,
+ PRIORITY_SPECIFIC_CONFIGURATION,
+ PRIORITY_GENERAL_CONFIGURATION,
+ PRIORITY_DEFAULT,
+ PRIORITY_COUNT
+ };
+
+ /**
+ * @brief Get the application option at the highest priority.
+ *
+ * If the return value is undefined, the option does not exist.
+ * @param name The name of the option.
+ * @return Returns the option data.
+ */
+ LLSD getOption(const std::string& name) const;
+
+ /**
+ * @brief Parse command line options and insert them into
+ * application command line options.
+ *
+ * The name inserted into the option will have leading option
+ * identifiers (a minus or double minus) stripped. All options
+ * with values will be stored as a string, while all options
+ * without values will be stored as true.
+ * @param argc The argc passed into main().
+ * @param argv The argv passed into main().
+ * @return Returns true if the parse succeeded.
+ */
+ bool parseCommandOptions(int argc, char** argv);
+
+ /**
+ * @brief Set the options at the specified priority.
+ *
+ * This function completely replaces the options at the priority
+ * level with the data specified. This function will make sure
+ * level and data might be valid before doing the replace.
+ * @param level The priority level of the data.
+ * @param data The data to set.
+ * @return Returns true if the option was set.
+ */
+ bool setOptionData(OptionPriority level, LLSD data);
+
+ /**
+ * @brief Get the option data at the specified priority.
+ *
+ * This method is probably not so useful except when merging
+ * information.
+ * @param level The priority level of the data.
+ * @return Returns The data (if any) at the level priority.
+ */
+ LLSD getOptionData(OptionPriority level);
+ //@}
+
+
+
+ //
+ // Main application logic
+ //
+ virtual bool init() = 0; // Override to do application initialization
+
+ //
+ // cleanup()
+ //
+ // It's currently assumed that the cleanup() method will only get
+ // called from the main thread or the error handling thread, as it will
+ // likely do thread shutdown, among other things.
+ //
+ virtual bool cleanup() = 0; // Override to do application cleanup
+
+ //
+ // mainLoop()
+ //
+ // Runs the application main loop. It's assumed that when you exit
+ // this method, the application is in one of the cleanup states, either QUITTING or ERROR
+ //
+ virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
+
+
+ //
+ // Application status
+ //
+ static void setQuitting(); // Set status to QUITTING, the app is now shutting down
+ static void setStopped(); // Set status to STOPPED, the app is done running and should exit
+ static void setError(); // Set status to ERROR, the error handler should run
+ static bool isStopped();
+ static bool isRunning();
+ static bool isQuitting();
+ static bool isError();
+ static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
+#if !LL_WINDOWS
+ static U32 getSigChildCount();
+ static void incSigChildCount();
+#endif
+ static int getPid();
+
+ //
+ // Error handling methods
+ //
+ void setErrorHandler(LLAppErrorHandler handler);
+
+#if !LL_WINDOWS
+ //
+ // Child process handling (Unix only for now)
+ //
+ // Set a callback to be run on exit of a child process
+ // WARNING! This callback is run from the signal handler due to the extreme crappiness of
+ // Linux threading requiring waitpid() to be called from the thread that spawned the process.
+ // At some point I will make this more behaved, but I'm not going to fix this right now - djs
+ void setChildCallback(pid_t pid, LLAppChildCallback callback);
+
+ // The child callback to run if no specific handler is set
+ void setDefaultChildCallback(LLAppChildCallback callback);
+
+ // Fork and do the proper signal handling/error handling mojo
+ // WARNING: You need to make sure your signal handling callback is correct after
+ // you fork, because not all threads are duplicated when you fork!
+ pid_t fork();
+#endif
+
+ /**
+ * @brief Get a reference to the application runner
+ *
+ * Please use the runner with caution. Since the Runner usage
+ * pattern is not yet clear, this method just gives access to it
+ * to add and remove runnables.
+ * @return Returns the application runner. Do not save the
+ * pointer past the caller's stack frame.
+ */
+ LLRunner& getRunner() { return mRunner; }
+
+public:
+ typedef std::map<std::string, std::string> string_map;
+ string_map mOptionMap; // Contains all command-line options and arguments in a map
+
+protected:
+
+ static void setStatus(EAppStatus status); // Use this to change the application status.
+ static EAppStatus sStatus; // Reflects current application status
+ static BOOL sErrorThreadRunning; // Set while the error thread is running
+
+#if !LL_WINDOWS
+ static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
+ typedef std::map<pid_t, LLChildInfo> child_map; // Map key is a PID
+ static child_map sChildMap;
+ static LLAppChildCallback sDefaultChildCallback;
+#endif
+
+ /**
+ * @brief This method is called once a frame to do once a frame tasks.
+ */
+ void stepFrame();
+
+private:
+ void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)
+
+ static void runErrorHandler();
+
+ // FIXME: On Windows, we need a routine to reset the structured exception handler when some evil driver has taken it over for their own purposes
+
+ typedef int(*signal_handler_func)(int signum);
+ static LLAppErrorHandler sErrorHandler;
+
+ // Default application threads
+ LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
+
+ // This is the application level runnable scheduler.
+ LLRunner mRunner;
+
+ /** @name Runtime option implementation */
+ //@{
+
+ // The application options.
+ LLSD mOptions;
+
+ //@}
+
+private:
+ // the static application instance if it was created.
+ static LLApp* sApplication;
+
+
+#if !LL_WINDOWS
+ friend void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+#endif
+
+public:
+ static BOOL sLogInSignal;
+};
+
+#endif // LL_LLAPP_H
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
new file mode 100644
index 0000000000..a7fc6a40a7
--- /dev/null
+++ b/indra/llcommon/llapr.cpp
@@ -0,0 +1,201 @@
+/**
+ * @file llapr.cpp
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
+apr_thread_mutex_t *gLogMutexp = NULL;
+
+
+void ll_init_apr()
+{
+ if (!gAPRPoolp)
+ {
+ // Initialize APR and create the global pool
+ apr_initialize();
+ apr_pool_create(&gAPRPoolp, NULL);
+
+ // Initialize the logging mutex
+ apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_DEFAULT, gAPRPoolp);
+ }
+}
+
+
+void ll_cleanup_apr()
+{
+ llinfos << "Cleaning up APR" << llendl;
+
+ if (gLogMutexp)
+ {
+ // Clean up the logging mutex
+
+ // All other threads NEED to be done before we clean up APR, so this is okay.
+ apr_thread_mutex_destroy(gLogMutexp);
+ gLogMutexp = NULL;
+ }
+ if (gAPRPoolp)
+ {
+ apr_pool_destroy(gAPRPoolp);
+ gAPRPoolp = NULL;
+ }
+ apr_terminate();
+}
+
+//
+// LLScopedLock
+//
+LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
+{
+ if(mutex)
+ {
+ if(ll_apr_warn_status(apr_thread_mutex_lock(mMutex)))
+ {
+ mLocked = false;
+ }
+ else
+ {
+ mLocked = true;
+ }
+ }
+ else
+ {
+ mLocked = false;
+ }
+}
+
+LLScopedLock::~LLScopedLock()
+{
+ unlock();
+}
+
+void LLScopedLock::unlock()
+{
+ if(mLocked)
+ {
+ if(!ll_apr_warn_status(apr_thread_mutex_unlock(mMutex)))
+ {
+ mLocked = false;
+ }
+ }
+}
+
+//
+// Misc functions
+//
+bool ll_apr_warn_status(apr_status_t status)
+{
+ if(APR_SUCCESS == status) return false;
+ char buf[MAX_STRING]; /* Flawfinder: ignore */
+ llwarns << "APR: " << apr_strerror(status, buf, MAX_STRING) << llendl;
+ return true;
+}
+
+void ll_apr_assert_status(apr_status_t status)
+{
+ llassert(ll_apr_warn_status(status) == false);
+}
+
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep)
+{
+ apr_file_t* apr_file;
+ apr_status_t s;
+ s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ if (sizep)
+ {
+ *sizep = 0;
+ }
+ return NULL;
+ }
+
+ if (sizep)
+ {
+ S32 file_size = 0;
+ apr_off_t offset = 0;
+ if (apr_file_seek(apr_file, APR_END, &offset) == APR_SUCCESS)
+ {
+ file_size = (S32)offset;
+ offset = 0;
+ apr_file_seek(apr_file, APR_SET, &offset);
+ }
+ *sizep = file_size;
+ }
+
+ return apr_file;
+}
+
+S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes)
+{
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_read(apr_file, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ return 0;
+ }
+ else
+ {
+ return (S32)sz;
+ }
+}
+
+
+S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes)
+{
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_write(apr_file, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ return 0;
+ }
+ else
+ {
+ return (S32)sz;
+ }
+}
+
+S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset)
+{
+ apr_off_t apr_offset = offset;
+ apr_status_t s = apr_file_seek(apr_file, where, &apr_offset);
+ if (s != APR_SUCCESS)
+ {
+ return -1;
+ }
+ else
+ {
+ return (S32)apr_offset;
+ }
+}
+
+bool ll_apr_file_remove(const LLString& filename)
+{
+ apr_status_t s;
+ s = apr_file_remove(filename.c_str(), gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ llwarns << "ll_apr_file_remove failed on file: " << filename << llendl;
+ return false;
+ }
+ return true;
+}
+
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname)
+{
+ apr_status_t s;
+ s = apr_file_rename(filename.c_str(), newname.c_str(), gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ llwarns << "ll_apr_file_rename failed on file: " << filename << llendl;
+ return false;
+ }
+ return true;
+}
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
new file mode 100644
index 0000000000..1e9e944eef
--- /dev/null
+++ b/indra/llcommon/llapr.h
@@ -0,0 +1,129 @@
+/**
+ * @file llapr.h
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPR_H
+#define LL_LLAPR_H
+
+#if LL_LINUX
+#include <sys/param.h> // Need PATH_MAX in APR headers...
+#endif
+
+#include "boost/noncopyable.hpp"
+
+#include "apr-1/apr_thread_proc.h"
+#include "apr-1/apr_thread_mutex.h"
+#include "apr-1/apr_getopt.h"
+#include "apr-1/apr_signal.h"
+#include "apr-1/apr_atomic.h"
+#include "llstring.h"
+
+
+/**
+ * @brief initialize the common apr constructs -- apr itself, the
+ * global pool, and a mutex.
+ */
+void ll_init_apr();
+
+/**
+ * @brief Cleanup those common apr constructs.
+ */
+void ll_cleanup_apr();
+
+/**
+ * @class LLScopedLock
+ * @brief Small class to help lock and unlock mutexes.
+ *
+ * This class is used to have a stack level lock once you already have
+ * an apr mutex handy. The constructor handles the lock, and the
+ * destructor handles the unlock. Instances of this class are
+ * <b>not</b> thread safe.
+ */
+class LLScopedLock : private boost::noncopyable
+{
+public:
+ /**
+ * @brief Constructor which accepts a mutex, and locks it.
+ *
+ * @param mutex An allocated APR mutex. If you pass in NULL,
+ * this wrapper will not lock.
+ */
+ LLScopedLock(apr_thread_mutex_t* mutex);
+
+ /**
+ * @brief Destructor which unlocks the mutex if still locked.
+ */
+ ~LLScopedLock();
+
+ /**
+ * @brief Check lock.
+ */
+ bool isLocked() const { return mLocked; }
+
+ /**
+ * @brief This method unlocks the mutex.
+ */
+ void unlock();
+
+protected:
+ bool mLocked;
+ apr_thread_mutex_t* mMutex;
+};
+
+template <typename Type> class LLAtomic32
+{
+public:
+ LLAtomic32<Type>() {};
+ LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
+ ~LLAtomic32<Type>() {};
+
+ operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
+ Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
+ void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
+ void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
+ Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
+ Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
+
+private:
+ apr_uint32_t mData;
+};
+
+typedef LLAtomic32<U32> LLAtomicU32;
+typedef LLAtomic32<S32> LLAtomicS32;
+
+// File IO convenience functions.
+// Returns NULL if the file fails to openm sets *sizep to file size of not NULL
+// abbreviated flags
+#define LL_APR_R (APR_READ) // "r"
+#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w"
+#define LL_APR_RB (APR_READ|APR_BINARY) // "rb"
+#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
+#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
+#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep = NULL);
+// Returns actual offset, -1 if seek fails
+S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset);
+// Returns bytes read/written, 0 if read/write fails
+S32 ll_apr_file_read(apr_file_t* apr_file, void* buf, S32 nbytes);
+S32 ll_apr_file_write(apr_file_t* apr_file, const void* buf, S32 nbytes);
+bool ll_apr_file_remove(const LLString& filename);
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname);
+
+/**
+ * @brief Function which approprately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+bool ll_apr_warn_status(apr_status_t status);
+
+void ll_apr_assert_status(apr_status_t status);
+
+extern "C" apr_pool_t* gAPRPoolp; // Global APR memory pool
+
+#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
new file mode 100644
index 0000000000..f6d9f166e1
--- /dev/null
+++ b/indra/llcommon/llassettype.cpp
@@ -0,0 +1,219 @@
+/**
+ * @file llassettype.cpp
+ * @brief Implementatino of LLAssetType functionality.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <time.h>
+
+#include "llassettype.h"
+#include "lltimer.h"
+
+// I added lookups for exact text of asset type enums in addition to the ones below, so shoot me. -Steve
+
+struct asset_info_t
+{
+ LLAssetType::EType type;
+ const char* desc;
+};
+
+asset_info_t asset_types[] =
+{
+ { LLAssetType::AT_TEXTURE, "TEXTURE" },
+ { LLAssetType::AT_SOUND, "SOUND" },
+ { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" },
+ { LLAssetType::AT_LANDMARK, "LANDMARK" },
+ { LLAssetType::AT_SCRIPT, "SCRIPT" },
+ { LLAssetType::AT_CLOTHING, "CLOTHING" },
+ { LLAssetType::AT_OBJECT, "OBJECT" },
+ { LLAssetType::AT_NOTECARD, "NOTECARD" },
+ { LLAssetType::AT_CATEGORY, "CATEGORY" },
+ { LLAssetType::AT_ROOT_CATEGORY, "ROOT_CATEGORY" },
+ { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" },
+ { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" },
+ { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" },
+ { LLAssetType::AT_BODYPART, "BODYPART" },
+ { LLAssetType::AT_TRASH, "TRASH" },
+ { LLAssetType::AT_SNAPSHOT_CATEGORY, "SNAPSHOT_CATEGORY" },
+ { LLAssetType::AT_LOST_AND_FOUND, "LOST_AND_FOUND" },
+ { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" },
+ { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" },
+ { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" },
+ { LLAssetType::AT_ANIMATION, "ANIMATION" },
+ { LLAssetType::AT_GESTURE, "GESTURE" },
+ { LLAssetType::AT_SIMSTATE, "SIMSTATE" },
+ { LLAssetType::AT_NONE, "NONE" },
+};
+
+LLAssetType::EType LLAssetType::getType(const LLString& sin)
+{
+ LLString s = sin;
+ LLString::toUpper(s);
+ for (S32 idx = 0; ;idx++)
+ {
+ asset_info_t* info = asset_types + idx;
+ if (info->type == LLAssetType::AT_NONE)
+ break;
+ if (s == info->desc)
+ return info->type;
+ }
+ return LLAssetType::AT_NONE;
+}
+
+LLString LLAssetType::getDesc(LLAssetType::EType type)
+{
+ for (S32 idx = 0; ;idx++)
+ {
+ asset_info_t* info = asset_types + idx;
+ if (type == info->type)
+ return LLString(info->desc);
+ if (info->type == LLAssetType::AT_NONE)
+ break;
+ }
+ return LLString("BAD TYPE");
+}
+
+//============================================================================
+
+// The asset type names are limited to 8 characters.
+// static
+const char* LLAssetType::mAssetTypeNames[LLAssetType::AT_COUNT] =
+{
+ "texture",
+ "sound",
+ "callcard",
+ "landmark",
+ "script",
+ "clothing",
+ "object",
+ "notecard",
+ "category",
+ "root",
+ "lsltext",
+ "lslbyte",
+ "txtr_tga",// Intentionally spelled this way. Limited to eight characters.
+ "bodypart",
+ "trash",
+ "snapshot",
+ "lstndfnd",
+ "snd_wav",
+ "img_tga",
+ "jpeg",
+ "animatn",
+ "gesture",
+ "simstate",
+};
+
+// This table is meant for decoding to human readable form. Put any
+// and as many printable characters you want in each one.
+// See also llinventory.cpp INVENTORY_TYPE_HUMAN_NAMES
+const char* LLAssetType::mAssetTypeHumanNames[LLAssetType::AT_COUNT] =
+{
+ "texture",
+ "sound",
+ "calling card",
+ "landmark",
+ "legacy script",
+ "clothing",
+ "object",
+ "note card",
+ "folder",
+ "root",
+ "lsl2 script",
+ "lsl bytecode",
+ "tga texture",
+ "body part",
+ "trash",
+ "snapshot",
+ "lost and found",
+ "sound",
+ "targa image",
+ "jpeg image",
+ "animation",
+ "gesture",
+ "simstate",
+};
+
+///----------------------------------------------------------------------------
+/// class LLAssetType
+///----------------------------------------------------------------------------
+
+// static
+const char* LLAssetType::lookup( LLAssetType::EType type )
+{
+ if( (type >= 0) && (type < AT_COUNT ))
+ {
+ return mAssetTypeNames[ S32( type ) ];
+ }
+ else
+ {
+ return "-1";
+ }
+}
+
+// static
+LLAssetType::EType LLAssetType::lookup( const char* name )
+{
+ for( S32 i = 0; i < AT_COUNT; i++ )
+ {
+ if( 0 == strcmp(name, mAssetTypeNames[i]) )
+ {
+ // match
+ return (EType)i;
+ }
+ }
+ return AT_NONE;
+}
+
+// static
+const char* LLAssetType::lookupHumanReadable(LLAssetType::EType type)
+{
+ if( (type >= 0) && (type < AT_COUNT ))
+ {
+ return mAssetTypeHumanNames[S32(type)];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+EDragAndDropType LLAssetType::lookupDragAndDropType( EType asset )
+{
+ switch( asset )
+ {
+ case AT_TEXTURE: return DAD_TEXTURE;
+ case AT_SOUND: return DAD_SOUND;
+ case AT_CALLINGCARD: return DAD_CALLINGCARD;
+ case AT_LANDMARK: return DAD_LANDMARK;
+ case AT_SCRIPT: return DAD_NONE;
+ case AT_CLOTHING: return DAD_CLOTHING;
+ case AT_OBJECT: return DAD_OBJECT;
+ case AT_NOTECARD: return DAD_NOTECARD;
+ case AT_CATEGORY: return DAD_CATEGORY;
+ case AT_ROOT_CATEGORY: return DAD_ROOT_CATEGORY;
+ case AT_LSL_TEXT: return DAD_SCRIPT;
+ case AT_BODYPART: return DAD_BODYPART;
+ case AT_ANIMATION: return DAD_ANIMATION;
+ case AT_GESTURE: return DAD_GESTURE;
+ default: return DAD_NONE;
+ };
+}
+
+// static. Generate a good default description
+void LLAssetType::generateDescriptionFor(LLAssetType::EType type,
+ LLString& desc)
+{
+ const S32 BUF_SIZE = 30;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+ time_t now;
+ time(&now);
+ memset(time_str, '\0', BUF_SIZE);
+ strftime(time_str, BUF_SIZE - 1, "%Y-%m-%d %H:%M:%S ", localtime(&now));
+ desc.assign(time_str);
+ desc.append(LLAssetType::lookupHumanReadable(type));
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
new file mode 100644
index 0000000000..2b5aadf078
--- /dev/null
+++ b/indra/llcommon/llassettype.h
@@ -0,0 +1,149 @@
+/**
+ * @file llassettype.h
+ * @brief Declaration of LLAssetType.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSETTYPE
+#define LL_LLASSETTYPE
+
+#include "stdenums.h" // for EDragAndDropType
+
+class LLAssetType
+{
+public:
+ enum EType
+ {
+ // Used for painting the faces of geometry.
+ // Stored in typical j2c stream format
+ AT_TEXTURE = 0,
+
+ // Used to fill the aural spectrum.
+ AT_SOUND = 1,
+
+ // Links instant message access to the user on the card. eg, a
+ // card for yourself, a card for linden support, a card for
+ // the guy you were talking to in the coliseum.
+ AT_CALLINGCARD = 2,
+
+ // Links to places in the world with location and a screen
+ // shot or image saved. eg, home, linden headquarters, the
+ // coliseum, or destinations where we want to increase
+ // traffic.
+ AT_LANDMARK = 3,
+
+ // Valid scripts that can be attached to an object. eg. open a
+ // door, jump into the air.
+ AT_SCRIPT = 4,
+
+ // A collection of textures and parameters that can be worn
+ // by an avatar.
+ AT_CLOTHING = 5,
+
+ // Any combination of textures, sounds, and scripts that are
+ // associated with a fixed piece of geometry. eg, a hot tub, a
+ // house with working door.
+ AT_OBJECT = 6,
+
+ // Just text
+ AT_NOTECARD = 7,
+
+ // A category holds a collection of inventory items. It's
+ // treated as an item in the inventory, and therefore needs a
+ // type.
+ AT_CATEGORY = 8,
+
+ // A root category is a user's root inventory category. We
+ // decided to expose it visually, so it seems logical to fold
+ // it into the asset types.
+ AT_ROOT_CATEGORY = 9,
+
+ // The LSL is the brand spanking new scripting language. We've
+ // split it into a text and bytecode representation.
+ AT_LSL_TEXT = 10,
+ AT_LSL_BYTECODE = 11,
+
+ // uncompressed TGA texture
+ AT_TEXTURE_TGA = 12,
+
+ // A collection of textures and parameters that can be worn
+ // by an avatar.
+ AT_BODYPART = 13,
+
+ // This asset type is meant to only be used as a marker for a
+ // category preferred type. Using this, we can throw things in
+ // the trash before completely deleting.
+ AT_TRASH = 14,
+
+ // This is a marker for a folder meant for snapshots. No
+ // actual assets will be snapshots, though if there were, you
+ // could interpret them as textures.
+ AT_SNAPSHOT_CATEGORY = 15,
+
+ // This is used to stuff lost&found items into
+ AT_LOST_AND_FOUND = 16,
+
+ // uncompressed sound
+ AT_SOUND_WAV = 17,
+
+ // uncompressed image, non-square, and not appropriate for use
+ // as a texture.
+ AT_IMAGE_TGA = 18,
+
+ // compressed image, non-square, and not appropriate for use
+ // as a texture.
+ AT_IMAGE_JPEG = 19,
+
+ // animation
+ AT_ANIMATION = 20,
+
+ // gesture, sequence of animations, sounds, chat, wait steps
+ AT_GESTURE = 21,
+
+ // simstate file
+ AT_SIMSTATE = 22,
+
+ // +*********************************************+
+ // | TO ADD AN ELEMENT TO THIS ENUM: |
+ // +*********************************************+
+ // | 1. INSERT BEFORE AT_COUNT |
+ // | 2. INCREMENT AT_COUNT BY 1 |
+ // | 3. ADD TO LLAssetType::mAssetTypeNames |
+ // | 4. ADD TO LLAssetType::mAssetTypeHumanNames |
+ // +*********************************************+
+
+ AT_COUNT = 23,
+
+ AT_NONE = -1
+ };
+
+ // machine transation between type and strings
+ static EType lookup(const char* name);
+ static const char* lookup(EType type);
+
+ // translation from a type to a human readable form.
+ static const char* lookupHumanReadable(EType type);
+
+ static EDragAndDropType lookupDragAndDropType( EType );
+
+ // Generate a good default description. You may want to add a verb
+ // or agent name after this depending on your application.
+ static void generateDescriptionFor(LLAssetType::EType type,
+ LLString& desc);
+
+ static EType getType(const LLString& sin);
+ static LLString getDesc(EType type);
+
+private:
+ // don't instantiate or derive one of these objects
+ LLAssetType( void ) {}
+ ~LLAssetType( void ) {}
+
+private:
+ static const char* mAssetTypeNames[];
+ static const char* mAssetTypeHumanNames[];
+};
+
+#endif // LL_LLASSETTYPE
diff --git a/indra/llcommon/llassoclist.h b/indra/llcommon/llassoclist.h
new file mode 100644
index 0000000000..d90a26dc8a
--- /dev/null
+++ b/indra/llcommon/llassoclist.h
@@ -0,0 +1,278 @@
+/**
+ * @file llassoclist.h
+ * @brief LLAssocList class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSOCLIST_H
+#define LL_LLASSOCLIST_H
+
+//------------------------------------------------------------------------
+// LLAssocList is an associative list container class.
+//
+// The implementation is a single linked list.
+// Both index and value objects are stored by value (not reference).
+// If pointer values are specified for index and/or value, this
+// container does NOT assume ownership of the referenced objects,
+// and does NOT delete() them on removal or destruction of the container.
+//
+// Note that operations are generally not optimized, and may of them
+// are O(n) complexity.
+//------------------------------------------------------------------------
+
+#include <iostream>
+
+template<class INDEX_TYPE, class VALUE_TYPE>
+class LLAssocList
+{
+private:
+ // internal list node type
+ class Node
+ {
+ public:
+ Node(const INDEX_TYPE &index, const VALUE_TYPE &value, Node *next)
+ {
+ mIndex = index;
+ mValue = value;
+ mNext = next;
+ }
+ ~Node() { }
+ INDEX_TYPE mIndex;
+ VALUE_TYPE mValue;
+ Node *mNext;
+ };
+
+ // head of the linked list
+ Node *mHead;
+
+public:
+ // Constructor
+ LLAssocList()
+ {
+ mHead = NULL;
+ }
+
+ // Destructor
+ ~LLAssocList()
+ {
+ removeAll();
+ }
+
+ // Returns TRUE if list is empty.
+ BOOL isEmpty()
+ {
+ return (mHead == NULL);
+ }
+
+ // Returns the number of items in the list.
+ U32 length()
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ count++;
+ }
+ return count;
+ }
+
+ // Removes item with the specified index.
+ BOOL remove( const INDEX_TYPE &index )
+ {
+ if (!mHead)
+ return FALSE;
+
+ if (mHead->mIndex == index)
+ {
+ Node *node = mHead;
+ mHead = mHead->mNext;
+ delete node;
+ return TRUE;
+ }
+
+ for ( Node *prev = mHead;
+ prev->mNext;
+ prev = prev->mNext )
+ {
+ if (prev->mNext->mIndex == index)
+ {
+ Node *node = prev->mNext;
+ prev->mNext = prev->mNext->mNext;
+ delete node;
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // Removes all items from the list.
+ void removeAll()
+ {
+ while ( mHead )
+ {
+ Node *node = mHead;
+ mHead = mHead->mNext;
+ delete node;
+ }
+ }
+
+ // Adds a new item to the head of the list,
+ // removing any existing item with same index.
+ void addToHead( const INDEX_TYPE &index, const VALUE_TYPE &value )
+ {
+ remove(index);
+ Node *node = new Node(index, value, mHead);
+ mHead = node;
+ }
+
+ // Adds a new item to the end of the list,
+ // removing any existing item with the same index.
+ void addToTail( const INDEX_TYPE &index, const VALUE_TYPE &value )
+ {
+ remove(index);
+ Node *node = new Node(index, value, NULL);
+ if (!mHead)
+ {
+ mHead = node;
+ return;
+ }
+ for ( Node *prev=mHead;
+ prev;
+ prev=prev->mNext )
+ {
+ if (!prev->mNext)
+ {
+ prev->mNext=node;
+ return;
+ }
+ }
+ }
+
+ // Sets the value of a specified index.
+ // If index does not exist, a new value will be added only if
+ // 'addIfNotFound' is set to TRUE.
+ // Returns TRUE if successful.
+ BOOL setValue( const INDEX_TYPE &index, const VALUE_TYPE &value, BOOL addIfNotFound=FALSE )
+ {
+ VALUE_TYPE *valueP = getValue(index);
+ if (valueP)
+ {
+ *valueP = value;
+ return TRUE;
+ }
+ if (!addIfNotFound)
+ return FALSE;
+ addToTail(index, value);
+ return TRUE;
+ }
+
+ // Sets the ith value in the list.
+ // A new value will NOT be addded, if the ith value does not exist.
+ // Returns TRUE if successful.
+ BOOL setValueAt( U32 i, const VALUE_TYPE &value )
+ {
+ VALUE_TYPE *valueP = getValueAt(i);
+ if (valueP)
+ {
+ *valueP = value;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ // Returns a pointer to the value for the specified index,
+ // or NULL if no item found.
+ VALUE_TYPE *getValue( const INDEX_TYPE &index )
+ {
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (node->mIndex == index)
+ return &node->mValue;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the ith value in the list, or
+ // NULL if i is not valid.
+ VALUE_TYPE *getValueAt( U32 i )
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (count == i)
+ return &node->mValue;
+ count++;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the index for the specified index,
+ // or NULL if no item found.
+ INDEX_TYPE *getIndex( const INDEX_TYPE &index )
+ {
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (node->mIndex == index)
+ return &node->mIndex;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the ith index in the list, or
+ // NULL if i is not valid.
+ INDEX_TYPE *getIndexAt( U32 i )
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (count == i)
+ return &node->mIndex;
+ count++;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the value for the specified index,
+ // or NULL if no item found.
+ VALUE_TYPE *operator[](const INDEX_TYPE &index)
+ {
+ return getValue(index);
+ }
+
+ // Returns a pointer to the ith value in the list, or
+ // NULL if i is not valid.
+ VALUE_TYPE *operator[](U32 i)
+ {
+ return getValueAt(i);
+ }
+
+ // Prints the list contents to the specified stream.
+ friend std::ostream &operator<<( std::ostream &os, LLAssocList &map )
+ {
+ os << "{";
+ for ( Node *node = map.mHead;
+ node;
+ node = node->mNext )
+ {
+ os << "<" << node->mIndex << ", " << node->mValue << ">";
+ if (node->mNext)
+ os << ", ";
+ }
+ os << "}";
+
+ return os;
+ }
+};
+
+#endif // LL_LLASSOCLIST_H
diff --git a/indra/llcommon/llavatarconstants.h b/indra/llcommon/llavatarconstants.h
new file mode 100644
index 0000000000..22a088af67
--- /dev/null
+++ b/indra/llcommon/llavatarconstants.h
@@ -0,0 +1,23 @@
+/**
+ * @file indra_constants.h
+ * @brief some useful short term constants for Indra
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_AVATAR_CONSTANTS_H
+#define LL_AVATAR_CONSTANTS_H
+
+// If this string is passed to dataserver in AvatarPropertiesUpdate
+// then no change is made to user.profile_web
+const char* const BLACKLIST_PROFILE_WEB_STR = "featureWebProfilesDisabled";
+
+// If profile web pages are feature blacklisted then this URL is
+// shown in the profile instead of the user's set URL
+const char* const BLACKLIST_PROFILE_WEB_URL = "http://secondlife.com/app/webdisabled";
+
+// Maximum number of avatar picks
+const S32 MAX_AVATAR_PICKS = 10;
+
+#endif
diff --git a/indra/llcommon/llboost.h b/indra/llcommon/llboost.h
new file mode 100644
index 0000000000..7b39ed082e
--- /dev/null
+++ b/indra/llcommon/llboost.h
@@ -0,0 +1,25 @@
+/**
+ * @file llboost.h
+ * @brief helper object & functions for use with boost
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBOOST_H
+#define LL_LLBOOST_H
+
+#include <boost/tokenizer.hpp>
+
+// boost_tokenizer typedef
+/* example usage:
+ boost_tokenizer tokens(input_string, boost::char_separator<char>(" \t\n"));
+ for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ std::string tok = *token_iter;
+ process_token_string( tok );
+ }
+*/
+typedef boost::tokenizer<boost::char_separator<char> > boost_tokenizer;
+
+#endif // LL_LLBOOST_H
diff --git a/indra/llcommon/llchat.h b/indra/llcommon/llchat.h
new file mode 100644
index 0000000000..88867b2081
--- /dev/null
+++ b/indra/llcommon/llchat.h
@@ -0,0 +1,69 @@
+/**
+ * @file llchat.h
+ * @author James Cook
+ * @brief Chat constants and data structures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHAT_H
+#define LL_LLCHAT_H
+
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3math.h"
+
+// enumerations used by the chat system
+typedef enum e_chat_source_type
+{
+ CHAT_SOURCE_SYSTEM = 0,
+ CHAT_SOURCE_AGENT = 1,
+ CHAT_SOURCE_OBJECT = 2
+} EChatSourceType;
+
+typedef enum e_chat_type
+{
+ CHAT_TYPE_WHISPER = 0,
+ CHAT_TYPE_NORMAL = 1,
+ CHAT_TYPE_SHOUT = 2,
+ CHAT_TYPE_START = 4,
+ CHAT_TYPE_STOP = 5,
+ CHAT_TYPE_DEBUG_MSG = 6
+} EChatType;
+
+typedef enum e_chat_audible_level
+{
+ CHAT_AUDIBLE_NOT = -1,
+ CHAT_AUDIBLE_BARELY = 0,
+ CHAT_AUDIBLE_FULLY = 1
+} EChatAudible;
+
+// A piece of chat
+class LLChat
+{
+public:
+ LLChat(const LLString& text = LLString::null)
+ : mText(text),
+ mFromName(),
+ mFromID(),
+ mSourceType(CHAT_SOURCE_AGENT),
+ mChatType(CHAT_TYPE_NORMAL),
+ mAudible(CHAT_AUDIBLE_FULLY),
+ mMuted(FALSE),
+ mTime(0.0),
+ mPosAgent()
+ { }
+
+ LLString mText; // UTF-8 line of text
+ LLString mFromName; // agent or object name
+ LLUUID mFromID; // agent id or object id
+ EChatSourceType mSourceType;
+ EChatType mChatType;
+ EChatAudible mAudible;
+ BOOL mMuted; // pass muted chat to maintain list of chatters
+ F64 mTime; // viewer only, seconds from viewer start
+ LLVector3 mPosAgent;
+};
+
+#endif
diff --git a/indra/llcommon/llclickaction.h b/indra/llcommon/llclickaction.h
new file mode 100644
index 0000000000..538fae3658
--- /dev/null
+++ b/indra/llcommon/llclickaction.h
@@ -0,0 +1,20 @@
+/**
+ * @file llclickaction.h
+ * @author James Cook
+ * @brief Constants for single-click actions on objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLICKACTION_H
+#define LL_LLCLICKACTION_H
+
+const U8 CLICK_ACTION_NONE = 0;
+const U8 CLICK_ACTION_TOUCH = 0;
+const U8 CLICK_ACTION_SIT = 1;
+const U8 CLICK_ACTION_BUY = 2;
+const U8 CLICK_ACTION_PAY = 3;
+const U8 CLICK_ACTION_OPEN = 4;
+
+#endif
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
new file mode 100644
index 0000000000..ff810abfa9
--- /dev/null
+++ b/indra/llcommon/llcommon.cpp
@@ -0,0 +1,43 @@
+/**
+ * @file llcommon.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcommon.h"
+
+//static
+BOOL LLCommon::sAprInitialized = FALSE;
+
+//static
+void LLCommon::initClass()
+{
+ LLMemory::initClass();
+ if (!sAprInitialized)
+ {
+ ll_init_apr();
+ sAprInitialized = TRUE;
+ }
+ LLTimer::initClass();
+ LLThreadSafeRefCount::initClass();
+// LLWorkerThread::initClass();
+// LLFrameCallbackManager::initClass();
+}
+
+//static
+void LLCommon::cleanupClass()
+{
+// LLFrameCallbackManager::cleanupClass();
+// LLWorkerThread::cleanupClass();
+ LLThreadSafeRefCount::cleanupClass();
+ LLTimer::cleanupClass();
+ if (sAprInitialized)
+ {
+ ll_cleanup_apr();
+ sAprInitialized = FALSE;
+ }
+ LLMemory::cleanupClass();
+}
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
new file mode 100644
index 0000000000..c37d479ba9
--- /dev/null
+++ b/indra/llcommon/llcommon.h
@@ -0,0 +1,28 @@
+/**
+ * @file llcommon.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_COMMON_H
+#define LL_COMMON_H
+
+#include "llmemory.h"
+#include "llapr.h"
+// #include "llframecallbackmanager.h"
+#include "lltimer.h"
+#include "llworkerthread.h"
+#include "llfile.h"
+
+class LLCommon
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+private:
+ static BOOL sAprInitialized;
+};
+
+#endif
+
diff --git a/indra/llcommon/llcriticaldamp.cpp b/indra/llcommon/llcriticaldamp.cpp
new file mode 100644
index 0000000000..ee7dfecdaa
--- /dev/null
+++ b/indra/llcommon/llcriticaldamp.cpp
@@ -0,0 +1,72 @@
+/**
+ * @file llcriticaldamp.cpp
+ * @brief Implementation of the critical damping functionality.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include <math.h>
+
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// static members
+//-----------------------------------------------------------------------------
+LLFrameTimer LLCriticalDamp::sInternalTimer;
+std::map<F32, F32> LLCriticalDamp::sInterpolants;
+F32 LLCriticalDamp::sTimeDelta;
+
+//-----------------------------------------------------------------------------
+// LLCriticalDamp()
+//-----------------------------------------------------------------------------
+LLCriticalDamp::LLCriticalDamp()
+{
+ sTimeDelta = 0.f;
+}
+
+// static
+//-----------------------------------------------------------------------------
+// updateInterpolants()
+//-----------------------------------------------------------------------------
+void LLCriticalDamp::updateInterpolants()
+{
+ sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32();
+
+ F32 time_constant;
+
+ for (std::map<F32, F32>::iterator iter = sInterpolants.begin();
+ iter != sInterpolants.end(); iter++)
+ {
+ time_constant = iter->first;
+ F32 new_interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
+ new_interpolant = llclamp(new_interpolant, 0.f, 1.f);
+ sInterpolants[time_constant] = new_interpolant;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getInterpolant()
+//-----------------------------------------------------------------------------
+F32 LLCriticalDamp::getInterpolant(const F32 time_constant, BOOL use_cache)
+{
+ if (time_constant == 0.f)
+ {
+ return 1.f;
+ }
+
+ if (use_cache && sInterpolants.count(time_constant))
+ {
+ return sInterpolants[time_constant];
+ }
+
+ F32 interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
+ interpolant = llclamp(interpolant, 0.f, 1.f);
+ if (use_cache)
+ {
+ sInterpolants[time_constant] = interpolant;
+ }
+
+ return interpolant;
+}
diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h
new file mode 100644
index 0000000000..3f18db9ec3
--- /dev/null
+++ b/indra/llcommon/llcriticaldamp.h
@@ -0,0 +1,35 @@
+/**
+ * @file llcriticaldamp.h
+ * @brief A lightweight class that calculates critical damping constants once
+ * per frame.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCRITICALDAMP_H
+#define LL_LLCRITICALDAMP_H
+
+#include <map>
+
+#include "llframetimer.h"
+
+class LLCriticalDamp
+{
+public:
+ LLCriticalDamp();
+
+ // MANIPULATORS
+ static void updateInterpolants();
+
+ // ACCESSORS
+ static F32 getInterpolant(const F32 time_constant, BOOL use_cache = TRUE);
+
+protected:
+ static LLFrameTimer sInternalTimer; // frame timer for calculating deltas
+
+ static std::map<F32, F32> sInterpolants;
+ static F32 sTimeDelta;
+};
+
+#endif // LL_LLCRITICALDAMP_H
diff --git a/indra/llcommon/lldarray.h b/indra/llcommon/lldarray.h
new file mode 100644
index 0000000000..c8b5b7fb14
--- /dev/null
+++ b/indra/llcommon/lldarray.h
@@ -0,0 +1,192 @@
+/**
+ * @file lldarray.h
+ * @brief Wrapped std::vector for backward compatibility.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDARRAY_H
+#define LL_LLDARRAY_H
+
+#include "llmath.h"
+#include "llerror.h"
+
+#include <vector>
+#include <map>
+
+// class LLDynamicArray<>; // = std::vector + reserves <BlockSize> elements
+// class LLDynamicArrayIndexed<>; // = std::vector + std::map if indices, only supports operator[] and begin(),end()
+
+//--------------------------------------------------------
+// LLDynamicArray declaration
+//--------------------------------------------------------
+// NOTE: BlockSize is used to reserve a minimal initial amount
+template <typename Type, int BlockSize = 32>
+class LLDynamicArray : public std::vector<Type>
+{
+public:
+ enum
+ {
+ OKAY = 0,
+ FAIL = -1
+ };
+
+ LLDynamicArray(S32 size=0) : std::vector<Type>(size) { if (size < BlockSize) std::vector<Type>::reserve(BlockSize); }
+
+ void reset() { std::vector<Type>::resize(0); }
+
+ // ACCESSORS
+ const Type& get(S32 index) const { return std::vector<Type>::operator[](index); }
+ Type& get(S32 index) { return std::vector<Type>::operator[](index); }
+ S32 find(const Type &obj) const;
+
+ S32 count() const { return std::vector<Type>::size(); }
+ S32 getLength() const { return std::vector<Type>::size(); }
+ S32 getMax() const { return std::vector<Type>::capacity(); }
+
+ // MANIPULATE
+ S32 put(const Type &obj); // add to end of array, returns index
+// Type* reserve(S32 num); // reserve a block of indices in advance
+ Type* reserve_block(U32 num); // reserve a block of indices in advance
+
+ S32 remove(S32 index); // remove by index, no bounds checking
+ S32 removeObj(const Type &obj); // remove by object
+ S32 removeLast();
+
+ void operator+=(const LLDynamicArray<Type,BlockSize> &other);
+};
+
+//--------------------------------------------------------
+// LLDynamicArray implementation
+//--------------------------------------------------------
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::find(const Type &obj) const
+{
+ typename std::vector<Type>::const_iterator iter = std::find(this->begin(), this->end(), obj);
+ if (iter != this->end())
+ {
+ return iter - this->begin();
+ }
+ return FAIL;
+}
+
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::remove(S32 i)
+{
+ // This is a fast removal by swapping with the last element
+ S32 sz = this->size();
+ if (i < 0 || i >= sz)
+ {
+ return FAIL;
+ }
+ if (i < sz-1)
+ {
+ this->operator[](i) = this->back();
+ }
+ this->pop_back();
+ return i;
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::removeObj(const Type& obj)
+{
+ typename std::vector<Type>::iterator iter = std::find(this->begin(), this->end(), obj);
+ if (iter != this->end())
+ {
+ typename std::vector<Type>::iterator last = this->end();
+ --last;
+ *iter = *last;
+ this->pop_back();
+ return iter - this->begin();
+ }
+ return FAIL;
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::removeLast()
+{
+ if (!this->empty())
+ {
+ this->pop_back();
+ return OKAY;
+ }
+ return FAIL;
+}
+
+template <typename Type,int BlockSize>
+inline Type* LLDynamicArray<Type,BlockSize>::reserve_block(U32 num)
+{
+ U32 sz = this->size();
+ this->resize(sz+num);
+ return &(this->operator[](sz));
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::put(const Type &obj)
+{
+ this->push_back(obj);
+ return this->size() - 1;
+}
+
+template <typename Type,int BlockSize>
+inline void LLDynamicArray<Type,BlockSize>::operator+=(const LLDynamicArray<Type,BlockSize> &other)
+{
+ insert(this->end(), other.begin(), other.end());
+}
+
+//--------------------------------------------------------
+// LLDynamicArrayIndexed declaration
+//--------------------------------------------------------
+
+template <typename Type, typename Key, int BlockSize = 32>
+class LLDynamicArrayIndexed
+{
+public:
+ typedef typename std::vector<Type>::iterator iterator;
+ typedef typename std::vector<Type>::const_iterator const_iterator;
+ typedef typename std::vector<Type>::reverse_iterator reverse_iterator;
+ typedef typename std::vector<Type>::const_reverse_iterator const_reverse_iterator;
+ typedef typename std::vector<Type>::size_type size_type;
+protected:
+ std::vector<Type> mVector;
+ std::map<Key, U32> mIndexMap;
+
+public:
+ LLDynamicArrayIndexed() { mVector.reserve(BlockSize); }
+
+ iterator begin() { return mVector.begin(); }
+ const_iterator begin() const { return mVector.begin(); }
+ iterator end() { return mVector.end(); }
+ const_iterator end() const { return mVector.end(); }
+
+ reverse_iterator rbegin() { return mVector.rbegin(); }
+ const_reverse_iterator rbegin() const { return mVector.rbegin(); }
+ reverse_iterator rend() { return mVector.rend(); }
+ const_reverse_iterator rend() const { return mVector.rend(); }
+
+ void reset() { mVector.resize(0); mIndexMap.resize(0); }
+ bool empty() const { return mVector.empty(); }
+ size_type size() const { return mVector.empty(); }
+
+ Type& operator[](const Key& k)
+ {
+ typename std::map<Key, U32>::iterator iter = mIndexMap.find(k);
+ if (iter == mIndexMap.end())
+ {
+ U32 n = mVector.size();
+ mIndexMap[k] = n;
+ mVector.resize(n+1);
+ return mVector[n];
+ }
+ else
+ {
+ return mVector[iter->second];
+ }
+ }
+
+};
+
+#endif
diff --git a/indra/llcommon/lldarrayptr.h b/indra/llcommon/lldarrayptr.h
new file mode 100644
index 0000000000..8fe1c5a5df
--- /dev/null
+++ b/indra/llcommon/lldarrayptr.h
@@ -0,0 +1,18 @@
+/**
+ * @file lldarrayptr.h
+ * @brief Wrapped std::vector for backward compatibility.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDARRAYPTR_H
+#define LL_LLDARRAYPTR_H
+
+#include "lldarray.h"
+
+template <class Type, int BlockSize = 32>
+class LLDynamicArrayPtr : public LLDynamicArray<Type, BlockSize>
+{
+};
+
+#endif // LL_LLDARRAYPTR_H
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp
new file mode 100644
index 0000000000..6ae2a1145f
--- /dev/null
+++ b/indra/llcommon/lldate.cpp
@@ -0,0 +1,174 @@
+/**
+ * @file lldate.cpp
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Implementation of the date class
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lldate.h"
+
+#include "apr-1/apr_time.h"
+
+#include <iomanip>
+#include <sstream>
+
+static const F64 DATE_EPOCH = 0.0;
+
+static const F64 LL_APR_USEC_PER_SEC = 1000000.0;
+ // should be APR_USEC_PER_SEC, but that relies on INT64_C which
+ // isn't defined in glib under our build set up for some reason
+
+
+LLDate::LLDate() : mSecondsSinceEpoch(DATE_EPOCH)
+{
+}
+
+LLDate::LLDate(const LLDate& date) :
+ mSecondsSinceEpoch(date.mSecondsSinceEpoch)
+{
+}
+
+LLDate::LLDate(F64 seconds_since_epoch) :
+ mSecondsSinceEpoch(seconds_since_epoch)
+{
+}
+
+LLDate::LLDate(const std::string& iso8601_date)
+{
+ if(!fromString(iso8601_date))
+ {
+ mSecondsSinceEpoch = DATE_EPOCH;
+ }
+}
+
+std::string LLDate::asString() const
+{
+ std::ostringstream stream;
+ toStream(stream);
+ return stream.str();
+}
+
+void LLDate::toStream(std::ostream& s) const
+{
+ apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC);
+
+ apr_time_exp_t exp_time;
+ if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS)
+ {
+ s << "1970-01-01T00:00:00Z";
+ return;
+ }
+
+ s << std::dec << std::setfill('0');
+#if( LL_WINDOWS || __GNUC__ > 2)
+ s << std::right;
+#else
+ s.setf(ios::right);
+#endif
+ s << std::setw(4) << (exp_time.tm_year + 1900)
+ << '-' << std::setw(2) << (exp_time.tm_mon + 1)
+ << '-' << std::setw(2) << (exp_time.tm_mday)
+ << 'T' << std::setw(2) << (exp_time.tm_hour)
+ << ':' << std::setw(2) << (exp_time.tm_min)
+ << ':' << std::setw(2) << (exp_time.tm_sec);
+ if (exp_time.tm_usec > 0)
+ {
+ s << '.' << std::setw(2)
+ << (int)(exp_time.tm_usec / (LL_APR_USEC_PER_SEC / 100));
+ }
+ s << 'Z';
+}
+
+bool LLDate::fromString(const std::string& iso8601_date)
+{
+ std::istringstream stream(iso8601_date);
+ return fromStream(stream);
+}
+
+bool LLDate::fromStream(std::istream& s)
+{
+ struct apr_time_exp_t exp_time;
+ apr_int32_t tm_part;
+ int c;
+
+ s >> tm_part;
+ exp_time.tm_year = tm_part - 1900;
+ c = s.get(); // skip the hypen
+ if (c != '-') { return false; }
+ s >> tm_part;
+ exp_time.tm_mon = tm_part - 1;
+ c = s.get(); // skip the hypen
+ if (c != '-') { return false; }
+ s >> tm_part;
+ exp_time.tm_mday = tm_part;
+
+ c = s.get(); // skip the T
+ if (c != 'T') { return false; }
+
+ s >> tm_part;
+ exp_time.tm_hour = tm_part;
+ c = s.get(); // skip the :
+ if (c != ':') { return false; }
+ s >> tm_part;
+ exp_time.tm_min = tm_part;
+ c = s.get(); // skip the :
+ if (c != ':') { return false; }
+ s >> tm_part;
+ exp_time.tm_sec = tm_part;
+
+ // zero out the unused fields
+ exp_time.tm_usec = 0;
+ exp_time.tm_wday = 0;
+ exp_time.tm_yday = 0;
+ exp_time.tm_isdst = 0;
+ exp_time.tm_gmtoff = 0;
+
+ // generate a time_t from that
+ apr_time_t time;
+ if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS)
+ {
+ return false;
+ }
+
+ F64 seconds_since_epoch = time / LL_APR_USEC_PER_SEC;
+
+ // check for fractional
+ c = s.peek();
+ if(c == '.')
+ {
+ F64 fractional = 0.0;
+ s >> fractional;
+ seconds_since_epoch += fractional;
+ }
+ s.get(); // skip the Z
+ if (c != 'Z') { return false; }
+
+ mSecondsSinceEpoch = seconds_since_epoch;
+ return true;
+}
+
+F64 LLDate::secondsSinceEpoch() const
+{
+ return mSecondsSinceEpoch;
+}
+
+void LLDate::secondsSinceEpoch(F64 seconds)
+{
+ mSecondsSinceEpoch = seconds;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLDate& date)
+{
+ date.toStream(s);
+ return s;
+}
+
+std::istream& operator>>(std::istream& s, LLDate& date)
+{
+ date.fromStream(s);
+ return s;
+}
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
new file mode 100644
index 0000000000..854613ad0b
--- /dev/null
+++ b/indra/llcommon/lldate.h
@@ -0,0 +1,102 @@
+/**
+ * @file lldate.h
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Declaration of a simple date class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDATE_H
+#define LL_LLDATE_H
+
+#include <iosfwd>
+
+#include "stdtypes.h"
+
+/**
+ * @class LLDate
+ * @brief This class represents a particular point in time in UTC.
+ *
+ * The date class represents a point in time after epoch - 1970-01-01.
+ */
+class LLDate
+{
+public:
+ /**
+ * @brief Construct a date equal to epoch.
+ */
+ LLDate();
+
+ /**
+ * @brief Construct a date equal to epoch.
+ */
+ LLDate(const LLDate& date);
+
+ /**
+ * @brief Construct a date from a seconds since epoch value.
+ *
+ * @pararm seconds_since_epoch The number of seconds since UTC epoch.
+ */
+ LLDate(F64 seconds_since_epoch);
+
+ /**
+ * @brief Construct a date from a string representation
+ *
+ * The date is constructed in the <code>fromString()</code>
+ * method. See that method for details of supported formats.
+ * If that method fails to parse the date, the date is set to epoch.
+ * @param iso8601_date An iso-8601 compatible representation of the date.
+ */
+ LLDate(const std::string& iso8601_date);
+
+ /**
+ * @brief Return the date as in ISO-8601 string.
+ *
+ * @return A string representation of the date.
+ */
+ std::string asString() const;
+ void toStream(std::ostream&) const;
+ /**
+ * @brief Set the date from an ISO-8601 string.
+ *
+ * The parser only supports strings conforming to
+ * YYYYF-MM-DDTHH:MM:SS.FFZ where Y is year, M is month, D is day,
+ * H is hour, M is minute, S is second, F is sub-second, and all
+ * other characters are literal.
+ * If this method fails to parse the date, the previous date is
+ * retained.
+ * @param iso8601_date An iso-8601 compatible representation of the date.
+ * @return Returns true if the string was successfully parsed.
+ */
+ bool fromString(const std::string& iso8601_date);
+ bool fromStream(std::istream&);
+
+ /**
+ * @brief Return the date in seconds since epoch.
+ *
+ * @return The number of seconds since epoch UTC.
+ */
+ F64 secondsSinceEpoch() const;
+
+ /**
+ * @brief Set the date in seconds since epoch.
+ *
+ * @param seconds The number of seconds since epoch UTC.
+ */
+ void secondsSinceEpoch(F64 seconds);
+
+private:
+ F64 mSecondsSinceEpoch;
+};
+
+
+// Helper function to stream out a date
+std::ostream& operator<<(std::ostream& s, const LLDate& date);
+
+// Helper function to stream in a date
+std::istream& operator>>(std::istream& s, LLDate& date);
+
+
+#endif // LL_LLDATE_H
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
new file mode 100644
index 0000000000..63322effbe
--- /dev/null
+++ b/indra/llcommon/lldefs.h
@@ -0,0 +1,185 @@
+/**
+ * @file lldefs.h
+ * @brief Various generic constant definitions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEFS_H
+#define LL_LLDEFS_H
+
+#include "stdtypes.h"
+
+// Often used array indices
+const U32 VX = 0;
+const U32 VY = 1;
+const U32 VZ = 2;
+const U32 VW = 3;
+const U32 VS = 3;
+
+const U32 VRED = 0;
+const U32 VGREEN = 1;
+const U32 VBLUE = 2;
+const U32 VALPHA = 3;
+
+const U32 INVALID_DIRECTION = 0xFFFFFFFF;
+const U32 EAST = 0;
+const U32 NORTH = 1;
+const U32 WEST = 2;
+const U32 SOUTH = 3;
+
+const U32 NORTHEAST = 4;
+const U32 NORTHWEST = 5;
+const U32 SOUTHWEST = 6;
+const U32 SOUTHEAST = 7;
+const U32 MIDDLE = 8;
+
+const U8 LL_SOUND_FLAG_NONE = 0x00;
+const U8 LL_SOUND_FLAG_LOOP = 0x01;
+const U8 LL_SOUND_FLAG_SYNC_MASTER = 0x02;
+const U8 LL_SOUND_FLAG_SYNC_SLAVE = 0x04;
+const U8 LL_SOUND_FLAG_SYNC_PENDING = 0x08;
+const U8 LL_SOUND_FLAG_SYNC_MASK = LL_SOUND_FLAG_SYNC_MASTER | LL_SOUND_FLAG_SYNC_SLAVE | LL_SOUND_FLAG_SYNC_PENDING;
+const U8 LL_SOUND_FLAG_QUEUE = 0x10;
+
+const U32 gDirOpposite[8] = {2, 3, 0, 1, 6, 7, 4, 5};
+const U32 gDirAdjacent[8][2] = {
+ {4, 7},
+ {4, 5},
+ {5, 6},
+ {6, 7},
+ {0, 1},
+ {1, 2},
+ {2, 3},
+ {0, 3}
+ };
+
+// Magnitude along the x and y axis
+const S32 gDirAxes[8][2] = {
+ { 1, 0}, // east
+ { 0, 1}, // north
+ {-1, 0}, // west
+ { 0,-1}, // south
+ { 1, 1}, // ne
+ {-1, 1}, // nw
+ {-1,-1}, // sw
+ { 1,-1}, // se
+ };
+
+const U8 EAST_MASK = 1;
+const U8 NORTH_MASK = 2;
+const U8 WEST_MASK = 4;
+const U8 SOUTH_MASK = 8;
+const U8 NORTHEAST_MASK = NORTH_MASK | EAST_MASK;
+const U8 NORTHWEST_MASK = NORTH_MASK | WEST_MASK;
+const U8 SOUTHWEST_MASK = SOUTH_MASK | WEST_MASK;
+const U8 SOUTHEAST_MASK = SOUTH_MASK | EAST_MASK;
+
+const S32 gDirMasks[8] = {
+ EAST_MASK,
+ NORTH_MASK,
+ WEST_MASK,
+ SOUTH_MASK,
+ NORTHEAST_MASK,
+ NORTHWEST_MASK,
+ SOUTHWEST_MASK,
+ SOUTHEAST_MASK
+ };
+
+// Sides of a box...
+// . Z __.Y
+// /|\ /| 0 = NO_SIDE
+// | / 1 = FRONT_SIDE = +x
+// +------|-----------+ 2 = BACK_SIDE = -x
+// /| |/ / /| 3 = LEFT_SIDE = +y
+// / | -5- |/ / | 4 = RIGHT_SIDE = -y
+// / | /| -3- / | 5 = TOP_SIDE = +z
+// +------------------+ | 6 = BOTTOM_SIDE = -z
+// | | | / | |
+// | |/| | / | |/|
+// | 2 | | *-------|-1--------> X
+// |/| | -4- |/| |
+// | +----|---------|---+
+// | / / | /
+// | / -6- | /
+// |/ / |/
+// +------------------+
+const U32 NO_SIDE = 0;
+const U32 FRONT_SIDE = 1;
+const U32 BACK_SIDE = 2;
+const U32 LEFT_SIDE = 3;
+const U32 RIGHT_SIDE = 4;
+const U32 TOP_SIDE = 5;
+const U32 BOTTOM_SIDE = 6;
+
+const U32 LL_MAX_PATH = 1024; // buffer size of maximum path + filename string length
+
+// For strings we send in messages
+const U32 STD_STRING_BUF_SIZE = 255; // Buffer size
+const U32 STD_STRING_STR_LEN = 254; // Length of the string (not including \0)
+const U32 MAX_STRING = STD_STRING_BUF_SIZE; // Buffer size
+
+const U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + 1 for good luck
+
+// C++ is our friend. . . use template functions to make life easier!
+
+// specific inlines for basic types
+//
+// defined for all:
+// llmin(a,b)
+// llmax(a,b)
+// llclamp(a,minimum,maximum)
+//
+// defined for F32, F64:
+// llclampf(a) // clamps a to [0.0 .. 1.0]
+//
+// defined for U16, U32, U64, S16, S32, S64, :
+// llclampb(a) // clamps a to [0 .. 255]
+//
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2)
+{
+ return (d1 > d2) ? d1 : d2;
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3)
+{
+ LLDATATYPE r = llmax(d1,d2);
+ return (r > d3 ? r : d3);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2)
+{
+ return (d1 < d2) ? d1 : d2;
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3)
+{
+ LLDATATYPE r = llmin(d1,d2);
+ return (r < d3 ? r : d3);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclamp(const LLDATATYPE& a, const LLDATATYPE& minval, const LLDATATYPE& maxval)
+{
+ return llmin(llmax(a, minval), maxval);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclampf(const LLDATATYPE& a)
+{
+ return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)1);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclampb(const LLDATATYPE& a)
+{
+ return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)255);
+}
+
+#endif // LL_LLDEFS_H
diff --git a/indra/llcommon/lldepthstack.h b/indra/llcommon/lldepthstack.h
new file mode 100644
index 0000000000..6b259ec9b0
--- /dev/null
+++ b/indra/llcommon/lldepthstack.h
@@ -0,0 +1,81 @@
+/**
+ * @file lldepthstack.h
+ * @brief Declaration of the LLDepthStack class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEPTHSTACK_H
+#define LL_LLDEPTHSTACK_H
+
+#include "linked_lists.h"
+
+template <class DATA_TYPE> class LLDepthStack
+{
+private:
+ LLLinkedList<DATA_TYPE> mStack;
+ U32 mCurrentDepth;
+ U32 mMaxDepth;
+
+public:
+ LLDepthStack() : mCurrentDepth(0), mMaxDepth(0) {}
+ ~LLDepthStack() {}
+
+ void setDepth(U32 depth)
+ {
+ mMaxDepth = depth;
+ }
+
+ U32 getDepth(void) const
+ {
+ return mCurrentDepth;
+ }
+
+ void push(DATA_TYPE *data)
+ {
+ if (mCurrentDepth < mMaxDepth)
+ {
+ mStack.addData(data);
+ mCurrentDepth++;
+ }
+ else
+ {
+ // the last item falls off stack and is deleted
+ mStack.getLastData();
+ mStack.deleteCurrentData();
+ mStack.addData(data);
+ }
+ }
+
+ DATA_TYPE *pop()
+ {
+ DATA_TYPE *tempp = mStack.getFirstData();
+ if (tempp)
+ {
+ mStack.removeCurrentData();
+ mCurrentDepth--;
+ }
+ return tempp;
+ }
+
+ DATA_TYPE *check()
+ {
+ DATA_TYPE *tempp = mStack.getFirstData();
+ return tempp;
+ }
+
+ void deleteAllData()
+ {
+ mCurrentDepth = 0;
+ mStack.deleteAllData();
+ }
+
+ void removeAllNodes()
+ {
+ mCurrentDepth = 0;
+ mStack.removeAllNodes();
+ }
+};
+
+#endif
diff --git a/indra/llcommon/lldlinked.h b/indra/llcommon/lldlinked.h
new file mode 100644
index 0000000000..54087848d9
--- /dev/null
+++ b/indra/llcommon/lldlinked.h
@@ -0,0 +1,77 @@
+/**
+ * @file lldlinked.h
+ * @brief Declaration of the LLDLinked class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDLINKED_H
+#define LL_LLDLINKED_H
+
+#include <stdlib.h>
+
+template <class Type> class LLDLinked
+{
+ LLDLinked* mNextp;
+ LLDLinked* mPrevp;
+public:
+
+ Type* getNext() { return (Type*)mNextp; }
+ Type* getPrev() { return (Type*)mPrevp; }
+ Type* getFirst() { return (Type*)mNextp; }
+
+ void init()
+ {
+ mNextp = mPrevp = NULL;
+ }
+
+ void unlink()
+ {
+ if (mPrevp) mPrevp->mNextp = mNextp;
+ if (mNextp) mNextp->mPrevp = mPrevp;
+ }
+
+ LLDLinked() { mNextp = mPrevp = NULL; }
+ virtual ~LLDLinked() { unlink(); }
+
+ virtual void deleteAll()
+ {
+ Type *curp = getFirst();
+ while(curp)
+ {
+ Type *nextp = curp->getNext();
+ curp->unlink();
+ delete curp;
+ curp = nextp;
+ }
+ }
+
+ void relink(Type &after)
+ {
+ LLDLinked *afterp = (LLDLinked*)&after;
+ afterp->mPrevp = this;
+ mNextp = afterp;
+ }
+
+ virtual void append(Type& after)
+ {
+ LLDLinked *afterp = (LLDLinked*)&after;
+ afterp->mPrevp = this;
+ afterp->mNextp = mNextp;
+ if (mNextp) mNextp->mPrevp = afterp;
+ mNextp = afterp;
+ }
+
+ virtual void insert(Type& before)
+ {
+ LLDLinked *beforep = (LLDLinked*)&before;
+ beforep->mNextp = this;
+ beforep->mPrevp = mPrevp;
+ if (mPrevp) mPrevp->mNextp = beforep;
+ mPrevp = beforep;
+ }
+
+ virtual void put(Type& obj) { append(obj); }
+};
+
+#endif
diff --git a/indra/llcommon/lldqueueptr.h b/indra/llcommon/lldqueueptr.h
new file mode 100644
index 0000000000..b5407d826a
--- /dev/null
+++ b/indra/llcommon/lldqueueptr.h
@@ -0,0 +1,334 @@
+/**
+ * @file lldqueueptr.h
+ * @brief LLDynamicQueuePtr declaration
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDQUEUEPTR_H
+#define LL_LLDQUEUEPTR_H
+
+template <class Type>
+class LLDynamicQueuePtr
+{
+public:
+ enum
+ {
+ OKAY = 0,
+ FAIL = -1
+ };
+
+ LLDynamicQueuePtr(const S32 size=8);
+ ~LLDynamicQueuePtr();
+
+ void init();
+ void destroy();
+ void reset();
+ void realloc(U32 newsize);
+
+ // ACCESSORS
+ const Type& get(const S32 index) const; // no bounds checking
+ Type& get(const S32 index); // no bounds checking
+ const Type& operator [] (const S32 index) const { return get(index); }
+ Type& operator [] (const S32 index) { return get(index); }
+ S32 find(const Type &obj) const;
+
+ S32 count() const { return (mLastObj >= mFirstObj ? mLastObj - mFirstObj : mLastObj + mMaxObj - mFirstObj); }
+ S32 getMax() const { return mMaxObj; }
+ S32 getFirst() const { return mFirstObj; }
+ S32 getLast () const { return mLastObj; }
+
+ // MANIPULATE
+ S32 push(const Type &obj); // add to end of Queue, returns index from start
+ S32 pull( Type &obj); // pull from Queue, returns index from start
+
+ S32 remove (S32 index); // remove by index
+ S32 removeObj(const Type &obj); // remove by object
+
+protected:
+ S32 mFirstObj, mLastObj, mMaxObj;
+ Type* mMemory;
+
+public:
+
+ void print()
+ {
+ /*
+ Convert this to llinfos if it's intended to be used - djs 08/30/02
+
+ printf("Printing from %d to %d (of %d): ",mFirstObj, mLastObj, mMaxObj);
+
+ if (mFirstObj <= mLastObj)
+ {
+ for (S32 i=mFirstObj;i<mLastObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ }
+ else
+ {
+ for (S32 i=mFirstObj;i<mMaxObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ for (i=0;i<mLastObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ }
+ printf("\n");
+ */
+ }
+
+};
+
+
+//--------------------------------------------------------
+// LLDynamicQueuePtrPtr implementation
+//--------------------------------------------------------
+
+
+template <class Type>
+inline LLDynamicQueuePtr<Type>::LLDynamicQueuePtr(const S32 size)
+{
+ init();
+ realloc(size);
+}
+
+template <class Type>
+inline LLDynamicQueuePtr<Type>::~LLDynamicQueuePtr()
+{
+ destroy();
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::init()
+{
+ mFirstObj = 0;
+ mLastObj = 0;
+ mMaxObj = 0;
+ mMemory = NULL;
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::realloc(U32 newsize)
+{
+ if (newsize)
+ {
+ if (mFirstObj > mLastObj && newsize > mMaxObj)
+ {
+ Type* new_memory = new Type[newsize];
+
+ llassert(new_memory);
+
+ S32 _count = count();
+ S32 i, m = 0;
+ for (i=mFirstObj; i < mMaxObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+ for (i=0; i <=mLastObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+
+ delete[] mMemory;
+ mMemory = new_memory;
+
+ mFirstObj = 0;
+ mLastObj = _count;
+ }
+ else
+ {
+ Type* new_memory = new Type[newsize];
+
+ llassert(new_memory);
+
+ S32 i, m = 0;
+ for (i=0; i < mLastObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+ delete[] mMemory;
+ mMemory = new_memory;
+ }
+ }
+ else if (mMemory)
+ {
+ delete[] mMemory;
+ mMemory = NULL;
+ }
+
+ mMaxObj = newsize;
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::destroy()
+{
+ reset();
+ delete[] mMemory;
+ mMemory = NULL;
+}
+
+
+template <class Type>
+void LLDynamicQueuePtr<Type>::reset()
+{
+ for (S32 i=0; i < mMaxObj; i++)
+ {
+ get(i) = NULL; // unrefs for pointers
+ }
+
+ mFirstObj = 0;
+ mLastObj = 0;
+}
+
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::find(const Type &obj) const
+{
+ S32 i;
+ if (mFirstObj <= mLastObj)
+ {
+ for ( i = mFirstObj; i < mLastObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ for ( i = mFirstObj; i < mMaxObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ for ( i = 0; i < mLastObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ }
+
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::remove(S32 i)
+{
+ if (mFirstObj > mLastObj)
+ {
+ if (i >= mFirstObj && i < mMaxObj)
+ {
+ while( i > mFirstObj)
+ {
+ mMemory[i] = mMemory[i-1];
+ i--;
+ }
+ mMemory[mFirstObj] = NULL;
+ mFirstObj++;
+ if (mFirstObj >= mMaxObj) mFirstObj = 0;
+
+ return count();
+ }
+ else if (i < mLastObj && i >= 0)
+ {
+ while(i < mLastObj)
+ {
+ mMemory[i] = mMemory[i+1];
+ i++;
+ }
+ mMemory[mLastObj] = NULL;
+ mLastObj--;
+ if (mLastObj < 0) mLastObj = mMaxObj-1;
+
+ return count();
+ }
+ }
+ else if (i <= mLastObj && i >= mFirstObj)
+ {
+ while(i < mLastObj)
+ {
+ mMemory[i] = mMemory[i+1];
+ i++;
+ }
+ mMemory[mLastObj] = NULL;
+ mLastObj--;
+ if (mLastObj < 0) mLastObj = mMaxObj-1;
+
+ return count();
+ }
+
+
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::removeObj(const Type& obj)
+{
+ S32 ind = find(obj);
+ if (ind >= 0)
+ {
+ return remove(ind);
+ }
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::push(const Type &obj)
+{
+ if (mMaxObj - count() <= 1)
+ {
+ realloc(mMaxObj * 2);
+ }
+
+ mMemory[mLastObj++] = obj;
+
+ if (mLastObj >= mMaxObj)
+ {
+ mLastObj = 0;
+ }
+
+ return count();
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::pull(Type &obj)
+{
+ obj = NULL;
+
+ if (count() < 1) return -1;
+
+ obj = mMemory[mFirstObj];
+ mMemory[mFirstObj] = NULL;
+
+ mFirstObj++;
+
+ if (mFirstObj >= mMaxObj)
+ {
+ mFirstObj = 0;
+ }
+
+ return count();
+}
+
+template <class Type>
+inline const Type& LLDynamicQueuePtr<Type>::get(const S32 i) const
+{
+ return mMemory[i];
+}
+
+template <class Type>
+inline Type& LLDynamicQueuePtr<Type>::get(const S32 i)
+{
+ return mMemory[i];
+}
+
+
+#endif // LL_LLDQUEUEPTR_H
diff --git a/indra/llcommon/llendianswizzle.h b/indra/llcommon/llendianswizzle.h
new file mode 100644
index 0000000000..1794620b46
--- /dev/null
+++ b/indra/llcommon/llendianswizzle.h
@@ -0,0 +1,76 @@
+/**
+ * @file llendianswizzle.h
+ * @brief Functions for in-place bit swizzling
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLENDIANSWIZZLE_H
+#define LL_LLENDIANSWIZZLE_H
+
+/* This function is intended to be used for in-place swizzling, particularly after fread() of
+ binary values from a file. Such as:
+
+ numRead = fread(scale.mV, sizeof(float), 3, fp);
+ llendianswizzle(scale.mV, sizeof(float), 3);
+
+ It assumes that the values in the file are LITTLE endian, so it's a no-op on a little endian machine.
+
+ It keys off of typesize to do the correct swizzle, so make sure that typesize is the size of the native type.
+
+ 64-bit types are not yet handled.
+*/
+
+#ifdef LL_LITTLE_ENDIAN
+ // little endian is native for most things.
+ inline void llendianswizzle(void *,int,int)
+ {
+ // Nothing to do
+ }
+#endif
+
+#ifdef LL_BIG_ENDIAN
+ // big endian requires a bit of work.
+ inline void llendianswizzle(void *p,int typesize, int count)
+ {
+ int i;
+ switch(typesize)
+ {
+ case 2:
+ {
+ U16 temp;
+ for(i=count ;i!=0 ;i--)
+ {
+ temp = ((U16*)p)[0];
+ ((U16*)p)[0] = ((temp >> 8) & 0x000000FF) | ((temp << 8) & 0x0000FF00);
+ p = (void*)(((U16*)p) + 1);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ U32 temp;
+ for(i=count; i!=0; i--)
+ {
+ temp = ((U32*)p)[0];
+ ((U32*)p)[0] =
+ ((temp >> 24) & 0x000000FF) |
+ ((temp >> 8) & 0x0000FF00) |
+ ((temp << 8) & 0x00FF0000) |
+ ((temp << 24) & 0xFF000000);
+ p = (void*)(((U32*)p) + 1);
+ }
+ }
+ break;
+ }
+
+ }
+#endif
+
+// Use this when working with a single integral value you want swizzled
+
+#define llendianswizzleone(x) llendianswizzle(&(x), sizeof(x), 1)
+
+#endif // LL_LLENDIANSWIZZLE_H
diff --git a/indra/llcommon/llenum.h b/indra/llcommon/llenum.h
new file mode 100644
index 0000000000..7443fe9d0f
--- /dev/null
+++ b/indra/llcommon/llenum.h
@@ -0,0 +1,60 @@
+/**
+ * @file llenum.h
+ * @author Tom Yedwab
+ * @brief Utility class for storing enum value <-> string lookup.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLENUM_H
+#define LL_LLENUM_H
+
+class LLEnum
+{
+public:
+ typedef std::pair<const std::string, const U32> enum_t;
+ enum
+ {
+ UNDEFINED = 0xffffffff,
+ };
+
+ LLEnum(const enum_t values_array[], const U32 length)
+ {
+ for (U32 i=0; i<length; ++i)
+ {
+ mEnumMap.insert(values_array[i]);
+ if (values_array[i].second >= mEnumArray.size())
+ {
+ mEnumArray.resize(values_array[i].second+1);
+ }
+ mEnumArray[values_array[i].second] = values_array[i].first;
+ }
+ }
+
+ const U32 operator[](std::string str)
+ {
+ std::map<const std::string, const U32>::iterator itor;
+ itor = mEnumMap.find(str);
+ if (itor != mEnumMap.end())
+ {
+ return itor->second;
+ }
+ return UNDEFINED;
+ }
+
+ const std::string operator[](U32 index)
+ {
+ if (index < mEnumArray.size())
+ {
+ return mEnumArray[index];
+ }
+ return "";
+ }
+
+private:
+ std::map<const std::string, const U32> mEnumMap;
+ std::vector<std::string> mEnumArray;
+};
+
+#endif // LL_LLENUM_H
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
new file mode 100644
index 0000000000..9f643fd9eb
--- /dev/null
+++ b/indra/llcommon/llerror.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file llerror.cpp
+ * @brief Function to crash.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#include "linden_common.h"
+
+#include "llerror.h"
+
+LLErrorBuffer gErrorBuffer;
+LLErrorStream gErrorStream(&gErrorBuffer);
+
+
+void _llcrash_and_loop()
+{
+ // Now, we go kaboom!
+ U32* crash = NULL;
+
+ *crash = 0;
+
+ while(TRUE)
+ {
+
+ // Loop forever, in case the crash didn't work?
+ }
+}
+
+LLScopedErrorLevel::LLScopedErrorLevel(LLErrorBuffer::ELevel error_level)
+{
+ mOrigErrorLevel = gErrorStream.getErrorLevel();
+ gErrorStream.setErrorLevel(error_level);
+}
+
+
+LLScopedErrorLevel::~LLScopedErrorLevel()
+{
+ gErrorStream.setErrorLevel(mOrigErrorLevel);
+}
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
new file mode 100644
index 0000000000..796ec4a421
--- /dev/null
+++ b/indra/llcommon/llerror.h
@@ -0,0 +1,218 @@
+/**
+ * @file llerror.h
+ * @brief Constants, functions, and macros for logging and runtime errors.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERROR_H
+#define LL_LLERROR_H
+
+#include <sstream>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "llerrorstream.h"
+#include "llerrorbuffer.h"
+
+// Specific error codes
+const S32 LL_ERR_NOERR = 0;
+const S32 LL_ERR_ASSET_REQUEST_FAILED = -1;
+//const S32 LL_ERR_ASSET_REQUEST_INVALID = -2;
+const S32 LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
+const S32 LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
+const S32 LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
+const S32 LL_ERR_EOF = -39;
+const S32 LL_ERR_CANNOT_OPEN_FILE = -42;
+const S32 LL_ERR_FILE_NOT_FOUND = -43;
+const S32 LL_ERR_FILE_EMPTY = -44;
+const S32 LL_ERR_TCP_TIMEOUT = -23016;
+const S32 LL_ERR_CIRCUIT_GONE = -23017;
+
+// Error types
+
+#define LLERR_IMAGE (1 << 1) // Image requests
+#define LLERR_MESSAGE (1 << 2) // Messaging
+#define LLERR_PERF (1 << 3) // Performance
+#define LLERR_SQL (1 << 4) // SQL statements
+#define LLERR_DOUG (1 << 5) // Doug's debugging
+#define LLERR_USER_INPUT (1 << 6) // Keyboard and mouse
+#define LLERR_TIMING (1 << 7) // Verbose time info
+#define LLERR_TASK (1 << 8) // Tracking tasks
+#define LLERR_MSG_HANDLER (1 << 9) //
+#define LLERR_CIRCUIT_INFO (1 << 10) // Message system circuit info
+#define LLERR_PHYSICS (1 << 11) // physics
+#define LLERR_VFS (1 << 12) // VFS
+const U32 LLERR_ALL = 0xffff;
+const U32 LLERR_NONE = 0x0;
+
+// Define one of these for different error levels in release...
+// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output.
+#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output
+#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output.
+
+
+//////////////////////////////////////////
+//
+// Implementation - ignore
+//
+//
+#ifdef _DEBUG
+#define SHOW_DEBUG
+#define SHOW_WARN
+#define SHOW_INFO
+#define SHOW_ASSERT
+#else // _DEBUG
+
+#ifdef RELEASE_SHOW_DEBUG
+#define SHOW_DEBUG
+#endif
+
+#ifdef RELEASE_SHOW_WARN
+#define SHOW_WARN
+#endif
+
+#ifdef RELEASE_SHOW_INFO
+#define SHOW_INFO
+#endif
+
+#ifdef RELEASE_SHOW_ASSERT
+#define SHOW_ASSERT
+#endif
+
+#endif // _DEBUG
+
+
+extern LLErrorStream gErrorStream;
+
+
+// LL Error macros
+//
+// Usage:
+//
+// llerrs << "An error, oh my!" << variable << endl;
+// llwarns << "Another error, fuck me!" << variable << endl;
+// llwarnst(LLERR_IMAGE) << "Debug, mother fucker" << endl;
+//
+// NOTE: The output format of filename(lineno): is so that MS DevStudio
+// can parse the output and automatically jump to that location
+
+inline std::string llerrno_string(int errnum)
+{
+ std::stringstream res;
+ res << "error(" << errnum << "):" << strerror(errnum) << " ";
+ return res.str();
+}
+
+inline std::string llerror_file_line(const char* file, S32 line)
+{
+ std::stringstream res;
+ res << file << "(" <<line << ")";
+ return res.str();
+}
+
+// Used to throw an error which is always causes a system halt.
+#define llerrs if (gErrorStream.isEnabledFor(LLErrorBuffer::FATAL)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::FATAL; \
+ llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : error\n"; \
+ llerror_oss << "ERROR: " << llerror_file_line(__FILE__, __LINE__) << " "
+
+// Used to show warnings
+#define llwarns if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : WARNING: "; \
+ else llerror_oss << "WARNING: "; \
+ llerror_oss
+
+// Alerts are for serious non-fatal situations that are not supposed to happen and need to alert someone
+#define llalerts if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : ALERT: "; \
+ else llerror_oss << "ALERT: "; \
+ llerror_oss
+
+// Used to show informational messages that don't get disabled
+#define llinfos if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
+ else llerror_oss << "INFO: "; \
+ llerror_oss
+
+#define llinfost(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO, type)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
+ else llerror_oss << "INFO: [" << #type << "] "; \
+ llerror_oss
+
+// Used for general debugging output
+#define lldebugs if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
+ else llerror_oss << "DEBUG: "; \
+ llerror_oss
+
+#define lldebugst(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG, type)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
+ else llerror_oss << "DEBUG: [" << #type << "] "; \
+ llerror_oss
+
+#define llendl std::endl; gErrorStream.crashOnError(llerror_oss, llerror_level); }
+#define llendflush std::endl << std::flush; gErrorStream.crashOnError(llerror_oss, llerror_level); }
+#define llcont llerror_oss
+
+#define llerror(msg, num) llerrs << "Error # " << num << ": " << msg << llendl;
+
+#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
+
+#ifdef SHOW_ASSERT
+#define llassert(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llassert(func)
+#endif
+#define llassert_always(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+
+#ifdef SHOW_ASSERT
+#define llverify(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llverify(func) (func); // get rid of warning C4189
+#endif
+
+// handy compile-time assert - enforce those template parameters!
+#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1]
+
+// Makes the app go down in flames, but on purpose!
+void _llcrash_and_loop();
+
+// Use as follows:
+// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl;
+//
+// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
+// should perhaps be replaced with boost::format.
+inline std::string llformat(const char *fmt, ...)
+{
+ char tstr[1024]; /* Flawfinder: ignore */
+ va_list va;
+ va_start(va, fmt);
+#if LL_WINDOWS
+ _vsnprintf(tstr, 1024, fmt, va);
+#else
+ vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */
+#endif
+ va_end(va);
+ return std::string(tstr);
+}
+
+// Helper class to temporarily change error level for the current scope.
+class LLScopedErrorLevel
+{
+public:
+ LLScopedErrorLevel(LLErrorBuffer::ELevel error_level);
+ ~LLScopedErrorLevel();
+
+private:
+ LLErrorBuffer::ELevel mOrigErrorLevel;
+};
+
+#endif // LL_LLERROR_H
diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp
new file mode 100644
index 0000000000..5c1ba1a64f
--- /dev/null
+++ b/indra/llcommon/llerrorthread.cpp
@@ -0,0 +1,189 @@
+/**
+ * @file llerrorthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llerrorthread.h"
+#include "llapp.h"
+
+LLErrorThread::LLErrorThread()
+ : LLThread("Error"),
+ mUserDatap(NULL)
+{
+}
+
+LLErrorThread::~LLErrorThread()
+{
+}
+
+void LLErrorThread::setUserData(void* user_data)
+{
+ mUserDatap = user_data;
+}
+
+
+void* LLErrorThread::getUserData() const
+{
+ return mUserDatap;
+}
+
+#if !LL_WINDOWS
+//
+// Various signal/error handling functions that can't be put into the class
+//
+void get_child_status(const int waitpid_status, int &process_status, bool &exited, bool do_logging)
+{
+ exited = false;
+ process_status = -1;
+ // The child process exited. Call its callback, and then clean it up
+ if (WIFEXITED(waitpid_status))
+ {
+ process_status = WEXITSTATUS(waitpid_status);
+ exited = true;
+ if (do_logging)
+ {
+ llinfos << "get_child_status - Child exited cleanly with return of " << process_status << llendl;
+ }
+ return;
+ }
+ else if (WIFSIGNALED(waitpid_status))
+ {
+ process_status = WTERMSIG(waitpid_status);
+ exited = true;
+ if (do_logging)
+ {
+ llinfos << "get_child_status - Child died because of uncaught signal " << process_status << llendl;
+#ifdef WCOREDUMP
+ if (WCOREDUMP(waitpid_status))
+ {
+ llinfos << "get_child_status - Child dumped core" << llendl;
+ }
+ else
+ {
+ llinfos << "get_child_status - Child didn't dump core" << llendl;
+ }
+#endif
+ }
+ return;
+ }
+ else if (do_logging)
+ {
+ // This is weird. I just dump the waitpid status into the status code,
+ // not that there's any way of telling what it is...
+ llinfos << "get_child_status - Got SIGCHILD but child didn't exit" << llendl;
+ process_status = waitpid_status;
+ }
+
+}
+#endif
+
+void LLErrorThread::run()
+{
+ LLApp::sErrorThreadRunning = TRUE;
+ // This thread sits and waits for the sole purpose
+ // of waiting for the signal/exception handlers to flag the
+ // application state as APP_STATUS_ERROR.
+ llinfos << "thread_error - Waiting for an error" << llendl;
+
+ S32 counter = 0;
+#if !LL_WINDOWS
+ U32 last_sig_child_count = 0;
+#endif
+ while (1)
+ {
+ if (LLApp::isError() || LLApp::isStopped())
+ {
+ // The application has stopped running, time to take action (maybe)
+ break;
+ }
+#if !LL_WINDOWS
+ // Check whether or not the main thread had a sig child we haven't handled.
+ U32 current_sig_child_count = LLApp::getSigChildCount();
+ if (last_sig_child_count != current_sig_child_count)
+ {
+ int status = 0;
+ pid_t child_pid = 0;
+ last_sig_child_count = current_sig_child_count;
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "thread_error handling SIGCHLD #" << current_sig_child_count << llendl;
+ }
+ for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin(); iter != LLApp::sChildMap.end();)
+ {
+ child_pid = iter->first;
+ LLChildInfo &child_info = iter->second;
+ // check the status of *all* children, in case we missed a signal
+ if (0 != waitpid(child_pid, &status, WNOHANG))
+ {
+ bool exited = false;
+ int exit_status = -1;
+ get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
+
+ if (child_info.mCallback)
+ {
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Running child callback" << llendl;
+ }
+ child_info.mCallback(child_pid, exited, status);
+ }
+ LLApp::sChildMap.erase(iter++);
+ }
+ else
+ {
+ // Child didn't terminate, yet we got a sigchild somewhere...
+ if (child_info.mGotSigChild && child_info.mCallback)
+ {
+ child_info.mCallback(child_pid, false, 0);
+ }
+ child_info.mGotSigChild = FALSE;
+ iter++;
+ }
+ }
+
+ // check the status of *all* children, in case we missed a signal
+ // Same as above, but use the default child callback
+ while(0 < (child_pid = waitpid( -1, &status, WNOHANG )))
+ {
+ if (0 != waitpid(child_pid, &status, WNOHANG))
+ {
+ bool exited = false;
+ int exit_status = -1;
+ get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
+ if (LLApp::sDefaultChildCallback)
+ {
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Running default child callback" << llendl;
+ }
+ LLApp::sDefaultChildCallback(child_pid, true, status);
+ }
+ }
+ }
+ }
+
+
+#endif
+ ms_sleep(10);
+ counter++;
+ }
+ if (LLApp::isError())
+ {
+ // The app is in an error state, run the application's error handler.
+ //llinfos << "thread_error - An error has occurred, running error callback!" << llendl;
+ // Run the error handling callback
+ LLApp::runErrorHandler();
+ }
+ else
+ {
+ // Everything is okay, a clean exit.
+ //llinfos << "thread_error - Application exited cleanly" << llendl;
+ }
+
+ //llinfos << "thread_error - Exiting" << llendl;
+ LLApp::sErrorThreadRunning = FALSE;
+}
+
diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h
new file mode 100644
index 0000000000..66cbed519e
--- /dev/null
+++ b/indra/llcommon/llerrorthread.h
@@ -0,0 +1,28 @@
+/**
+ * @file llerrorthread.h
+ * @brief Specialized thread to handle runtime errors.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERRORTHREAD_H
+#define LL_LLERRORTHREAD_H
+
+#include "llthread.h"
+
+class LLErrorThread : public LLThread
+{
+public:
+ LLErrorThread();
+ ~LLErrorThread();
+
+ /*virtual*/ void run(void);
+ void setUserData(void *user_data);
+ void *getUserData() const;
+
+protected:
+ void* mUserDatap; // User data associated with this thread
+};
+
+#endif // LL_LLERRORTHREAD_H
diff --git a/indra/llcommon/llevent.cpp b/indra/llcommon/llevent.cpp
new file mode 100644
index 0000000000..6e6fce6ec3
--- /dev/null
+++ b/indra/llcommon/llevent.cpp
@@ -0,0 +1,285 @@
+/**
+ * @file llevent.cpp
+ * @brief LLEvent and LLEventListener base classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llevent.h"
+
+/************************************************
+ Events
+************************************************/
+
+// virtual
+LLEvent::~LLEvent()
+{
+}
+
+// virtual
+bool LLEvent::accept(LLEventListener* listener)
+{
+ return true;
+}
+
+// virtual
+const std::string& LLEvent::desc()
+{
+ return mDesc;
+}
+
+/************************************************
+ Observables
+************************************************/
+
+LLObservable::LLObservable()
+ : mDispatcher(new LLEventDispatcher())
+{
+}
+
+// virtual
+LLObservable::~LLObservable()
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->disengage(this);
+ mDispatcher = NULL;
+ }
+}
+
+// virtual
+bool LLObservable::setDispatcher(LLPointer<LLEventDispatcher> dispatcher)
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->disengage(this);
+ mDispatcher = NULL;
+ }
+ if (dispatcher.notNull() || dispatcher->engage(this))
+ {
+ mDispatcher = dispatcher;
+ return true;
+ }
+ return false;
+}
+
+// Returns the current dispatcher pointer.
+// virtual
+LLEventDispatcher* LLObservable::getDispatcher()
+{
+ return mDispatcher;
+}
+
+// Notifies the dispatcher of an event being fired.
+void LLObservable::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->fireEvent(event, filter);
+ }
+}
+
+/************************************************
+ Dispatchers
+************************************************/
+
+class LLEventDispatcher::Impl
+{
+public:
+ virtual ~Impl() { }
+ virtual bool engage(LLObservable* observable) { return true; }
+ virtual void disengage(LLObservable* observable) { }
+
+ virtual void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata) = 0;
+ virtual void removeListener(LLEventListener *listener) = 0;
+ virtual std::vector<LLListenerEntry> getListeners() const = 0;
+ virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter) = 0;
+};
+
+bool LLEventDispatcher::engage(LLObservable* observable)
+{
+ return impl->engage(observable);
+}
+
+void LLEventDispatcher::disengage(LLObservable* observable)
+{
+ impl->disengage(observable);
+}
+
+void LLEventDispatcher::addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata)
+{
+ impl->addListener(listener, filter, userdata);
+}
+
+void LLEventDispatcher::removeListener(LLEventListener *listener)
+{
+ impl->removeListener(listener);
+}
+
+std::vector<LLListenerEntry> LLEventDispatcher::getListeners() const
+{
+ return impl->getListeners();
+}
+
+
+bool LLEventDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ return impl->fireEvent(event, filter);
+}
+
+class LLSimpleDispatcher : public LLEventDispatcher::Impl
+{
+public:
+ LLSimpleDispatcher(LLEventDispatcher *parent) : mParent(parent) { }
+ virtual ~LLSimpleDispatcher();
+ virtual void addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata);
+ virtual void removeListener(LLEventListener* listener);
+ virtual std::vector<LLListenerEntry> getListeners() const;
+ virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+protected:
+ std::vector<LLListenerEntry> mListeners;
+ LLEventDispatcher *mParent;
+};
+
+LLSimpleDispatcher::~LLSimpleDispatcher()
+{
+ while (mListeners.size() > 0)
+ {
+ removeListener(mListeners.begin()->listener);
+ }
+}
+
+void LLSimpleDispatcher::addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata)
+{
+ if (listener == NULL) return;
+ removeListener(listener);
+ LLListenerEntry new_entry;
+ new_entry.listener = listener;
+ new_entry.filter = filter;
+ new_entry.userdata = userdata;
+ mListeners.push_back(new_entry);
+ listener->handleAttach(mParent);
+}
+
+void LLSimpleDispatcher::removeListener(LLEventListener* listener)
+{
+ std::vector<LLListenerEntry>::iterator itor;
+ for (itor=mListeners.begin(); itor!=mListeners.end();)
+ {
+ if ((*itor).listener == listener)
+ {
+ mListeners.erase(itor);
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+ listener->handleDetach(mParent);
+}
+
+std::vector<LLListenerEntry> LLSimpleDispatcher::getListeners() const
+{
+ std::vector<LLListenerEntry> ret;
+ std::vector<LLListenerEntry>::const_iterator itor;
+ for (itor=mListeners.begin(); itor!=mListeners.end(); ++itor)
+ {
+ ret.push_back(*itor);
+ }
+
+ return ret;
+}
+
+// virtual
+bool LLSimpleDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ std::vector<LLListenerEntry>::iterator itor;
+ LLString filter_string = filter.asString();
+ for (itor=mListeners.begin(); itor!=mListeners.end(); ++itor)
+ {
+ LLListenerEntry& entry = *itor;
+ if (filter_string == "" || entry.filter.asString() == filter_string)
+ {
+ (entry.listener)->handleEvent(event, (*itor).userdata);
+ }
+ }
+ return true;
+}
+
+LLEventDispatcher::LLEventDispatcher()
+{
+ impl = new LLSimpleDispatcher(this);
+}
+
+LLEventDispatcher::~LLEventDispatcher()
+{
+ if (impl)
+ {
+ delete impl;
+ }
+}
+
+/************************************************
+ Listeners
+************************************************/
+
+LLEventListener::~LLEventListener()
+{
+}
+
+LLSimpleListener::~LLSimpleListener()
+{
+ clearDispatchers();
+}
+
+void LLSimpleListener::clearDispatchers()
+{
+ // Remove myself from all listening dispatchers
+ std::vector<LLEventDispatcher *>::iterator itor;
+ while (mDispatchers.size() > 0)
+ {
+ itor = mDispatchers.begin();
+ LLEventDispatcher *dispatcher = *itor;
+ dispatcher->removeListener(this);
+ itor = mDispatchers.begin();
+ if (itor != mDispatchers.end() && (*itor) == dispatcher)
+ {
+ // Somehow, the dispatcher was not removed. Remove it forcibly
+ mDispatchers.erase(itor);
+ }
+ }
+}
+
+bool LLSimpleListener::handleAttach(LLEventDispatcher *dispatcher)
+{
+ // Add dispatcher if it doesn't already exist
+ std::vector<LLEventDispatcher *>::iterator itor;
+ for (itor = mDispatchers.begin(); itor != mDispatchers.end(); ++itor)
+ {
+ if ((*itor) == dispatcher) return true;
+ }
+ mDispatchers.push_back(dispatcher);
+ return true;
+}
+
+bool LLSimpleListener::handleDetach(LLEventDispatcher *dispatcher)
+{
+ // Remove dispatcher from list
+ std::vector<LLEventDispatcher *>::iterator itor;
+ for (itor = mDispatchers.begin(); itor != mDispatchers.end(); )
+ {
+ if ((*itor) == dispatcher)
+ {
+ itor = mDispatchers.erase(itor);
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+ return true;
+}
diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h
new file mode 100644
index 0000000000..4a619ba16f
--- /dev/null
+++ b/indra/llcommon/llevent.h
@@ -0,0 +1,178 @@
+/**
+ * @file llevent.h
+ * @author Tom Yedwab
+ * @brief LLEvent and LLEventListener base classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_EVENT_H
+#define LL_EVENT_H
+
+#include "llsd.h"
+#include "llmemory.h"
+
+class LLEventListener;
+class LLEvent;
+class LLEventDispatcher;
+class LLObservable;
+
+// Abstract event. All events derive from LLEvent
+class LLEvent : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEvent();
+
+public:
+ LLEvent(LLObservable* source, const std::string& desc = "") : mSource(source), mDesc(desc) { }
+
+ LLObservable* getSource() { return mSource; }
+ virtual LLSD getValue() { return LLSD(); }
+ // Determines whether this particular listener
+ // should be notified of this event.
+ // If this function returns true, handleEvent is
+ // called on the listener with this event as the
+ // argument.
+ // Defaults to handling all events. Override this
+ // if associated with an Observable with many different listeners
+ virtual bool accept(LLEventListener* listener);
+
+ // return a string describing the event
+ virtual const std::string& desc();
+
+private:
+ LLObservable* mSource;
+ std::string mDesc;
+};
+
+// Abstract listener. All listeners derive from LLEventListener
+class LLEventListener : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEventListener();
+
+public:
+
+ // Processes the event.
+ // TODO: Make the return value less ambiguous?
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
+
+ // Called when an dispatcher starts/stops listening
+ virtual bool handleAttach(LLEventDispatcher *dispatcher) = 0;
+ virtual bool handleDetach(LLEventDispatcher *dispatcher) = 0;
+};
+
+// A listener which tracks references to it and cleans up when it's deallocated
+class LLSimpleListener : public LLEventListener
+{
+public:
+ virtual ~LLSimpleListener();
+ void clearDispatchers();
+ virtual bool handleAttach(LLEventDispatcher *dispatcher);
+ virtual bool handleDetach(LLEventDispatcher *dispatcher);
+
+protected:
+ std::vector<LLEventDispatcher *> mDispatchers;
+};
+
+class LLObservable; // defined below
+
+// A structure which stores a Listener and its metadata
+struct LLListenerEntry
+{
+ LLEventListener* listener;
+ LLSD filter;
+ LLSD userdata;
+};
+
+// Base class for a dispatcher - an object which listens
+// to events being fired and relays them to their
+// appropriate destinations.
+class LLEventDispatcher : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEventDispatcher();
+
+public:
+ // The default constructor creates a default simple dispatcher implementation.
+ // The simple implementation has an array of listeners and fires every event to
+ // all of them.
+ LLEventDispatcher();
+
+ // This dispatcher is being attached to an observable object.
+ // If we return false, the attach fails.
+ bool engage(LLObservable* observable);
+
+ // This dispatcher is being detached from an observable object.
+ void disengage(LLObservable* observable);
+
+ // Adds a listener to this dispatcher, with a given user data
+ // that will be passed to the listener when an event is fired.
+ void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata);
+
+ // Removes a listener from this dispatcher
+ void removeListener(LLEventListener *listener);
+
+ // Gets a list of interested listeners
+ std::vector<LLListenerEntry> getListeners() const;
+
+ // Handle an event that has just been fired by communicating it
+ // to listeners, passing it across a network, etc.
+ bool fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+public:
+ class Impl;
+private:
+ Impl* impl;
+};
+
+// Interface for observable data (data that fires events)
+// In order for this class to work properly, it needs
+// an instance of an LLEventDispatcher to route events to their
+// listeners.
+class LLObservable
+{
+public:
+ // Initialize with the default Dispatcher
+ LLObservable();
+ virtual ~LLObservable();
+
+ // Replaces the existing dispatcher pointer to the new one,
+ // informing the dispatcher of the change.
+ virtual bool setDispatcher(LLPointer<LLEventDispatcher> dispatcher);
+
+ // Returns the current dispatcher pointer.
+ virtual LLEventDispatcher* getDispatcher();
+
+ void addListener(LLEventListener *listener, LLSD filter = "", const LLSD& userdata = "")
+ {
+ if (mDispatcher.notNull()) mDispatcher->addListener(listener, filter, userdata);
+ }
+ void removeListener(LLEventListener *listener)
+ {
+ if (mDispatcher.notNull()) mDispatcher->removeListener(listener);
+ }
+ // Notifies the dispatcher of an event being fired.
+ void fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+protected:
+ LLPointer<LLEventDispatcher> mDispatcher;
+};
+
+// Utility mixer class which fires & handles events
+class LLSimpleListenerObservable : public LLObservable, public LLSimpleListener
+{
+public:
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
+};
+
+class LLValueChangedEvent : public LLEvent
+{
+public:
+ LLValueChangedEvent(LLObservable* source, LLSD value) : LLEvent(source, "value_changed"), mValue(value) { }
+ LLSD getValue() { return mValue; }
+ LLSD mValue;
+};
+
+#endif // LL_EVENT_H
diff --git a/indra/llcommon/lleventemitter.h b/indra/llcommon/lleventemitter.h
new file mode 100644
index 0000000000..649f32df1e
--- /dev/null
+++ b/indra/llcommon/lleventemitter.h
@@ -0,0 +1,84 @@
+/**
+ * @file lleventemitter.h
+ * @brief General event emitter class
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// header guard
+#ifndef LL_EVENTEMITTER_H
+#define LL_EVENTEMITTER_H
+
+// standard headers
+#include <algorithm>
+#include <typeinfo>
+#include <iostream>
+#include <string>
+#include <list>
+
+#include "stdtypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// templatized emitter class
+template < class T >
+class eventEmitter
+{
+ public:
+ typedef typename T::EventType EventType;
+ typedef std::list< T* > ObserverContainer;
+ typedef void ( T::*observerMethod )( const EventType& );
+
+ protected:
+ ObserverContainer observers;
+
+ public:
+ eventEmitter () { };
+
+ ~eventEmitter () { };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ BOOL addObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return FALSE;
+
+ // check if observer already exists
+ if ( std::find ( observers.begin (), observers.end (), observerIn ) != observers.end () )
+ return FALSE;
+
+ // save it
+ observers.push_back ( observerIn );
+
+ return true;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ BOOL remObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return FALSE;
+
+ observers.remove ( observerIn );
+
+ return TRUE;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ void update ( observerMethod method, const EventType& msgIn )
+ {
+ typename std::list< T* >::iterator iter = observers.begin ();
+
+ while ( iter != observers.end () )
+ {
+ ( ( *iter )->*method ) ( msgIn );
+
+ ++iter;
+ };
+ };
+};
+
+#endif // lleventemitter_h
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
new file mode 100644
index 0000000000..b5d7f8adc8
--- /dev/null
+++ b/indra/llcommon/llfasttimer.h
@@ -0,0 +1,187 @@
+/**
+ * @file llfasttimer.h
+ * @brief Declaration of a fast timer.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFASTTIMER_H
+#define LL_LLFASTTIMER_H
+
+#include "lltimer.h"
+
+#define FAST_TIMER_ON 1
+
+U64 get_cpu_clock_count();
+
+class LLFastTimer
+{
+public:
+ enum EFastTimerType
+ {
+ // high level
+ FTM_FRAME,
+ FTM_UPDATE,
+ FTM_RENDER,
+ FTM_SWAP,
+ FTM_IDLE,
+ FTM_SLEEP,
+
+ // common simulate components
+ FTM_UPDATE_ANIMATION,
+ FTM_UPDATE_TERRAIN,
+ FTM_UPDATE_PRIMITIVES,
+ FTM_UPDATE_PARTICLES,
+ FTM_UPDATE_SKY,
+ FTM_UPDATE_TEXTURES,
+
+ // common render components
+ FTM_RENDER_GEOMETRY,
+ FTM_RENDER_TERRAIN,
+ FTM_RENDER_SIMPLE,
+ FTM_RENDER_SHINY,
+ FTM_RENDER_BUMP,
+ FTM_RENDER_TREES,
+ FTM_RENDER_CHARACTERS,
+ FTM_RENDER_OCCLUSION,
+ FTM_RENDER_ALPHA,
+ FTM_RENDER_CLOUDS,
+ FTM_RENDER_HUD,
+ FTM_RENDER_PARTICLES,
+ FTM_RENDER_WATER,
+ FTM_RENDER_TIMER,
+ FTM_RENDER_UI,
+ FTM_RENDER_FONTS,
+
+ // newview specific
+ FTM_MESSAGES,
+ FTM_REBUILD,
+ FTM_STATESORT,
+ FTM_POOLS,
+ FTM_POOLRENDER,
+ FTM_IDLE_CB,
+ FTM_WORLD_UPDATE,
+ FTM_UPDATE_MOVE,
+ FTM_OCTREE_BALANCE,
+ FTM_UPDATE_LIGHTS,
+ FTM_CULL,
+ FTM_CULL_REBOUND,
+ FTM_GEO_UPDATE,
+ FTM_GEO_RESERVE,
+ FTM_GEO_LIGHT,
+ FTM_GEO_SHADOW,
+ FTM_GEN_VOLUME,
+ FTM_GEN_TRIANGLES,
+ FTM_GEN_FLEX,
+ FTM_AUDIO_UPDATE,
+ FTM_RESET_DRAWORDER,
+ FTM_OBJECTLIST_UPDATE,
+ FTM_AVATAR_UPDATE,
+ FTM_JOINT_UPDATE,
+ FTM_ATTACHMENT_UPDATE,
+ FTM_LOD_UPDATE,
+ FTM_REGION_UPDATE,
+ FTM_CLEANUP,
+ FTM_NETWORK,
+ FTM_IDLE_NETWORK,
+ FTM_CREATE_OBJECT,
+ FTM_LOAD_AVATAR,
+ FTM_PROCESS_MESSAGES,
+ FTM_PROCESS_OBJECTS,
+ FTM_PROCESS_IMAGES,
+ FTM_IMAGE_UPDATE,
+ FTM_IMAGE_CREATE,
+ FTM_IMAGE_DECODE,
+ FTM_PIPELINE,
+ FTM_VFILE_WAIT,
+ FTM_FLEXIBLE_UPDATE,
+ FTM_OCCLUSION,
+ FTM_OCCLUSION_READBACK,
+ FTM_HUD_EFFECTS,
+ FTM_HUD_UPDATE,
+ FTM_INVENTORY,
+ FTM_AUTO_SELECT,
+ FTM_ARRANGE,
+ FTM_FILTER,
+ FTM_REFRESH,
+ FTM_SORT,
+
+ // Temp
+ FTM_TEMP1,
+ FTM_TEMP2,
+ FTM_TEMP3,
+ FTM_TEMP4,
+ FTM_TEMP5,
+ FTM_TEMP6,
+ FTM_TEMP7,
+ FTM_TEMP8,
+
+ FTM_OTHER, // Special, used by display code
+
+ FTM_NUM_TYPES
+ };
+ enum { FTM_HISTORY_NUM = 60 };
+ enum { FTM_MAX_DEPTH = 64 };
+
+public:
+ LLFastTimer(EFastTimerType type)
+ {
+#if FAST_TIMER_ON
+ mType = type;
+
+ // These don't get counted, because they use CPU clockticks
+ //gTimerBins[gCurTimerBin]++;
+ //LLTimer::sNumTimerCalls++;
+
+ U64 cpu_clocks = get_cpu_clock_count();
+
+ sStart[sCurDepth] = cpu_clocks;
+ sCurDepth++;
+#endif
+ };
+ ~LLFastTimer()
+ {
+#if FAST_TIMER_ON
+ U64 end,delta;
+ int i;
+
+ // These don't get counted, because they use CPU clockticks
+ //gTimerBins[gCurTimerBin]++;
+ //LLTimer::sNumTimerCalls++;
+ end = get_cpu_clock_count();
+
+ sCurDepth--;
+ delta = end - sStart[sCurDepth];
+ sCounter[mType] += delta;
+ sCalls[mType]++;
+ // Subtract delta from parents
+ for (i=0; i<sCurDepth; i++)
+ sStart[i] += delta;
+#endif
+ }
+
+ static void reset();
+ static U64 countsPerSecond();
+
+public:
+ static int sCurDepth;
+ static U64 sStart[FTM_MAX_DEPTH];
+ static U64 sCounter[FTM_NUM_TYPES];
+ static U64 sCalls[FTM_NUM_TYPES];
+ static U64 sCountAverage[FTM_NUM_TYPES];
+ static U64 sCallAverage[FTM_NUM_TYPES];
+ static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
+ static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
+ static S32 sCurFrameIndex;
+ static S32 sLastFrameIndex;
+ static int sPauseHistory;
+ static int sResetHistory;
+ static F64 sCPUClockFrequency;
+
+private:
+ EFastTimerType mType;
+};
+
+
+#endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
new file mode 100644
index 0000000000..a02383c69e
--- /dev/null
+++ b/indra/llcommon/llfile.cpp
@@ -0,0 +1,251 @@
+/**
+ * @file llfile.cpp
+ * @author Michael Schlachter
+ * @date 2006-03-23
+ * @brief Implementation of cross-platform POSIX file buffer and c++
+ * stream classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llfile.h"
+#include "llstring.h"
+#include "llerror.h"
+
+using namespace std;
+
+// static
+int LLFile::mkdir(const char* dirname, int perms)
+{
+#if LL_WINDOWS
+ // permissions are ignored on Windows
+ std::string utf8dirname = dirname;
+ llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
+ return _wmkdir(utf16dirname.c_str());
+#else
+ return ::mkdir(dirname, (mode_t)perms);
+#endif
+}
+
+// static
+LLFILE* LLFile::fopen(const char* filename, const char* mode) /* Flawfinder: ignore */
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8mode = mode;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
+ return _wfopen(utf16filename.c_str(),utf16mode.c_str());
+#else
+ return ::fopen(filename,mode); /* Flawfinder: ignore */
+#endif
+}
+
+LLFILE* LLFile::_fsopen(const char* filename, const char* mode, int sharingFlag)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8mode = mode;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
+ return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
+#else
+ llassert(0);//No corresponding function on non-windows
+ return NULL;
+#endif
+}
+
+int LLFile::remove(const char* filename)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ return _wremove(utf16filename.c_str());
+#else
+ return ::remove(filename);
+#endif
+}
+
+int LLFile::rename(const char* filename, const char* newname)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8newname = newname;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
+ return _wrename(utf16filename.c_str(),utf16newname.c_str());
+#else
+ return ::rename(filename,newname);
+#endif
+}
+
+int LLFile::stat(const char* filename, llstat* filestatus)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ return _wstat(utf16filename.c_str(),filestatus);
+#else
+ return ::stat(filename,filestatus);
+#endif
+}
+
+
+/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
+
+#if USE_LLFILESTREAMS
+
+LLFILE * LLFile::_Fiopen(const char *filename, std::ios::openmode mode,int) // protection currently unused
+{ // open a file
+ static const char *mods[] =
+ { // fopen mode strings corresponding to valid[i]
+ "r", "w", "w", "a", "rb", "wb", "wb", "ab",
+ "r+", "w+", "a+", "r+b", "w+b", "a+b",
+ 0};
+ static const int valid[] =
+ { // valid combinations of open flags
+ ios_base::in,
+ ios_base::out,
+ ios_base::out | ios_base::trunc,
+ ios_base::out | ios_base::app,
+ ios_base::in | ios_base::binary,
+ ios_base::out | ios_base::binary,
+ ios_base::out | ios_base::trunc | ios_base::binary,
+ ios_base::out | ios_base::app | ios_base::binary,
+ ios_base::in | ios_base::out,
+ ios_base::in | ios_base::out | ios_base::trunc,
+ ios_base::in | ios_base::out | ios_base::app,
+ ios_base::in | ios_base::out | ios_base::binary,
+ ios_base::in | ios_base::out | ios_base::trunc
+ | ios_base::binary,
+ ios_base::in | ios_base::out | ios_base::app
+ | ios_base::binary,
+ 0};
+
+ FILE *fp = 0;
+ int n;
+ ios_base::openmode atendflag = mode & ios_base::ate;
+ ios_base::openmode norepflag = mode & ios_base::_Noreplace;
+
+ if (mode & ios_base::_Nocreate)
+ mode |= ios_base::in; // file must exist
+ mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
+ for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
+ ; // look for a valid mode
+
+ if (valid[n] == 0)
+ return (0); // no valid mode
+ else if (norepflag && mode & (ios_base::out || ios_base::app)
+ && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
+ { // file must not exist, close and fail
+ fclose(fp);
+ return (0);
+ }
+ else if (fp != 0 && fclose(fp) != 0)
+ return (0); // can't close after test open
+// should open with protection here, if other than default
+ else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
+ return (0); // open failed
+
+ if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
+ return (fp); // no need to seek to end, or seek succeeded
+
+ fclose(fp); // can't position at end
+ return (0);
+}
+
+/************** input file stream ********************************/
+
+void llifstream::close()
+{ // close the C stream
+ if (_Filebuffer && _Filebuffer->close() == 0)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ }
+}
+
+void llifstream::open(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot) /* Flawfinder: ignore */
+{ // open a C stream with specified mode
+
+ FILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot);
+ if(filep == NULL)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ return;
+ }
+ llassert(_Filebuffer == NULL);
+ _Filebuffer = new _Myfb(filep);
+ _Myios::init(_Filebuffer);
+}
+
+bool llifstream::is_open() const
+{ // test if C stream has been opened
+ if(_Filebuffer)
+ return (_Filebuffer->is_open());
+ return false;
+}
+llifstream::~llifstream()
+{
+ delete _Filebuffer;
+}
+
+llifstream::llifstream(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot)
+ : std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+
+{ // construct with named file and specified mode
+ open(_Filename, _Mode | ios_base::in, _Prot); /* Flawfinder: ignore */
+}
+
+
+/************** output file stream ********************************/
+
+bool llofstream::is_open() const
+{ // test if C stream has been opened
+ if(_Filebuffer)
+ return (_Filebuffer->is_open());
+ return false;
+}
+
+void llofstream::open(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot) /* Flawfinder: ignore */
+{ // open a C stream with specified mode
+
+ FILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot);
+ if(filep == NULL)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ return;
+ }
+ llassert(_Filebuffer==NULL);
+ _Filebuffer = new _Myfb(filep);
+ _Myios::init(_Filebuffer);
+}
+
+void llofstream::close()
+{ // close the C stream
+ llassert(_Filebuffer);
+ if (_Filebuffer->close() == 0)
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+}
+
+llofstream::llofstream(const char *_Filename,
+ std::ios_base::openmode _Mode,
+ int _Prot)
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL)
+{ // construct with named file and specified mode
+ open(_Filename, _Mode , _Prot); /* Flawfinder: ignore */
+}
+
+llofstream::~llofstream()
+{ // destroy the object
+ delete _Filebuffer;
+}
+
+#endif // #if USE_LLFILESTREAMS
+
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
new file mode 100644
index 0000000000..c7c4d2718a
--- /dev/null
+++ b/indra/llcommon/llfile.h
@@ -0,0 +1,151 @@
+/**
+ * @file llfile.h
+ * @author Michael Schlachter
+ * @date 2006-03-23
+ * @brief Declaration of cross-platform POSIX file buffer and c++
+ * stream classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFILE_H
+#define LL_LLFILE_H
+
+/**
+ * This class provides a cross platform interface to the filesystem.
+ * Attempts to mostly mirror the POSIX style IO functions.
+ */
+
+#include <string>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fstream>
+#include "stdtypes.h"
+
+typedef FILE LLFILE;
+
+#ifdef LL_WINDOWS
+#define USE_LLFILESTREAMS 1
+#else
+#define USE_LLFILESTREAMS 0
+#endif
+
+
+#if LL_WINDOWS
+// windows version of stat function and stat data structure are called _stat
+typedef struct _stat llstat;
+#else
+typedef struct stat llstat;
+#endif
+
+class LLFile
+{
+public:
+ // All these functions take UTF8 path/filenames.
+ static LLFILE* fopen(const char* filename,const char* accessmode); /* Flawfinder: ignore */
+ static LLFILE* _fsopen(const char* filename,const char* accessmode,int sharingFlag);
+
+ // perms is a permissions mask like 0777 or 0700. In most cases it will
+ // be overridden by the user's umask. It is ignored on Windows.
+ static int mkdir(const char* filename, int perms = 0700);
+
+ static int remove(const char* filename);
+ static int rename(const char* filename,const char* newname);
+ static int stat(const char* filename,llstat* file_status);
+ static LLFILE * _Fiopen(const char *filename, std::ios::openmode mode,int); // protection currently unused
+};
+
+
+#if USE_LLFILESTREAMS
+
+class llifstream : public std::basic_istream < char , std::char_traits < char > >
+{
+ // input stream associated with a C stream
+public:
+ typedef std::basic_ifstream<char,std::char_traits < char > > _Myt;
+ typedef std::basic_filebuf<char,std::char_traits< char > > _Myfb;
+ typedef std::basic_ios<char,std::char_traits< char > > _Myios;
+
+ llifstream()
+ : std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+ { // construct unopened
+ }
+
+ explicit llifstream(const char *_Filename,
+ ios_base::openmode _Mode = ios_base::in,
+ int _Prot = (int)ios_base::_Openprot);
+
+ explicit llifstream(_Filet *_File)
+ : std::basic_istream<char,std::char_traits< char > >(NULL,true),
+ _Filebuffer(new _Myfb(_File))
+ { // construct with specified C stream
+ }
+ virtual ~llifstream();
+
+ _Myfb *rdbuf() const
+ { // return pointer to file buffer
+ return _Filebuffer;
+ }
+ bool is_open() const;
+ void open(const char *_Filename,
+ ios_base::openmode _Mode = ios_base::in,
+ int _Prot = (int)ios_base::_Openprot); /* Flawfinder: ignore */
+ void close();
+
+private:
+ _Myfb* _Filebuffer; // the file buffer
+};
+
+
+class llofstream : public std::basic_ostream< char , std::char_traits < char > >
+{
+public:
+ typedef std::basic_ostream< char , std::char_traits < char > > _Myt;
+ typedef std::basic_filebuf< char , std::char_traits < char > > _Myfb;
+ typedef std::basic_ios<char,std::char_traits < char > > _Myios;
+
+ llofstream()
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL)
+ { // construct unopened
+ }
+
+ explicit llofstream(const char *_Filename,
+ std::ios_base::openmode _Mode = ios_base::out,
+ int _Prot = (int)std::ios_base::_Openprot);
+
+
+ explicit llofstream(_Filet *_File)
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),
+ _Filebuffer(new _Myfb(_File))//_File)
+ { // construct with specified C stream
+ }
+
+ virtual ~llofstream();
+
+ _Myfb *rdbuf() const
+ { // return pointer to file buffer
+ return _Filebuffer;
+ }
+
+ bool is_open() const;
+
+ void open(const char *_Filename,ios_base::openmode _Mode = ios_base::out,int _Prot = (int)ios_base::_Openprot); /* Flawfinder: ignore */
+
+ void close();
+
+private:
+ _Myfb *_Filebuffer; // the file buffer
+};
+
+
+
+#else
+//Use standard file streams on non windows platforms
+#define llifstream std::ifstream
+#define llofstream std::ofstream
+
+#endif
+
+
+#endif // not LL_LLFILE_H
diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp
new file mode 100644
index 0000000000..ca7bf1ce48
--- /dev/null
+++ b/indra/llcommon/llfixedbuffer.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file llfixedbuffer.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#include "linden_common.h"
+
+#include "llfixedbuffer.h"
+
+LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
+{
+ mMaxLines = max_lines;
+ mTimer.reset();
+}
+
+
+LLFixedBuffer::~LLFixedBuffer()
+{
+ clear();
+}
+
+
+void LLFixedBuffer::clear()
+{
+ mLines.clear();
+ mAddTimes.clear();
+ mLineLengths.clear();
+
+ mTimer.reset();
+}
+
+
+void LLFixedBuffer::addLine(const LLString& utf8line)
+{
+ LLWString wstring = utf8str_to_wstring(utf8line);
+ LLFixedBuffer::addLine(wstring);
+}
+
+void LLFixedBuffer::addLine(const LLWString& line)
+{
+ if (line.empty())
+ {
+ return;
+ }
+
+ removeExtraLines();
+
+ mLines.push_back(line);
+ mLineLengths.push_back((S32)line.length());
+ mAddTimes.push_back(mTimer.getElapsedTimeF32());
+}
+
+
+void LLFixedBuffer::setMaxLines(S32 max_lines)
+{
+ mMaxLines = max_lines;
+
+ removeExtraLines();
+}
+
+
+void LLFixedBuffer::removeExtraLines()
+{
+ while ((S32)mLines.size() > llmax(0, (S32)(mMaxLines - 1)))
+ {
+ mLines.pop_front();
+ mAddTimes.pop_front();
+ mLineLengths.pop_front();
+ }
+}
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
new file mode 100644
index 0000000000..2ca21b2ded
--- /dev/null
+++ b/indra/llcommon/llfixedbuffer.h
@@ -0,0 +1,44 @@
+/**
+ * @file llfixedbuffer.h
+ * @brief A fixed size buffer of lines.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFIXEDBUFFER_H
+#define LL_LLFIXEDBUFFER_H
+
+#include "timer.h"
+#include <deque>
+#include <string>
+#include "llstring.h"
+
+// Fixed size buffer for console output and other things.
+
+class LLFixedBuffer
+{
+public:
+ LLFixedBuffer(const U32 max_lines = 20);
+ virtual ~LLFixedBuffer();
+
+ LLTimer mTimer;
+ U32 mMaxLines;
+ std::deque<LLWString> mLines;
+ std::deque<F32> mAddTimes;
+ std::deque<S32> mLineLengths;
+
+ void clear(); // Clear the buffer, and reset it.
+ virtual void addLine(const LLString& utf8line);
+ virtual void addLine(const LLWString& line);
+
+ // Get lines currently in the buffer, up to max_size chars, max_length lines
+ char *getLines(U32 max_size = 0, U32 max_length = 0);
+ void setMaxLines(S32 max_lines);
+protected:
+ virtual void removeExtraLines();
+};
+
+const U32 FIXED_BUF_MAX_LINE_LEN = 255; // Not including termnating 0
+
+#endif //LL_FIXED_BUFFER_H
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
new file mode 100644
index 0000000000..c66410651f
--- /dev/null
+++ b/indra/llcommon/llframetimer.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file llframetimer.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "u64.h"
+
+#include "llframetimer.h"
+
+// Static members
+//LLTimer LLFrameTimer::sInternalTimer;
+U64 LLFrameTimer::sStartTotalTime = totalTime();
+F64 LLFrameTimer::sFrameTime = 0.0;
+U64 LLFrameTimer::sTotalTime = 0;
+F64 LLFrameTimer::sTotalSeconds = 0.0;
+S32 LLFrameTimer::sFrameCount = 0;
+U64 LLFrameTimer::sFrameDeltaTime = 0;
+const F64 USEC_PER_SECOND = 1000000.0;
+const F64 USEC_TO_SEC_F64 = 0.000001;
+
+// static
+void LLFrameTimer::updateFrameTime()
+{
+ U64 total_time = totalTime();
+ sFrameDeltaTime = total_time - sTotalTime;
+ sTotalTime = total_time;
+ sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
+ sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
+ sFrameCount++;
+}
+
+void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
+{
+ mStartTime = sFrameTime;
+ mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
+}
+
+F64 LLFrameTimer::expiresAt() const
+{
+ F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
+ expires_at += mExpiry;
+ return expires_at;
+}
+
+BOOL LLFrameTimer::checkExpirationAndReset(F32 expiration)
+{
+ //llinfos << "LLFrameTimer::checkExpirationAndReset()" << llendl;
+ //llinfos << " mStartTime:" << mStartTime << llendl;
+ //llinfos << " sFrameTime:" << sFrameTime << llendl;
+ //llinfos << " mExpiry: " << mExpiry << llendl;
+
+ if(hasExpired())
+ {
+ reset();
+ setTimerExpirySec(expiration);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// static
+F32 LLFrameTimer::getFrameDeltaTimeF32()
+{
+ return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
+}
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
new file mode 100644
index 0000000000..05b01e6a72
--- /dev/null
+++ b/indra/llcommon/llframetimer.h
@@ -0,0 +1,122 @@
+/**
+ * @file llframetimer.h
+ * @brief A lightweight timer that measures seconds and is only
+ * updated once per frame.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFRAMETIMER_H
+#define LL_LLFRAMETIMER_H
+
+/**
+ * *NOTE: Because of limitations on linux which we do not really have
+ * time to explore, the total time is derived from the frame time
+ * and is recsynchronized on every frame.
+ */
+
+#include "lltimer.h"
+#include "timing.h"
+
+class LLFrameTimer
+{
+public:
+ LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(TRUE) {}
+
+ // Return the number of seconds since the start of this
+ // application instance.
+ static F64 getElapsedSeconds()
+ {
+ // Loses msec precision after ~4.5 hours...
+ return sFrameTime;
+ }
+
+ // Return a low precision usec since epoch
+ static U64 getTotalTime()
+ {
+ return sTotalTime ? sTotalTime : totalTime();
+ }
+
+ // Return a low precision seconds since epoch
+ static F64 getTotalSeconds()
+ {
+ return sTotalSeconds;
+ }
+
+ // Call this method once per frame to update the current frame
+ // time.
+ static void updateFrameTime();
+
+ static S32 getFrameCount() { return sFrameCount; }
+
+ static F32 getFrameDeltaTimeF32();
+
+ // MANIPULATORS
+ void start() { reset(); mStarted = TRUE; }
+ void stop() { mStarted = FALSE; }
+ void reset() { mStartTime = sFrameTime; mExpiry = sFrameTime; }
+ void setTimerExpirySec(F32 expiration) { mExpiry = expiration + mStartTime; }
+ void setExpiryAt(F64 seconds_since_epoch);
+ BOOL checkExpirationAndReset(F32 expiration);
+ F32 getElapsedTimeAndResetF32() { F32 t = F32(sFrameTime - mStartTime); reset(); return t; }
+
+ void setAge(const F64 age) { mStartTime = sFrameTime - age; }
+
+ // ACCESSORS
+ BOOL hasExpired() const { return (sFrameTime >= mExpiry); }
+ F32 getTimeToExpireF32() const { return (F32)(mExpiry - sFrameTime); }
+ F32 getElapsedTimeF32() const { return (F32)(sFrameTime - mStartTime); }
+ BOOL getStarted() const { return mStarted; }
+
+ // return the seconds since epoch when this timer will expire.
+ F64 expiresAt() const;
+
+protected:
+ // A single, high resolution timer that drives all LLFrameTimers
+ // *NOTE: no longer used.
+ //static LLTimer sInternalTimer;
+
+ //
+ // Aplication constants
+ //
+
+ // Start time of opp in usec since epoch
+ static U64 sStartTotalTime;
+
+ //
+ // Data updated per frame
+ //
+
+ // Seconds since application start
+ static F64 sFrameTime;
+
+ // Time that has elapsed since last call to updateFrameTime()
+ static U64 sFrameDeltaTime;
+
+ // Total microseconds since epoch.
+ static U64 sTotalTime;
+
+ // Seconds since epoch.
+ static F64 sTotalSeconds;
+
+ // Total number of frames elapsed in application
+ static S32 sFrameCount;
+
+ //
+ // Member data
+ //
+
+ // Number of seconds after application start when this timer was
+ // started. Set equal to sFrameTime when reset.
+ F64 mStartTime;
+
+ // Timer expires this many seconds after application start time.
+ F64 mExpiry;
+
+ // Useful bit of state usually associated with timers, but does
+ // not affect actual functionality
+ BOOL mStarted;
+};
+
+#endif // LL_LLFRAMETIMER_H
diff --git a/indra/llcommon/llhash.h b/indra/llcommon/llhash.h
new file mode 100644
index 0000000000..fbf059bbcd
--- /dev/null
+++ b/indra/llcommon/llhash.h
@@ -0,0 +1,45 @@
+/**
+ * @file llhash.h
+ * @brief Wrapper for a hash function.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHASH_H
+#define LL_LLHASH_H
+
+#include "llpreprocessor.h" // for GCC_VERSION
+
+#if (LL_WINDOWS)
+#include <hash_map>
+#include <algorithm>
+#elif LL_DARWIN || LL_LINUX
+# if GCC_VERSION >= 30400 // gcc 3.4 and up
+# include <ext/hashtable.h>
+# elif __GNUC__ >= 3
+# include <ext/stl_hashtable.h>
+# else
+# include <hashtable.h>
+# endif
+#else
+#error Please define your platform.
+#endif
+
+template<class T> inline size_t llhash(T value)
+{
+#if LL_WINDOWS
+ return stdext::hash_value<T>(value);
+#elif ( (defined _STLPORT_VERSION) || ((LL_LINUX) && (__GNUC__ <= 2)) )
+ std::hash<T> H;
+ return H(value);
+#elif LL_DARWIN || LL_LINUX
+ __gnu_cxx::hash<T> H;
+ return H(value);
+#else
+#error Please define your platform.
+#endif
+}
+
+#endif
+
diff --git a/indra/llcommon/llindexedqueue.h b/indra/llcommon/llindexedqueue.h
new file mode 100644
index 0000000000..c5ee58a4c6
--- /dev/null
+++ b/indra/llcommon/llindexedqueue.h
@@ -0,0 +1,137 @@
+/**
+ * @file llindexedqueue.h
+ * @brief An indexed FIFO queue, where only one element with each key
+ * can be in the queue.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINDEXEDQUEUE_H
+#define LL_LLINDEXEDQUEUE_H
+
+// An indexed FIFO queue, where only one element with each key can be in the queue.
+// This is ONLY used in the interest list, you'll probably want to review this code
+// carefully if you want to use it elsewhere - Doug
+
+template <typename Type>
+class LLIndexedQueue
+{
+protected:
+ typedef std::deque<Type> type_deque;
+ type_deque mQueue;
+ std::set<Type> mKeySet;
+
+public:
+ LLIndexedQueue() {}
+
+ // move_if_there is an O(n) operation
+ bool push_back(const Type &value, bool move_if_there = false)
+ {
+ if (mKeySet.find(value) != mKeySet.end())
+ {
+ // Already on the queue
+ if (move_if_there)
+ {
+ // Remove the existing entry.
+ typename type_deque::iterator it;
+ for (it = mQueue.begin(); it != mQueue.end(); ++it)
+ {
+ if (*it == value)
+ {
+ break;
+ }
+ }
+
+ // This HAS to succeed, otherwise there's a serious bug in the keyset implementation
+ // (although this isn't thread safe, at all)
+
+ mQueue.erase(it);
+ }
+ else
+ {
+ // We're not moving it, leave it alone
+ return false;
+ }
+ }
+ else
+ {
+ // Doesn't exist, add it to the key set
+ mKeySet.insert(value);
+ }
+
+ mQueue.push_back(value);
+
+ // We succeeded in adding the new element.
+ return true;
+ }
+
+ bool push_front(const Type &value, bool move_if_there = false)
+ {
+ if (mKeySet.find(value) != mKeySet.end())
+ {
+ // Already on the queue
+ if (move_if_there)
+ {
+ // Remove the existing entry.
+ typename type_deque::iterator it;
+ for (it = mQueue.begin(); it != mQueue.end(); ++it)
+ {
+ if (*it == value)
+ {
+ break;
+ }
+ }
+
+ // This HAS to succeed, otherwise there's a serious bug in the keyset implementation
+ // (although this isn't thread safe, at all)
+
+ mQueue.erase(it);
+ }
+ else
+ {
+ // We're not moving it, leave it alone
+ return false;
+ }
+ }
+ else
+ {
+ // Doesn't exist, add it to the key set
+ mKeySet.insert(value);
+ }
+
+ mQueue.push_front(value);
+ return true;
+ }
+
+ void pop()
+ {
+ Type value = mQueue.front();
+ mKeySet.erase(value);
+ mQueue.pop_front();
+ }
+
+ Type &front()
+ {
+ return mQueue.front();
+ }
+
+ S32 size() const
+ {
+ return mQueue.size();
+ }
+
+ bool empty() const
+ {
+ return mQueue.empty();
+ }
+
+ void clear()
+ {
+ // Clear out all elements on the queue
+ mQueue.clear();
+ mKeySet.clear();
+ }
+};
+
+#endif // LL_LLINDEXEDQUEUE_H
diff --git a/indra/llcommon/lllinkedqueue.h b/indra/llcommon/lllinkedqueue.h
new file mode 100644
index 0000000000..c386ca0e2b
--- /dev/null
+++ b/indra/llcommon/lllinkedqueue.h
@@ -0,0 +1,291 @@
+/**
+ * @file lllinkedqueue.h
+ * @brief Declaration of linked queue classes.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLINKEDQUEUE_H
+#define LL_LLLINKEDQUEUE_H
+
+#include "llerror.h"
+
+// node that actually contains the data
+template <class DATA_TYPE> class LLLinkedQueueNode
+{
+public:
+ DATA_TYPE mData;
+ LLLinkedQueueNode *mNextp;
+ LLLinkedQueueNode *mPrevp;
+
+
+public:
+ LLLinkedQueueNode();
+ LLLinkedQueueNode(const DATA_TYPE data);
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLLinkedQueueNode();
+};
+
+
+
+template <class DATA_TYPE> class LLLinkedQueue
+{
+
+public:
+ LLLinkedQueue();
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLLinkedQueue();
+
+ // Puts at end of FIFO
+ void push(const DATA_TYPE data);
+
+ // Takes off front of FIFO
+ BOOL pop(DATA_TYPE &data);
+ BOOL peek(DATA_TYPE &data);
+
+ void reset();
+
+ S32 getLength() const;
+
+ BOOL isEmpty() const;
+
+ BOOL remove(const DATA_TYPE data);
+
+ BOOL checkData(const DATA_TYPE data) const;
+
+private:
+ // add node to end of list
+ // set mCurrentp to mQueuep
+ void addNodeAtEnd(LLLinkedQueueNode<DATA_TYPE> *nodep);
+
+private:
+ LLLinkedQueueNode<DATA_TYPE> mHead; // head node
+ LLLinkedQueueNode<DATA_TYPE> mTail; // tail node
+ S32 mLength;
+};
+
+
+//
+// Nodes
+//
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::LLLinkedQueueNode() :
+ mData(), mNextp(NULL), mPrevp(NULL)
+{ }
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::LLLinkedQueueNode(const DATA_TYPE data) :
+ mData(data), mNextp(NULL), mPrevp(NULL)
+{ }
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::~LLLinkedQueueNode()
+{ }
+
+
+//
+// Queue itself
+//
+
+template <class DATA_TYPE>
+LLLinkedQueue<DATA_TYPE>::LLLinkedQueue()
+: mHead(),
+ mTail(),
+ mLength(0)
+{ }
+
+
+// destructor destroys list and nodes, but not data in nodes
+template <class DATA_TYPE>
+LLLinkedQueue<DATA_TYPE>::~LLLinkedQueue()
+{
+ reset();
+}
+
+
+// put data into a node and stick it at the end of the list
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::push(const DATA_TYPE data)
+{
+ // make the new node
+ LLLinkedQueueNode<DATA_TYPE> *nodep = new LLLinkedQueueNode<DATA_TYPE>(data);
+
+ addNodeAtEnd(nodep);
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::remove(const DATA_TYPE data)
+{
+ BOOL b_found = FALSE;
+
+ LLLinkedQueueNode<DATA_TYPE> *currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ if (currentp->mData == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (currentp->mNextp)
+ {
+ currentp->mNextp->mPrevp = currentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = currentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (currentp->mPrevp)
+ {
+ currentp->mPrevp->mNextp = currentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = currentp->mNextp;
+ }
+
+ // remove the node
+ delete currentp;
+ mLength--;
+ break;
+ }
+ currentp = currentp->mNextp;
+ }
+
+ return b_found;
+}
+
+
+// remove all nodes from the list but do not delete associated data
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::reset()
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+ LLLinkedQueueNode<DATA_TYPE> *nextp;
+ currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ nextp = currentp->mNextp;
+ delete currentp;
+ currentp = nextp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mLength = 0;
+}
+
+template <class DATA_TYPE>
+S32 LLLinkedQueue<DATA_TYPE>::getLength() const
+{
+ return mLength;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::isEmpty() const
+{
+ return mLength <= 0;
+}
+
+// check to see if data is in list
+// set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::checkData(const DATA_TYPE data) const
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ if (currentp->mData == data)
+ {
+ return TRUE;
+ }
+ currentp = currentp->mNextp;
+ }
+ return FALSE;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::pop(DATA_TYPE &data)
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+
+ currentp = mHead.mNextp;
+ if (!currentp)
+ {
+ return FALSE;
+ }
+
+ mHead.mNextp = currentp->mNextp;
+ if (currentp->mNextp)
+ {
+ currentp->mNextp->mPrevp = currentp->mPrevp;
+ }
+ else
+ {
+ mTail.mPrevp = currentp->mPrevp;
+ }
+
+ data = currentp->mData;
+ delete currentp;
+ mLength--;
+ return TRUE;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::peek(DATA_TYPE &data)
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+
+ currentp = mHead.mNextp;
+ if (!currentp)
+ {
+ return FALSE;
+ }
+ data = currentp->mData;
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// private members
+//////////////////////////////////////////////////////////////////////////////////////////
+
+
+// add node to end of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::addNodeAtEnd(LLLinkedQueueNode<DATA_TYPE> *nodep)
+{
+ // add the node to the end of the list
+ nodep->mNextp = NULL;
+ nodep->mPrevp = mTail.mPrevp;
+ mTail.mPrevp = nodep;
+
+ // if there's something in the list, fix its back pointer
+ if (nodep->mPrevp)
+ {
+ nodep->mPrevp->mNextp = nodep;
+ }
+ else // otherwise fix the head node
+ {
+ mHead.mNextp = nodep;
+ }
+ mLength++;
+}
+
+#endif
diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp
new file mode 100644
index 0000000000..7dad6f82d8
--- /dev/null
+++ b/indra/llcommon/lllivefile.cpp
@@ -0,0 +1,74 @@
+/**
+ * @file lllivefile.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lllivefile.h"
+
+
+LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) :
+mForceCheck(true),
+mRefreshPeriod(refresh_period),
+mFilename(filename),
+mLastModTime(0),
+mLastExists(false)
+{
+}
+
+
+LLLiveFile::~LLLiveFile()
+{
+}
+
+
+bool LLLiveFile::checkAndReload()
+{
+ if (!mForceCheck && mRefreshTimer.getElapsedTimeF32() < mRefreshPeriod)
+ {
+ // Skip the check if not enough time has elapsed and we're not
+ // forcing a check of the file
+ return false;
+ }
+ mForceCheck = false;
+ mRefreshTimer.reset();
+
+ // Stat the file to see if it exists and when it was last modified.
+ llstat stat_data;
+ int res = LLFile::stat(mFilename.c_str(), &stat_data);
+
+ if (res)
+ {
+ // Couldn't stat the file, that means it doesn't exist or is
+ // broken somehow. Clear flags and return.
+ if (mLastExists)
+ {
+ loadFile(); // Load the file, even though it's missing to allow it to clear state.
+ mLastExists = false;
+ return true;
+ }
+ return false;
+ }
+
+ // The file exists, decide if we want to load it.
+ if (mLastExists)
+ {
+ // The file existed last time, don't read it if it hasn't changed since
+ // last time.
+ if (stat_data.st_mtime <= mLastModTime)
+ {
+ return false;
+ }
+ }
+
+ // We want to read the file. Update status info for the file.
+ mLastExists = true;
+ mLastModTime = stat_data.st_mtime;
+
+ loadFile();
+ return true;
+}
+
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h
new file mode 100644
index 0000000000..97c88a5c5c
--- /dev/null
+++ b/indra/llcommon/lllivefile.h
@@ -0,0 +1,34 @@
+/**
+ * @file lllivefile.h
+ * @brief Automatically reloads a file whenever it changes or is removed.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLIVEFILE_H
+#define LL_LLLIVEFILE_H
+
+#include "llframetimer.h"
+
+class LLLiveFile
+{
+public:
+ LLLiveFile(const std::string &filename, const F32 refresh_period = 5.f);
+ virtual ~LLLiveFile();
+
+ bool checkAndReload(); // Returns true if the file changed in any way
+
+protected:
+ virtual void loadFile() = 0; // Implement this to load your file if it changed
+
+ bool mForceCheck;
+ F32 mRefreshPeriod;
+ LLFrameTimer mRefreshTimer;
+
+ std::string mFilename;
+ time_t mLastModTime;
+ bool mLastExists;
+};
+
+#endif //LL_LLLIVEFILE_H
diff --git a/indra/llcommon/lllocalidhashmap.h b/indra/llcommon/lllocalidhashmap.h
new file mode 100644
index 0000000000..12f2b3f2d7
--- /dev/null
+++ b/indra/llcommon/lllocalidhashmap.h
@@ -0,0 +1,877 @@
+/**
+ * @file lllocalidhashmap.h
+ * @brief Map specialized for dealing with local ids
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLOCALIDHASHMAP_H
+#define LL_LLLOCALIDHASHMAP_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+
+const S32 MAX_ITERS = 4;
+// LocalID hash map
+
+//
+// LLLocalIDHashNode
+//
+
+template <class DATA, int SIZE>
+class LLLocalIDHashNode
+{
+public:
+ LLLocalIDHashNode();
+
+public:
+ S32 mCount;
+ U32 mKey[SIZE];
+ DATA mData[SIZE];
+ LLLocalIDHashNode<DATA, SIZE> *mNextNodep;
+};
+
+
+//
+// LLLocalIDHashNode implementation
+//
+template <class DATA, int SIZE>
+LLLocalIDHashNode<DATA, SIZE>::LLLocalIDHashNode()
+{
+ mCount = 0;
+ mNextNodep = NULL;
+}
+
+//
+// LLLocalIDHashMapIter
+//
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMap;
+
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMapIter
+{
+public:
+ LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ ~LLLocalIDHashMapIter();
+
+ void setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ inline void first();
+ inline void next();
+ inline DATA_TYPE& current(); // *NOTE: Deprecate? Phoenix 2005-04-15
+ inline BOOL done() const;
+ inline S32 currentBin() const;
+ inline void setBin(S32 bin);
+
+ DATA_TYPE& operator*() const
+ {
+ return mCurHashNodep->mData[mCurHashNodeKey];
+ }
+ DATA_TYPE* operator->() const
+ {
+ return &(operator*());
+ }
+
+ LLLocalIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
+
+ S32 mCurHashMapNodeNum;
+ S32 mCurHashNodeKey;
+
+ DATA_TYPE mNull;
+
+ S32 mIterID;
+};
+
+
+
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMap
+{
+public:
+ friend class LLLocalIDHashMapIter<DATA_TYPE, SIZE>;
+
+ LLLocalIDHashMap(); // DO NOT use this unless you explicitly setNull, or the data type constructs a "null"
+ // object by default
+ // basic constructor including sorter
+ LLLocalIDHashMap(const DATA_TYPE &null_data);
+ // Hack, this should really be a const ref, but I'm not doing it that way because the sim
+ // usually uses pointers.
+ ~LLLocalIDHashMap();
+
+ inline DATA_TYPE &get(const U32 local_id);
+ inline BOOL check(const U32 local_id) const;
+ inline DATA_TYPE &set(const U32 local_id, const DATA_TYPE data);
+ inline BOOL remove(const U32 local_id);
+ void removeAll();
+
+ void setNull(const DATA_TYPE data) { mNull = data; }
+
+ inline S32 getLength() const; // Warning, NOT O(1!)
+
+ void dumpIter();
+ void dumpBin(U32 bin);
+
+protected:
+ // Only used by the iterator.
+ void addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
+ void removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
+
+ // Remove the item and shift all items afterward down the list,
+ // fixing up iterators as we go.
+ BOOL removeWithShift(const U32 local_id);
+
+protected:
+ LLLocalIDHashNode<DATA_TYPE, SIZE> mNodes[256];
+
+ S32 mIterCount;
+ LLLocalIDHashMapIter<DATA_TYPE, SIZE> *mIters[MAX_ITERS];
+
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLLocalIDHashMap implementation
+//
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap()
+: mIterCount(0),
+ mNull()
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ mIters[i] = NULL;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap(const DATA_TYPE &null_data)
+: mIterCount(0),
+ mNull(null_data)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ mIters[i] = NULL;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::~LLLocalIDHashMap()
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i])
+ {
+ mIters[i]->mHashMapp = NULL;
+ mIterCount--;
+ }
+ }
+ removeAll();
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeAll()
+{
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+
+ BOOL first = TRUE;
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ nodep->mData[i] = mNull;
+ }
+
+ nodep->mCount = 0;
+ // Done with all objects in this node, go to the next.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
+ nodep = nodep->mNextNodep;
+
+ // Delete the node if it's not the first node
+ if (first)
+ {
+ first = FALSE;
+ curp->mNextNodep = NULL;
+ }
+ else
+ {
+ delete curp;
+ }
+ } while (nodep);
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpIter()
+{
+ std::cout << "Hash map with " << mIterCount << " iterators" << std::endl;
+
+ std::cout << "Hash Map Iterators:" << std::endl;
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i])
+ {
+ llinfos << i << " " << mIters[i]->mCurHashNodep << " " << mIters[i]->mCurHashNodeKey << llendl;
+ }
+ else
+ {
+ llinfos << i << "null" << llendl;
+ }
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpBin(U32 bin)
+{
+ std::cout << "Dump bin " << bin << std::endl;
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ S32 node = 0;
+ do // First node guaranteed to be there.
+ {
+ std::cout << "Bin " << bin
+ << " node " << node
+ << " count " << nodep->mCount
+ << " contains " << std::flush;
+
+ S32 i;
+ for (i = 0; i < nodep->mCount; i++)
+ {
+ std::cout << nodep->mData[i] << " " << std::flush;
+ }
+
+ std::cout << std::endl;
+
+ nodep = nodep->mNextNodep;
+ node++;
+ } while (nodep);
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLLocalIDHashMap<DATA_TYPE, SIZE>::getLength() const
+{
+ S32 count = 0;
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ while (nodep)
+ {
+ count += nodep->mCount;
+ nodep = nodep->mNextNodep;
+ }
+ }
+ return count;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::get(const U32 local_id)
+{
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found it.
+ return nodep->mData[i];
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return mNull;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::check(const U32 local_id) const
+{
+ const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found it.
+ return TRUE;
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ // Didn't find anything
+ return FALSE;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::set(const U32 local_id, const DATA_TYPE data)
+{
+ // Set is just like a normal find, except that if we find a match
+ // we replace it with the input value.
+ // If we don't find a match, we append to the end of the list.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ while (1)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found a match for this key, replace the data with
+ // the incoming data.
+ nodep->mData[i] = data;
+ return nodep->mData[i];
+ }
+ }
+ if (!nodep->mNextNodep)
+ {
+ // We've iterated through all of the keys without finding a match
+ if (i < SIZE)
+ {
+ // There's still some space on this node, append
+ // the key and data to it.
+ nodep->mKey[i] = local_id;
+ nodep->mData[i] = data;
+ nodep->mCount++;
+
+ return nodep->mData[i];
+ }
+ else
+ {
+ // This node is full, append a new node to the end.
+ nodep->mNextNodep = new LLLocalIDHashNode<DATA_TYPE, SIZE>;
+ nodep->mNextNodep->mKey[0] = local_id;
+ nodep->mNextNodep->mData[0] = data;
+ nodep->mNextNodep->mCount = 1;
+
+ return nodep->mNextNodep->mData[0];
+ }
+ }
+
+ // No match on this node, go to the next
+ nodep = nodep->mNextNodep;
+ }
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::remove(const U32 local_id)
+{
+ // Remove is the trickiest operation.
+ // What we want to do is swap the last element of the last
+ // node if we find the one that we want to remove, but we have
+ // to deal with deleting the node from the tail if it's empty, but
+ // NOT if it's the only node left.
+
+ const S32 node_index = local_id & 0xff;
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
+
+ // A modification of the standard search algorithm.
+ do // First node guaranteed to be there
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // If we're removing the item currently pointed to by one
+ // or more iterators, we can just swap in the last item
+ // and back the iterator(s) up by one.
+ // Otherwise, we need to do a slow and safe shift of all
+ // items back to one position to fill the hole and fix up
+ // all iterators we find.
+ BOOL need_shift = FALSE;
+ S32 cur_iter;
+ if (mIterCount)
+ {
+ for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
+ {
+ if (mIters[cur_iter])
+ {
+ // We only care if the hash map node is on the one
+ // that we're working on. If it's before, we've already
+ // traversed it, if it's after, changing the order doesn't
+ // matter.
+ if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
+ {
+ if ((mIters[cur_iter]->mCurHashNodep == nodep)
+ && (mIters[cur_iter]->mCurHashNodeKey == i))
+ {
+ // it's on the one we're deleting, we'll
+ // fix the iterator quickly below.
+ }
+ else
+ {
+ // We're trying to remove an item on this
+ // iterator's chain that this
+ // iterator doesn't point to! We need to do
+ // the slow remove-and-shift-down case.
+ need_shift = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ // Removing an item that isn't pointed to by all iterators
+ if (need_shift)
+ {
+ return removeWithShift(local_id);
+ }
+
+ // Fix the iterators that point to this node/i pair, the
+ // one we're deleting
+ for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
+ {
+ if (mIters[cur_iter])
+ {
+ // We only care if the hash map node is on the one
+ // that we're working on. If it's before, we've already
+ // traversed it, if it's after, changing the order doesn't
+ // matter.
+ if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
+ {
+ if ((mIters[cur_iter]->mCurHashNodep == nodep)
+ && (mIters[cur_iter]->mCurHashNodeKey == i))
+ {
+ // We can handle the case where we're deleting
+ // the element we're on trivially (sort of).
+ if (nodep->mCount > 1)
+ {
+ // If we're not going to delete this node,
+ // it's OK.
+ mIters[cur_iter]->mCurHashNodeKey--;
+ }
+ else
+ {
+ // We're going to delete this node, because this
+ // is the last element on it.
+
+ // Find the next node, and then back up one.
+ mIters[cur_iter]->next();
+ mIters[cur_iter]->mCurHashNodeKey--;
+ }
+ }
+ }
+ }
+ }
+
+ // We found the node that we want to remove.
+ // Find the last (and next-to-last) node, and the index of the last
+ // element. We could conceviably start from the node we're on,
+ // but that makes it more complicated, this is easier.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[node_index];
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
+
+ // Find the last and next-to-last
+ while (lastp->mNextNodep)
+ {
+ prevp = lastp;
+ lastp = lastp->mNextNodep;
+ }
+
+ // First, swap in the last to the current location.
+ nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
+ nodep->mData[i] = lastp->mData[lastp->mCount - 1];
+
+ // Now, we delete the entry
+ lastp->mCount--;
+ lastp->mData[lastp->mCount] = mNull;
+
+ if (!lastp->mCount)
+ {
+ // We deleted the last element!
+ if (lastp != &mNodes[local_id & 0xff])
+ {
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete lastp;
+ }
+ }
+
+ return TRUE;
+ }
+ }
+
+ // Iterate to the next node, we've scanned all the entries in this one.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return FALSE;
+}
+
+template <class DATA_TYPE, int SIZE>
+BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::removeWithShift(const U32 local_id)
+{
+ const S32 node_index = local_id & 0xFF;
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* prevp = NULL;
+ BOOL found = FALSE;
+
+ do // First node guaranteed to be there
+ {
+ const S32 count = nodep->mCount;
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // Found the item. Start shifting items from later
+ // in the list over this item.
+ found = TRUE;
+ }
+
+ if (found)
+ {
+ // If there is an iterator on this node, we need to
+ // back it up.
+ S32 cur_iter;
+ for (cur_iter = 0; cur_iter <MAX_ITERS; cur_iter++)
+ {
+ LLLocalIDHashMapIter<DATA_TYPE, SIZE>* iter;
+ iter = mIters[cur_iter];
+ // If an iterator is on this node,i pair, then back it up.
+ if (iter
+ && iter->mCurHashMapNodeNum == node_index
+ && iter->mCurHashNodep == nodep
+ && iter->mCurHashNodeKey == i)
+ {
+ if (i > 0)
+ {
+ // Don't need to move iterator nodep, since
+ // we're in the same node.
+ iter->mCurHashNodeKey--;
+ }
+ else if (prevp)
+ {
+ // need to go the previous node, last item
+ iter->mCurHashNodep = prevp;
+ iter->mCurHashNodeKey = prevp->mCount - 1;
+ }
+ else
+ {
+ // we're on the first item in the list, but
+ // need to go back anyhow.
+
+ // BUG: If this deletion empties the list,
+ // iter->done() will be wrong until
+ // iter->next() is called.
+ iter->mCurHashNodeKey = -1;
+ }
+ }
+ }
+
+ // Copy data from the next position into this position.
+ if (i < count-1)
+ {
+ // we're not on the last item in the node,
+ // so we can copy within the node
+ nodep->mKey[i] = nodep->mKey[i+1];
+ nodep->mData[i] = nodep->mData[i+1];
+ }
+ else if (nodep->mNextNodep)
+ {
+ // we're on the last item in the node,
+ // but there's a next node we can copy from
+ nodep->mKey[i] = nodep->mNextNodep->mKey[0];
+ nodep->mData[i] = nodep->mNextNodep->mData[0];
+ }
+ else
+ {
+ // We're on the last position in the list.
+ // No one to copy from. Replace with nothing.
+ nodep->mKey[i] = 0;
+ nodep->mData[i] = mNull;
+ }
+ }
+ }
+
+ // Last node in chain, so delete the last node
+ if (found
+ && !nodep->mNextNodep)
+ {
+ // delete the last item off the last node
+ nodep->mCount--;
+
+ if (nodep->mCount == 0)
+ {
+ // We deleted the last element!
+ if (nodep != &mNodes[node_index])
+ {
+ // Always have a prevp if we're not the head.
+ llassert(prevp);
+
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete nodep;
+ nodep = NULL;
+ }
+ }
+
+ // Deleted last item in chain, so we're done.
+ return found;
+ }
+
+ prevp = nodep;
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return found;
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i] == iter)
+ {
+ mIters[i] = NULL;
+ mIterCount--;
+ return;
+ }
+ }
+ llerrs << "Iterator " << iter << " not found for removal in hash map!" << llendl;
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i] == NULL)
+ {
+ mIters[i] = iter;
+ mIterCount++;
+ return;
+ }
+ }
+ llerrs << "More than " << MAX_ITERS << " iterating over a map simultaneously!" << llendl;
+}
+
+
+
+//
+// LLLocalIDHashMapIter Implementation
+//
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMapIter<DATA_TYPE, SIZE>::LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ mHashMapp = NULL;
+ setMap(hash_mapp);
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMapIter<DATA_TYPE, SIZE>::~LLLocalIDHashMapIter()
+{
+ if (mHashMapp)
+ {
+ mHashMapp->removeIter(this);
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ if (mHashMapp)
+ {
+ mHashMapp->removeIter(this);
+ }
+ mHashMapp = hash_mapp;
+ if (mHashMapp)
+ {
+ mHashMapp->addIter(this);
+ }
+
+ mCurHashNodep = NULL;
+ mCurHashMapNodeNum = -1;
+ mCurHashNodeKey = 0;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::first()
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ for (i = 0; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // Completely empty!
+ mCurHashNodep = NULL;
+ //return mNull;
+ return;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMapIter<DATA_TYPE, SIZE>::done() const
+{
+ return mCurHashNodep ? FALSE : TRUE;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLLocalIDHashMapIter<DATA_TYPE, SIZE>::currentBin() const
+{
+ if ( (mCurHashMapNodeNum > 255)
+ ||(mCurHashMapNodeNum < 0))
+ {
+ return 0;
+ }
+ else
+ {
+ return mCurHashMapNodeNum;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setBin(S32 bin)
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ bin = llclamp(bin, 0, 255);
+ for (i = bin; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ return;
+ }
+ }
+ for (i = 0; i < bin; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ return;
+ }
+ }
+ // Completely empty!
+ mCurHashNodep = NULL;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMapIter<DATA_TYPE, SIZE>::current()
+{
+ if (!mCurHashNodep)
+ {
+ return mNull;
+ }
+ return mCurHashNodep->mData[mCurHashNodeKey];
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::next()
+{
+ // No current entry, this iterator is done
+ if (!mCurHashNodep)
+ {
+ //return mNull;
+ return;
+ }
+
+ // Go to the next element
+ mCurHashNodeKey++;
+ if (mCurHashNodeKey < mCurHashNodep->mCount)
+ {
+ // We're not done with this node, return the current element
+ //return mCurHashNodep->mData[mCurHashNodeKey];
+ return;
+ }
+
+ // Done with this node, move to the next
+ mCurHashNodep = mCurHashNodep->mNextNodep;
+ if (mCurHashNodep)
+ {
+ // Return the first element
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+
+ // Find the next non-empty node (keyed on the first byte)
+ mCurHashMapNodeNum++;
+
+ S32 i;
+ for (i = mCurHashMapNodeNum; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ // We found one that wasn't empty
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // OK, we're done, nothing else to iterate
+ mCurHashNodep = NULL;
+ mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
+ //return mNull;
+ return;
+}
+
+#endif // LL_LLLOCALIDHASHMAP_H
diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h
new file mode 100644
index 0000000000..5c9017e43b
--- /dev/null
+++ b/indra/llcommon/lllslconstants.h
@@ -0,0 +1,139 @@
+/**
+ * @file lllslconstants.h
+ * @author James Cook
+ * @brief Constants used in lsl.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLSLCONSTANTS_H
+#define LL_LLLSLCONSTANTS_H
+
+// LSL: Return flags for llGetAgentInfo
+const U32 AGENT_FLYING = 0x0001;
+const U32 AGENT_ATTACHMENTS = 0x0002;
+const U32 AGENT_SCRIPTED = 0x0004;
+const U32 AGENT_MOUSELOOK = 0x0008;
+const U32 AGENT_SITTING = 0x0010;
+const U32 AGENT_ON_OBJECT = 0x0020;
+const U32 AGENT_AWAY = 0x0040;
+const U32 AGENT_WALKING = 0x0080;
+const U32 AGENT_IN_AIR = 0x0100;
+const U32 AGENT_TYPING = 0x0200;
+const U32 AGENT_CROUCHING = 0x0400;
+const U32 AGENT_BUSY = 0x0800;
+const U32 AGENT_ALWAYS_RUN = 0x1000;
+
+const S32 LSL_REMOTE_DATA_CHANNEL = 1;
+const S32 LSL_REMOTE_DATA_REQUEST = 2;
+const S32 LSL_REMOTE_DATA_REPLY = 3;
+
+// Constants used in extended LSL primitive setter and getters
+const S32 LSL_PRIM_TYPE_LEGACY = 1; // No longer supported.
+const S32 LSL_PRIM_MATERIAL = 2;
+const S32 LSL_PRIM_PHYSICS = 3;
+const S32 LSL_PRIM_TEMP_ON_REZ = 4;
+const S32 LSL_PRIM_PHANTOM = 5;
+const S32 LSL_PRIM_POSITION = 6;
+const S32 LSL_PRIM_SIZE = 7;
+const S32 LSL_PRIM_ROTATION = 8;
+const S32 LSL_PRIM_TYPE = 9; // Replacement for LSL_PRIM_TYPE_LEGACY
+const S32 LSL_PRIM_TEXTURE = 17;
+const S32 LSL_PRIM_COLOR = 18;
+const S32 LSL_PRIM_BUMP_SHINY = 19;
+const S32 LSL_PRIM_FULLBRIGHT = 20;
+const S32 LSL_PRIM_FLEXIBLE = 21;
+const S32 LSL_PRIM_TEXGEN = 22;
+const S32 LSL_PRIM_POINT_LIGHT = 23;
+const S32 LSL_PRIM_CAST_SHADOWS = 24;
+
+const S32 LSL_PRIM_TYPE_BOX = 0;
+const S32 LSL_PRIM_TYPE_CYLINDER= 1;
+const S32 LSL_PRIM_TYPE_PRISM = 2;
+const S32 LSL_PRIM_TYPE_SPHERE = 3;
+const S32 LSL_PRIM_TYPE_TORUS = 4;
+const S32 LSL_PRIM_TYPE_TUBE = 5;
+const S32 LSL_PRIM_TYPE_RING = 6;
+
+const S32 LSL_PRIM_HOLE_DEFAULT = 0x00;
+const S32 LSL_PRIM_HOLE_CIRCLE = 0x10;
+const S32 LSL_PRIM_HOLE_SQUARE = 0x20;
+const S32 LSL_PRIM_HOLE_TRIANGLE= 0x30;
+
+const S32 LSL_PRIM_MATERIAL_STONE = 0;
+const S32 LSL_PRIM_MATERIAL_METAL = 1;
+const S32 LSL_PRIM_MATERIAL_GLASS = 2;
+const S32 LSL_PRIM_MATERIAL_WOOD = 3;
+const S32 LSL_PRIM_MATERIAL_FLESH = 4;
+const S32 LSL_PRIM_MATERIAL_PLASTIC = 5;
+const S32 LSL_PRIM_MATERIAL_RUBBER = 6;
+const S32 LSL_PRIM_MATERIAL_LIGHT = 7;
+
+const S32 LSL_PRIM_SHINY_NONE = 0;
+const S32 LSL_PRIM_SHINY_LOW = 1;
+const S32 LSL_PRIM_SHINY_MEDIUM = 2;
+const S32 LSL_PRIM_SHINY_HIGH = 3;
+
+const S32 LSL_PRIM_TEXGEN_DEFAULT = 0;
+const S32 LSL_PRIM_TEXGEN_PLANAR = 1;
+
+const S32 LSL_PRIM_BUMP_NONE = 0;
+const S32 LSL_PRIM_BUMP_BRIGHT = 1;
+const S32 LSL_PRIM_BUMP_DARK = 2;
+const S32 LSL_PRIM_BUMP_WOOD = 3;
+const S32 LSL_PRIM_BUMP_BARK = 4;
+const S32 LSL_PRIM_BUMP_BRICKS = 5;
+const S32 LSL_PRIM_BUMP_CHECKER = 6;
+const S32 LSL_PRIM_BUMP_CONCRETE = 7;
+const S32 LSL_PRIM_BUMP_TILE = 8;
+const S32 LSL_PRIM_BUMP_STONE = 9;
+const S32 LSL_PRIM_BUMP_DISKS = 10;
+const S32 LSL_PRIM_BUMP_GRAVEL = 11;
+const S32 LSL_PRIM_BUMP_BLOBS = 12;
+const S32 LSL_PRIM_BUMP_SIDING = 13;
+const S32 LSL_PRIM_BUMP_LARGETILE = 14;
+const S32 LSL_PRIM_BUMP_STUCCO = 15;
+const S32 LSL_PRIM_BUMP_SUCTION = 16;
+const S32 LSL_PRIM_BUMP_WEAVE = 17;
+
+const S32 LSL_ALL_SIDES = -1;
+const S32 LSL_LINK_ROOT = 1;
+const S32 LSL_LINK_FIRST_CHILD = 2;
+const S32 LSL_LINK_SET = -1;
+const S32 LSL_LINK_ALL_OTHERS = -2;
+const S32 LSL_LINK_ALL_CHILDREN = -3;
+const S32 LSL_LINK_THIS = -4;
+
+// LSL constants for llSetForSell
+const S32 SELL_NOT = 0;
+const S32 SELL_ORIGINAL = 1;
+const S32 SELL_COPY = 2;
+const S32 SELL_CONTENTS = 3;
+
+// LSL constants for llSetPayPrice
+const S32 PAY_PRICE_HIDE = -1;
+const S32 PAY_PRICE_DEFAULT = -2;
+const S32 MAX_PAY_BUTTONS = 4;
+const S32 PAY_BUTTON_DEFAULT_0 = 1;
+const S32 PAY_BUTTON_DEFAULT_1 = 5;
+const S32 PAY_BUTTON_DEFAULT_2 = 10;
+const S32 PAY_BUTTON_DEFAULT_3 = 20;
+
+// lsl email registration.
+const S32 EMAIL_REG_SUBSCRIBE_OBJECT = 0x01;
+const S32 EMAIL_REG_UNSUBSCRIBE_OBJECT = 0x02;
+const S32 EMAIL_REG_UNSUBSCRIBE_SIM = 0x04;
+
+const S32 LIST_STAT_RANGE = 0;
+const S32 LIST_STAT_MIN = 1;
+const S32 LIST_STAT_MAX = 2;
+const S32 LIST_STAT_MEAN = 3;
+const S32 LIST_STAT_MEDIAN = 4;
+const S32 LIST_STAT_STD_DEV = 5;
+const S32 LIST_STAT_SUM = 6;
+const S32 LIST_STAT_SUM_SQUARES = 7;
+const S32 LIST_STAT_NUM_COUNT = 8;
+const S32 LIST_STAT_GEO_MEAN = 9;
+
+#endif
diff --git a/indra/llcommon/llmap.h b/indra/llcommon/llmap.h
new file mode 100644
index 0000000000..fc958421da
--- /dev/null
+++ b/indra/llcommon/llmap.h
@@ -0,0 +1,231 @@
+/**
+ * @file llmap.h
+ * @brief LLMap class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMAP_H
+#define LL_LLMAP_H
+
+#include <stdio.h>
+#include <utility>
+#include <map>
+
+// llmap uses the fast stl library code in a manner consistant with LLSkipMap, et. al.
+
+template<class INDEX_TYPE, class MAPPED_TYPE> class LLMap
+{
+private:
+ typedef typename std::map<INDEX_TYPE, MAPPED_TYPE> stl_map_t;
+ typedef typename stl_map_t::iterator stl_iter_t;
+ typedef typename stl_map_t::value_type stl_value_t;
+
+ stl_map_t mStlMap;
+ stl_iter_t mCurIter; // *iterator = pair<const INDEX_TYPE, MAPPED_TYPE>
+ MAPPED_TYPE dummy_data;
+ INDEX_TYPE dummy_index;
+
+public:
+ LLMap() : mStlMap()
+ {
+ memset((void*)(&dummy_data), 0x0, sizeof(MAPPED_TYPE));
+ memset((void*)(&dummy_index), 0x0, sizeof(INDEX_TYPE));
+ mCurIter = mStlMap.begin();
+ }
+ ~LLMap()
+ {
+ mStlMap.clear();
+ }
+
+ // use these functions to itterate through a list
+ void resetMap()
+ {
+ mCurIter = mStlMap.begin();
+ }
+
+ // get the current data and bump mCurrentp
+ // This is kind of screwy since it returns a reference;
+ // We have to have a dummy value for when we reach the end
+ // or in case we have an empty list. Presumably, this value
+ // will initialize to some NULL value that will end the iterator.
+ // We really shouldn't be using getNextData() or getNextKey() anyway...
+ MAPPED_TYPE &getNextData()
+ {
+ if (mCurIter == mStlMap.end())
+ {
+ return dummy_data;
+ }
+ else
+ {
+ return (*mCurIter++).second;
+ }
+ }
+
+ const INDEX_TYPE &getNextKey()
+ {
+ if (mCurIter == mStlMap.end())
+ {
+ return dummy_index;
+ }
+ else
+ {
+ return (*mCurIter++).first;
+ }
+ }
+
+ MAPPED_TYPE &getFirstData()
+ {
+ resetMap();
+ return getNextData();
+ }
+
+ const INDEX_TYPE &getFirstKey()
+ {
+ resetMap();
+ return getNextKey();
+ }
+
+ S32 getLength()
+ {
+ return mStlMap.size();
+ }
+
+ void addData(const INDEX_TYPE &index, MAPPED_TYPE pointed_to)
+ {
+ mStlMap.insert(stl_value_t(index, pointed_to));
+ }
+
+ void addData(const INDEX_TYPE &index)
+ {
+ mStlMap.insert(stl_value_t(index, dummy_data));
+ }
+
+ // if index doesn't exist, then insert a new node and return it
+ MAPPED_TYPE &getData(const INDEX_TYPE &index)
+ {
+ std::pair<stl_iter_t, bool> res;
+ res = mStlMap.insert(stl_value_t(index, dummy_data));
+ return res.first->second;
+ }
+
+ // if index doesn't exist, then insert a new node, return it, and set b_new_entry to true
+ MAPPED_TYPE &getData(const INDEX_TYPE &index, BOOL &b_new_entry)
+ {
+ std::pair<stl_iter_t, bool> res;
+ res = mStlMap.insert(stl_value_t(index, dummy_data));
+ b_new_entry = res.second;
+ return res.first->second;
+ }
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ MAPPED_TYPE getIfThere(const INDEX_TYPE &index)
+ {
+ stl_iter_t iter;
+ iter = mStlMap.find(index);
+ if (iter == mStlMap.end())
+ {
+ return (MAPPED_TYPE)0;
+ }
+ else
+ {
+ return (*iter).second;
+ }
+ }
+
+
+ // if index doesn't exist, then make a new node and return it
+ MAPPED_TYPE &operator[](const INDEX_TYPE &index)
+ {
+ return getData(index);
+ }
+
+ // do a reverse look-up, return NULL if failed
+ INDEX_TYPE reverseLookup(const MAPPED_TYPE data)
+ {
+ stl_iter_t iter;
+ stl_iter_t end_iter;
+ iter = mStlMap.begin();
+ end_iter = mStlMap.end();
+ while (iter != end_iter)
+ {
+ if ((*iter).second == data)
+ return (*iter).first;
+ iter++;
+ }
+ return (INDEX_TYPE)0;
+ }
+
+ BOOL removeData(const INDEX_TYPE &index)
+ {
+ mCurIter = mStlMap.find(index);
+ if (mCurIter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ stl_iter_t iter = mCurIter++; // incrament mCurIter to the next element
+ mStlMap.erase(iter);
+ return TRUE;
+ }
+ }
+
+ // does this index exist?
+ BOOL checkData(const INDEX_TYPE &index)
+ {
+ stl_iter_t iter;
+ iter = mStlMap.find(index);
+ if (iter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ mCurIter = iter;
+ return TRUE;
+ }
+ }
+
+ BOOL deleteData(const INDEX_TYPE &index)
+ {
+ mCurIter = mStlMap.find(index);
+ if (mCurIter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ stl_iter_t iter = mCurIter++; // incrament mCurIter to the next element
+ delete (*iter).second;
+ mStlMap.erase(iter);
+ return TRUE;
+ }
+ }
+
+ void deleteAllData()
+ {
+ stl_iter_t iter;
+ stl_iter_t end_iter;
+ iter = mStlMap.begin();
+ end_iter = mStlMap.end();
+ while (iter != end_iter)
+ {
+ delete (*iter).second;
+ iter++;
+ }
+ mStlMap.clear();
+ mCurIter = mStlMap.end();
+ }
+
+ void removeAllData()
+ {
+ mStlMap.clear();
+ }
+};
+
+
+#endif
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
new file mode 100644
index 0000000000..fd2c408fd4
--- /dev/null
+++ b/indra/llcommon/llmemory.cpp
@@ -0,0 +1,293 @@
+/**
+ * @file llmemory.cpp
+ * @brief Very special memory allocation/deallocation stuff here
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmemory.h"
+
+//----------------------------------------------------------------------------
+
+//static
+char* LLMemory::reserveMem = 0;
+
+//static
+void LLMemory::initClass()
+{
+ if (!reserveMem)
+ {
+ reserveMem = new char[16*1024]; // reserve 16K for out of memory error handling
+ }
+}
+
+//static
+void LLMemory::cleanupClass()
+{
+ delete [] reserveMem;
+ reserveMem = NULL;
+}
+
+//static
+void LLMemory::freeReserve()
+{
+ delete [] reserveMem;
+ reserveMem = NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+//static
+#if MEM_TRACK_TYPE
+S32 LLMemType::sCurDepth = 0;
+S32 LLMemType::sCurType = LLMemType::MTYPE_INIT;
+S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH];
+S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
+S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
+S32 LLMemType::sOverheadMem = 0;
+
+const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] =
+{
+ "INIT",
+ "STARTUP",
+ "MAIN",
+
+ "IMAGEBASE",
+ "IMAGERAW",
+ "IMAGEFORMATTED",
+
+ "APPFMTIMAGE",
+ "APPRAWIMAGE",
+ "APPAUXRAWIMAGE",
+
+ "DRAWABLE",
+ "OBJECT",
+ "PIPELINE",
+ "AVATAR",
+ "PARTICLES",
+ "REGIONS",
+ "INVENTORY",
+ "ANIMATION",
+ "NETWORK",
+ "PHYSICS",
+ "INTERESTLIST",
+
+ "SCRIPT",
+ "SCRIPT_RUN",
+ "SCRIPT_BYTECODE",
+
+ "IO_PUMP",
+ "IO_TCP",
+ "IO_BUFFER",
+ "IO_HTTP_SERVER"
+ "IO_SD_SERVER",
+ "IO_SD_CLIENT",
+ "IO_URL_REQUEST",
+
+ "TEMP1",
+ "TEMP2",
+ "TEMP3",
+ "TEMP4",
+ "TEMP5",
+ "TEMP6",
+ "TEMP7",
+ "TEMP8",
+ "TEMP9"
+};
+
+#endif
+S32 LLMemType::sTotalMem = 0;
+S32 LLMemType::sMaxTotalMem = 0;
+
+//static
+void LLMemType::printMem()
+{
+ S32 misc_mem = sTotalMem;
+#if MEM_TRACK_TYPE
+ for (S32 i=0; i<MTYPE_NUM_TYPES; i++)
+ {
+ if (sMemCount[i])
+ {
+ llinfos << llformat("MEM: % 20s %03d MB (%03d MB)",sTypeDesc[i],sMemCount[i]>>20,sMaxMemCount[i]>>20) << llendl;
+ }
+ misc_mem -= sMemCount[i];
+ }
+#endif
+ llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl;
+ llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl;
+}
+
+#if MEM_TRACK_MEM
+
+void* ll_allocate (size_t size)
+{
+ if (size == 0)
+ {
+ llwarns << "Null allocation" << llendl;
+ }
+
+ size = (size+3)&~3;
+ S32 alloc_size = size + 4;
+#if MEM_TRACK_TYPE
+ alloc_size += 4;
+#endif
+ char* p = (char*)malloc(alloc_size);
+ if (p == NULL)
+ {
+ LLMemory::freeReserve();
+ llerrs << "Out of memory Error" << llendl;
+ }
+ LLMemType::sTotalMem += size;
+ LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem);
+ LLMemType::sOverheadMem += 4;
+ *(size_t*)p = size;
+ p += 4;
+#if MEM_TRACK_TYPE
+ if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES)
+ {
+ llerrs << "Memory Type Error: new" << llendl;
+ }
+ LLMemType::sOverheadMem += 4;
+ *(S32*)p = LLMemType::sCurType;
+ p += 4;
+ LLMemType::sMemCount[LLMemType::sCurType] += size;
+ if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType])
+ {
+ LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType];
+ }
+#endif
+ return (void*)p;
+}
+
+void ll_release (void *pin)
+{
+ if (!pin)
+ {
+ return;
+ }
+ char* p = (char*)pin;
+#if MEM_TRACK_TYPE
+ p -= 4;
+ S32 type = *(S32*)p;
+ if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES)
+ {
+ llerrs << "Memory Type Error: delete" << llendl;
+ }
+#endif
+ p -= 4;
+ S32 size = *(size_t*)p;
+ LLMemType::sOverheadMem -= 4;
+#if MEM_TRACK_TYPE
+ LLMemType::sMemCount[type] -= size;
+ LLMemType::sOverheadMem -= 4;
+#endif
+ LLMemType::sTotalMem -= size;
+ free(p);
+}
+
+#else
+
+void* ll_allocate (size_t size)
+{
+ if (size == 0)
+ {
+ llwarns << "Null allocation" << llendl;
+ }
+ void *p = malloc(size);
+ if (p == NULL)
+ {
+ LLMemory::freeReserve();
+ llerrs << "Out of memory Error" << llendl;
+ }
+ return p;
+}
+
+void ll_release (void *p)
+{
+ free(p);
+}
+
+#endif
+
+#if MEM_TRACK_MEM
+
+void* operator new (size_t size)
+{
+ return ll_allocate(size);
+}
+
+void* operator new[] (size_t size)
+{
+ return ll_allocate(size);
+}
+
+void operator delete (void *p)
+{
+ ll_release(p);
+}
+
+void operator delete[] (void *p)
+{
+ ll_release(p);
+}
+
+#endif
+
+//----------------------------------------------------------------------------
+
+//static
+LLMutex* LLThreadSafeRefCount::sMutex = 0;
+
+//static
+void LLThreadSafeRefCount::initClass()
+{
+ if (!sMutex)
+ {
+ sMutex = new LLMutex(0);
+ }
+}
+
+//static
+void LLThreadSafeRefCount::cleanupClass()
+{
+ delete sMutex;
+ sMutex = NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+LLThreadSafeRefCount::LLThreadSafeRefCount() :
+ mRef(0)
+{
+}
+
+LLThreadSafeRefCount::~LLThreadSafeRefCount()
+{
+ if (mRef != 0)
+ {
+ llerrs << "deleting non-zero reference" << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+LLRefCount::LLRefCount() :
+ mRef(0)
+{
+}
+
+LLRefCount::~LLRefCount()
+{
+ if (mRef != 0)
+ {
+ llerrs << "deleting non-zero reference" << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
new file mode 100644
index 0000000000..905d05254a
--- /dev/null
+++ b/indra/llcommon/llmemory.h
@@ -0,0 +1,300 @@
+/**
+ * @file llmemory.h
+ * @brief Memory allocation/deallocation header-stuff goes here.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_MEMORY_H
+#define LL_MEMORY_H
+
+#include <new>
+#include <cstdlib>
+
+#include "llerror.h"
+#include "llthread.h"
+#include "llmemtype.h"
+
+extern S32 gTotalDAlloc;
+extern S32 gTotalDAUse;
+extern S32 gDACount;
+
+const U32 LLREFCOUNT_SENTINEL_VALUE = 0xAAAAAAAA;
+
+//----------------------------------------------------------------------------
+
+class LLMemory
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+ static void freeReserve();
+private:
+ static char* reserveMem;
+};
+
+//----------------------------------------------------------------------------
+// RefCount objects should generally only be accessed by way of LLPointer<>'s
+// NOTE: LLPointer<LLFoo> x = new LLFoo(); MAY NOT BE THREAD SAFE
+// if LLFoo::LLFoo() does anything like put itself in an update queue.
+// The queue may get accessed before it gets assigned to x.
+// The correct implementation is:
+// LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting
+// x->instantiate(); // does stuff like place x into an update queue
+
+class LLThreadSafeRefCount
+{
+public:
+ static void initClass(); // creates sMutex
+ static void cleanupClass(); // destroys sMutex
+
+private:
+ static LLMutex* sMutex;
+
+private:
+ LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
+ LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
+
+protected:
+ virtual ~LLThreadSafeRefCount(); // use unref()
+
+public:
+ LLThreadSafeRefCount();
+
+ void ref()
+ {
+ if (sMutex) sMutex->lock();
+ mRef++;
+ if (sMutex) sMutex->unlock();
+ }
+
+ S32 unref()
+ {
+ llassert(mRef >= 1);
+ if (sMutex) sMutex->lock();
+ S32 res = --mRef;
+ if (sMutex) sMutex->unlock();
+ if (0 == res)
+ {
+ delete this;
+ res = 0;
+ }
+ return res;
+ }
+ S32 getNumRefs() const
+ {
+ return mRef;
+ }
+
+private:
+ S32 mRef;
+};
+
+//----------------------------------------------------------------------------
+
+class LLRefCount
+{
+protected:
+ LLRefCount(const LLRefCount&); // not implemented
+private:
+ LLRefCount&operator=(const LLRefCount&); // not implemented
+
+protected:
+ virtual ~LLRefCount(); // use unref()
+
+public:
+ LLRefCount();
+
+ void ref()
+ {
+ mRef++;
+ }
+
+ S32 unref()
+ {
+ llassert(mRef >= 1);
+ if (0 == --mRef)
+ {
+ delete this;
+ return 0;
+ }
+ return mRef;
+ }
+ S32 getNumRefs() const
+ {
+ return mRef;
+ }
+
+private:
+ S32 mRef;
+};
+
+//----------------------------------------------------------------------------
+
+template <class Type> class LLPointer
+{
+public:
+
+ LLPointer() :
+ mPointer(NULL)
+ {
+ }
+
+ LLPointer(Type* ptr) :
+ mPointer(ptr)
+ {
+ ref();
+ }
+
+ LLPointer(const LLPointer<Type>& ptr) :
+ mPointer(ptr.mPointer)
+ {
+ ref();
+ }
+
+ // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
+ template<typename Subclass>
+ LLPointer(const LLPointer<Subclass>& ptr) :
+ mPointer(ptr.get())
+ {
+ ref();
+ }
+
+ ~LLPointer()
+ {
+ unref();
+ }
+
+ Type* get() const { return mPointer; }
+ const Type* operator->() const { return mPointer; }
+ Type* operator->() { return mPointer; }
+ const Type& operator*() const { return *mPointer; }
+ Type& operator*() { return *mPointer; }
+
+ operator BOOL() const { return (mPointer != NULL); }
+ operator bool() const { return (mPointer != NULL); }
+ bool operator!() const { return (mPointer == NULL); }
+ bool isNull() const { return (mPointer == NULL); }
+ bool notNull() const { return (mPointer != NULL); }
+
+ operator Type*() const { return mPointer; }
+ operator const Type*() const { return mPointer; }
+ bool operator !=(Type* ptr) const { return (mPointer != ptr); }
+ bool operator ==(Type* ptr) const { return (mPointer == ptr); }
+ bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); }
+ bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
+ bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
+
+ LLPointer<Type>& operator =(Type* ptr)
+ {
+ if( mPointer != ptr )
+ {
+ unref();
+ mPointer = ptr;
+ ref();
+ }
+
+ return *this;
+ }
+
+ LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
+ {
+ if( mPointer != ptr.mPointer )
+ {
+ unref();
+ mPointer = ptr.mPointer;
+ ref();
+ }
+ return *this;
+ }
+
+ // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
+ template<typename Subclass>
+ LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr)
+ {
+ if( mPointer != ptr.get() )
+ {
+ unref();
+ mPointer = ptr.get();
+ ref();
+ }
+ return *this;
+ }
+
+protected:
+ void ref()
+ {
+ if (mPointer)
+ {
+ mPointer->ref();
+ }
+ }
+
+ void unref()
+ {
+ if (mPointer)
+ {
+ Type *tempp = mPointer;
+ mPointer = NULL;
+ tempp->unref();
+ if (mPointer != NULL)
+ {
+ llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl;
+ unref();
+ }
+ }
+ }
+
+protected:
+ Type* mPointer;
+};
+
+// LLInitializedPointer is just a pointer with a default constructor that initializes it to NULL
+// NOT a smart pointer like LLPointer<>
+// Useful for example in std::map<int,LLInitializedPointer<LLFoo> >
+// (std::map uses the default constructor for creating new entries)
+template <typename T> class LLInitializedPointer
+{
+public:
+ LLInitializedPointer() : mPointer(NULL) {}
+ ~LLInitializedPointer() { delete mPointer; }
+
+ const T* operator->() const { return mPointer; }
+ T* operator->() { return mPointer; }
+ const T& operator*() const { return *mPointer; }
+ T& operator*() { return *mPointer; }
+ operator const T*() const { return mPointer; }
+ operator T*() { return mPointer; }
+ T* operator=(T* x) { return (mPointer = x); }
+ operator bool() const { return mPointer != NULL; }
+ bool operator!() const { return mPointer == NULL; }
+ bool operator==(T* rhs) { return mPointer == rhs; }
+ bool operator==(const LLInitializedPointer<T>* rhs) { return mPointer == rhs.mPointer; }
+
+protected:
+ T* mPointer;
+};
+
+//----------------------------------------------------------------------------
+
+// LLSingleton implements the getInstance() method part of the Singleton pattern. It can't make
+// the derived class constructors protected, though, so you have to do that yourself.
+// The proper way to use LLSingleton is to inherit from it while using the typename that you'd
+// like to be static as the template parameter, like so:
+// class FooBar: public LLSingleton<FooBar>
+// As currently written, it is not thread-safe.
+template <typename T>
+class LLSingleton
+{
+public:
+ static T* getInstance()
+ {
+ static T instance;
+ return &instance;
+ }
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmemorystream.cpp b/indra/llcommon/llmemorystream.cpp
new file mode 100644
index 0000000000..dc0ad5aadb
--- /dev/null
+++ b/indra/llcommon/llmemorystream.cpp
@@ -0,0 +1,52 @@
+/**
+ * @file llmemorystream.cpp
+ * @author Phoenix
+ * @date 2005-06-03
+ * @brief Buffer and stream for a fixed linear memory segment.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmemorystream.h"
+
+LLMemoryStreamBuf::LLMemoryStreamBuf(const U8* start, S32 length)
+{
+ reset(start, length);
+}
+
+LLMemoryStreamBuf::~LLMemoryStreamBuf()
+{
+}
+
+void LLMemoryStreamBuf::reset(const U8* start, S32 length)
+{
+ setg((char*)start, (char*)start, (char*)start + length);
+}
+
+int LLMemoryStreamBuf::underflow()
+{
+ //lldebugs << "LLMemoryStreamBuf::underflow()" << llendl;
+ if(gptr() < egptr())
+ {
+ return *gptr();
+ }
+ return EOF;
+}
+
+/**
+ * @class LLMemoryStreamBuf
+ */
+
+LLMemoryStream::LLMemoryStream(const U8* start, S32 length) :
+ std::istream(&mStreamBuf),
+ mStreamBuf(start, length)
+{
+}
+
+LLMemoryStream::~LLMemoryStream()
+{
+}
+
+
diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h
new file mode 100644
index 0000000000..7553c4a425
--- /dev/null
+++ b/indra/llcommon/llmemorystream.h
@@ -0,0 +1,63 @@
+/**
+ * @file llmemorystream.h
+ * @author Phoenix
+ * @date 2005-06-03
+ * @brief Implementation of a simple fixed memory stream
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMEMORYSTREAM_H
+#define LL_LLMEMORYSTREAM_H
+
+/**
+ * This is a simple but effective optimization when you want to treat
+ * a chunk of memory as an istream. I wrote this to avoid turing a
+ * buffer into a string, and then throwing the string into an
+ * iostringstream just to parse it into another datatype, eg, LLSD.
+ */
+
+#include <iostream>
+
+/**
+ * @class LLMemoryStreamBuf
+ * @brief This implements a wrapper around a piece of memory for istreams
+ *
+ * The memory passed in is NOT owned by an instance. The caller must
+ * be careful to always pass in a valid memory location that exists
+ * for at least as long as this streambuf.
+ */
+class LLMemoryStreamBuf : public std::streambuf
+{
+public:
+ LLMemoryStreamBuf(const U8* start, S32 length);
+ ~LLMemoryStreamBuf();
+
+ void reset(const U8* start, S32 length);
+
+protected:
+ int underflow();
+ //std::streamsize xsgetn(char* dest, std::streamsize n);
+};
+
+
+/**
+ * @class LLMemoryStream
+ * @brief This implements a wrapper around a piece of memory for istreams
+ *
+ * The memory passed in is NOT owned by an instance. The caller must
+ * be careful to always pass in a valid memory location that exists
+ * for at least as long as this streambuf.
+ */
+class LLMemoryStream : public std::istream
+{
+public:
+ LLMemoryStream(const U8* start, S32 length);
+ ~LLMemoryStream();
+
+protected:
+ LLMemoryStreamBuf mStreamBuf;
+};
+
+#endif // LL_LLMEMORYSTREAM_H
diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h
new file mode 100644
index 0000000000..17afaa6a8a
--- /dev/null
+++ b/indra/llcommon/llmemtype.h
@@ -0,0 +1,135 @@
+/**
+ * @file llmemtype.h
+ * @brief Runtime memory usage debugging utilities.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MEMTYPE_H
+#define LL_MEMTYPE_H
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+class LLMemType;
+
+extern void* ll_allocate (size_t size);
+extern void ll_release (void *p);
+
+#define MEM_TRACK_MEM 0
+#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM)
+
+#if MEM_TRACK_TYPE
+#define MEM_TYPE_NEW(T) \
+static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \
+static void operator delete(void* p) { ll_release(p); }
+
+#else
+#define MEM_TYPE_NEW(T)
+#endif // MEM_TRACK_TYPE
+
+
+//----------------------------------------------------------------------------
+
+class LLMemType
+{
+public:
+ // Also update sTypeDesc in llmemory.cpp
+ enum EMemType
+ {
+ MTYPE_INIT,
+ MTYPE_STARTUP,
+ MTYPE_MAIN,
+
+ MTYPE_IMAGEBASE,
+ MTYPE_IMAGERAW,
+ MTYPE_IMAGEFORMATTED,
+
+ MTYPE_APPFMTIMAGE,
+ MTYPE_APPRAWIMAGE,
+ MTYPE_APPAUXRAWIMAGE,
+
+ MTYPE_DRAWABLE,
+ MTYPE_OBJECT,
+ MTYPE_SPACE_PARTITION,
+ MTYPE_PIPELINE,
+ MTYPE_AVATAR,
+ MTYPE_PARTICLES,
+ MTYPE_REGIONS,
+ MTYPE_INVENTORY,
+ MTYPE_ANIMATION,
+ MTYPE_NETWORK,
+ MTYPE_PHYSICS,
+ MTYPE_INTERESTLIST,
+
+ MTYPE_SCRIPT,
+ MTYPE_SCRIPT_RUN,
+ MTYPE_SCRIPT_BYTECODE,
+
+ MTYPE_IO_PUMP,
+ MTYPE_IO_TCP,
+ MTYPE_IO_BUFFER,
+ MTYPE_IO_HTTP_SERVER,
+ MTYPE_IO_SD_SERVER,
+ MTYPE_IO_SD_CLIENT,
+ MTYPE_IO_URL_REQUEST,
+
+ MTYPE_TEMP1,
+ MTYPE_TEMP2,
+ MTYPE_TEMP3,
+ MTYPE_TEMP4,
+ MTYPE_TEMP5,
+ MTYPE_TEMP6,
+ MTYPE_TEMP7,
+ MTYPE_TEMP8,
+ MTYPE_TEMP9,
+
+ MTYPE_OTHER, // Special, used by display code
+
+ MTYPE_NUM_TYPES
+ };
+ enum { MTYPE_MAX_DEPTH = 64 };
+
+public:
+ LLMemType(EMemType type)
+ {
+#if MEM_TRACK_TYPE
+ if (type < 0 || type >= MTYPE_NUM_TYPES)
+ llerrs << "LLMemType error" << llendl;
+ if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH)
+ llerrs << "LLMemType error" << llendl;
+ sType[sCurDepth] = sCurType;
+ sCurDepth++;
+ sCurType = type;
+#endif
+ }
+ ~LLMemType()
+ {
+#if MEM_TRACK_TYPE
+ sCurDepth--;
+ sCurType = sType[sCurDepth];
+#endif
+ }
+
+ static void reset();
+ static void printMem();
+
+public:
+#if MEM_TRACK_TYPE
+ static S32 sCurDepth;
+ static S32 sCurType;
+ static S32 sType[MTYPE_MAX_DEPTH];
+ static S32 sMemCount[MTYPE_NUM_TYPES];
+ static S32 sMaxMemCount[MTYPE_NUM_TYPES];
+ static S32 sOverheadMem;
+ static const char* sTypeDesc[MTYPE_NUM_TYPES];
+#endif
+ static S32 sTotalMem;
+ static S32 sMaxTotalMem;
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmortician.cpp b/indra/llcommon/llmortician.cpp
new file mode 100644
index 0000000000..eddfbb559e
--- /dev/null
+++ b/indra/llcommon/llmortician.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file llmortician.cpp
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llmortician.h"
+
+#include <list>
+
+std::list<LLMortician*> gGraveyard;
+
+BOOL LLMortician::sDestroyImmediate = FALSE;
+
+LLMortician::~LLMortician()
+{
+ gGraveyard.remove(this);
+}
+
+void LLMortician::updateClass()
+{
+ while (!gGraveyard.empty())
+ {
+ LLMortician* dead = gGraveyard.front();
+ delete dead;
+ }
+}
+
+void LLMortician::die()
+{
+ // It is valid to call die() more than once on something that hasn't died yet
+ if (sDestroyImmediate)
+ {
+ //HACK: we need to do this to ensure destruction order on shutdown
+ mIsDead = TRUE;
+ delete this;
+ return;
+ }
+ else if (!mIsDead)
+ {
+ mIsDead = TRUE;
+ gGraveyard.push_back(this);
+ }
+}
+
+// static
+void LLMortician::setZealous(BOOL b)
+{
+ sDestroyImmediate = b;
+}
diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h
new file mode 100644
index 0000000000..6e69f1af0b
--- /dev/null
+++ b/indra/llcommon/llmortician.h
@@ -0,0 +1,33 @@
+/**
+ * @file llmortician.h
+ * @brief Base class for delayed deletions.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLMORTICIAN_H
+#define LLMORTICIAN_H
+
+#include "stdtypes.h"
+
+class LLMortician
+{
+public:
+ LLMortician() { mIsDead = FALSE; }
+ static void updateClass();
+ virtual ~LLMortician();
+ void die();
+ BOOL isDead() { return mIsDead; }
+
+ // sets destroy immediate true
+ static void setZealous(BOOL b);
+
+private:
+ static BOOL sDestroyImmediate;
+
+ BOOL mIsDead;
+};
+
+#endif
+
diff --git a/indra/llcommon/llnametable.h b/indra/llcommon/llnametable.h
new file mode 100644
index 0000000000..d05885402d
--- /dev/null
+++ b/indra/llcommon/llnametable.h
@@ -0,0 +1,87 @@
+/**
+ * @file llnametable.h
+ * @brief LLNameTable class is a table to associate pointers with string names
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMETABLE_H
+#define LL_LLNAMETABLE_H
+
+#include <map>
+
+#include "string_table.h"
+
+template <class DATA>
+class LLNameTable
+{
+public:
+ LLNameTable()
+ : mNameMap()
+ {
+ }
+
+ ~LLNameTable()
+ {
+ }
+
+ void addEntry(const std::string& name, DATA data)
+ {
+ addEntry(name.c_str(), data);
+ }
+
+ void addEntry(const char *name, DATA data)
+ {
+ char *tablename = gStringTable.addString(name);
+ mNameMap[tablename] = data;
+ }
+
+ BOOL checkName(const std::string& name) const
+ {
+ return checkName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ BOOL checkName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ return mNameMap.count(tablename) ? TRUE : FALSE;
+ }
+
+ DATA resolveName(const std::string& name) const
+ {
+ return resolveName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ DATA resolveName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ const_iter_t iter = mNameMap.find(tablename);
+ if (iter != mNameMap.end())
+ return iter->second;
+ else
+ return 0;
+ }
+
+ // O(N)! (currently only used in one place... (newsim/llstate.cpp))
+ const char *resolveData(const DATA &data) const
+ {
+ const_iter_t iter = mNameMap.begin();
+ const_iter_t end = mNameMap.end();
+ for (; iter != end; ++iter)
+ {
+ if (iter->second == data)
+ return iter->first;
+ }
+ return NULL;
+ }
+
+ typedef std::map<const char *, DATA> name_map_t;
+ typedef typename std::map<const char *,DATA>::iterator iter_t;
+ typedef typename std::map<const char *,DATA>::const_iterator const_iter_t;
+ name_map_t mNameMap;
+};
+
+#endif
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
new file mode 100644
index 0000000000..829ae0de21
--- /dev/null
+++ b/indra/llcommon/llpreprocessor.h
@@ -0,0 +1,99 @@
+/**
+ * @file llpreprocessor.h
+ * @brief This file should be included in all Linden Lab files and
+ * should only contain special preprocessor directives
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLPREPROCESSOR_H
+#define LLPREPROCESSOR_H
+
+// Figure out endianness of platform
+#ifdef LL_LINUX
+#define __ENABLE_WSTRING
+#include <endian.h>
+#endif // LL_LINUX
+
+#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)))
+#define LL_LITTLE_ENDIAN 1
+#else
+#define LL_BIG_ENDIAN 1
+#endif
+
+// Per-OS feature switches.
+
+#if LL_DARWIN
+ #define LL_QUICKTIME_ENABLED 1
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 1
+#elif LL_WINDOWS
+ #define LL_QUICKTIME_ENABLED 1
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 1
+#elif LL_LINUX
+ #define LL_QUICKTIME_ENABLED 0
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 0
+#endif
+
+#if LL_LIBXUL_ENABLED && !defined(MOZILLA_INTERNAL_API)
+ // Without this, nsTAString.h errors out with:
+ // "Cannot use internal string classes without MOZILLA_INTERNAL_API defined. Use the frozen header nsStringAPI.h instead."
+ // It might be worth our while to figure out if we can use the frozen apis at some point...
+ #define MOZILLA_INTERNAL_API 1
+#endif
+
+// Deal with minor differences on Unixy OSes.
+#if LL_DARWIN || LL_LINUX
+ #define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+ // Different name, same functionality.
+ #define stricmp strcasecmp
+ #define strnicmp strncasecmp
+
+ // Not sure why this is different, but...
+ #ifndef MAX_PATH
+ #define MAX_PATH PATH_MAX
+ #endif // not MAX_PATH
+
+#endif
+
+// Deal with the differeneces on Windows
+#if defined(LL_WINDOWS)
+#define snprintf _snprintf
+#endif // LL_WINDOWS
+
+// Static linking with apr on windows needs to be declared.
+#ifdef LL_WINDOWS
+#ifndef APR_DECLARE_STATIC
+#define APR_DECLARE_STATIC // For APR on Windows
+#endif
+#ifndef APU_DECLARE_STATIC
+#define APU_DECLARE_STATIC // For APR util on Windows
+#endif
+#endif
+
+#if defined(LL_WINDOWS)
+#define BOOST_REGEX_NO_LIB 1
+#define CURL_STATICLIB 1
+#endif // LL_WINDOWS
+
+
+// Deal with VC6 problems
+#if defined(LL_WINDOWS)
+#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4.
+//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
+#pragma warning( disable : 4786 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation.
+#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
+#endif // LL_WINDOWS
+
+#endif // not LL_LINDEN_PREPROCESSOR_H
diff --git a/indra/llcommon/llpriqueuemap.h b/indra/llcommon/llpriqueuemap.h
new file mode 100644
index 0000000000..68fad0d6df
--- /dev/null
+++ b/indra/llcommon/llpriqueuemap.h
@@ -0,0 +1,127 @@
+/**
+ * @file llpriqueuemap.h
+ * @brief Priority queue implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLPRIQUEUEMAP_H
+#define LL_LLPRIQUEUEMAP_H
+
+#include <map>
+
+//
+// Priority queue, implemented under the hood as a
+// map. Needs to be done this way because none of the
+// standard STL containers provide a representation
+// where it's easy to reprioritize.
+//
+
+template <class DATA>
+class LLPQMKey
+{
+public:
+ LLPQMKey(const F32 priority, DATA data) : mPriority(priority), mData(data)
+ {
+ }
+
+ bool operator<(const LLPQMKey &b) const
+ {
+ if (mPriority > b.mPriority)
+ {
+ return TRUE;
+ }
+ if (mPriority < b.mPriority)
+ {
+ return FALSE;
+ }
+ if (mData > b.mData)
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ F32 mPriority;
+ DATA mData;
+};
+
+template <class DATA_TYPE>
+class LLPriQueueMap
+{
+public:
+ typedef typename std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE>::iterator pqm_iter;
+ typedef std::pair<LLPQMKey<DATA_TYPE>, DATA_TYPE> pqm_pair;
+ typedef void (*set_pri_fn)(DATA_TYPE &data, const F32 priority);
+ typedef F32 (*get_pri_fn)(DATA_TYPE &data);
+
+
+ LLPriQueueMap(set_pri_fn set_pri, get_pri_fn get_pri) : mSetPriority(set_pri), mGetPriority(get_pri)
+ {
+ }
+
+ void push(const F32 priority, DATA_TYPE data)
+ {
+#ifdef _DEBUG
+ pqm_iter iter = mMap.find(LLPQMKey<DATA_TYPE>(priority, data));
+ if (iter != mMap.end())
+ {
+ llerrs << "Pushing already existing data onto queue!" << llendl;
+ }
+#endif
+ mMap.insert(pqm_pair(LLPQMKey<DATA_TYPE>(priority, data), data));
+ }
+
+ BOOL pop(DATA_TYPE *datap)
+ {
+ pqm_iter iter;
+ iter = mMap.begin();
+ if (iter == mMap.end())
+ {
+ return FALSE;
+ }
+ *datap = (*(iter)).second;
+ mMap.erase(iter);
+
+ return TRUE;
+ }
+
+ void reprioritize(const F32 new_priority, DATA_TYPE data)
+ {
+ pqm_iter iter;
+ F32 cur_priority = mGetPriority(data);
+ LLPQMKey<DATA_TYPE> cur_key(cur_priority, data);
+ iter = mMap.find(cur_key);
+ if (iter == mMap.end())
+ {
+ llwarns << "Data not on priority queue!" << llendl;
+ // OK, try iterating through all of the data and seeing if we just screwed up the priority
+ // somehow.
+ for (iter = mMap.begin(); iter != mMap.end(); iter++)
+ {
+ if ((*(iter)).second == data)
+ {
+ llerrs << "Data on priority queue but priority not matched!" << llendl;
+ }
+ }
+ return;
+ }
+
+ mMap.erase(iter);
+ mSetPriority(data, new_priority);
+ push(new_priority, data);
+ }
+
+ S32 getLength() const
+ {
+ return (S32)mMap.size();
+ }
+
+ // Hack: public for use by the transfer manager, ugh.
+ std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE> mMap;
+protected:
+ void (*mSetPriority)(DATA_TYPE &data, const F32 priority);
+ F32 (*mGetPriority)(DATA_TYPE &data);
+};
+
+#endif // LL_LLPRIQUEUEMAP_H
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
new file mode 100644
index 0000000000..04e1bc0839
--- /dev/null
+++ b/indra/llcommon/llprocessor.cpp
@@ -0,0 +1,2125 @@
+/**
+ * @file llprocessor.cpp
+ * @brief Code to figure out the processor. Originally by Benjamin Jurke.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Filename: Processor.cpp
+// =======================
+// Author: Benjamin Jurke
+// File history: 27.02.2002 - File created. Support for Intel and AMD processors
+// 05.03.2002 - Fixed the CPUID bug: On Pre-Pentium CPUs the CPUID
+// command is not available
+// - The CProcessor::WriteInfoTextFile function do not
+// longer use Win32 file functions (-> os independend)
+// - Optional include of the windows.h header which is
+// still need for CProcessor::GetCPUFrequency.
+// 06.03.2002 - My birthday (18th :-))
+// - Replaced the '\r\n' line endings in function
+// CProcessor::CPUInfoToText by '\n'
+// - Replaced unsigned __int64 by signed __int64 for
+// solving some compiler conversion problems
+// - Fixed a bug at family=6, model=6 (Celeron -> P2)
+//////////////////////////////////////////////////////////////////////////////////
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include "processor.h"
+
+#if !LL_DARWIN
+
+#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+// We need the QueryPerformanceCounter and Sleep functions
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE
+#endif
+
+
+// Some macros we often need
+////////////////////////////
+#define CheckBit(var, bit) ((var & (1 << bit)) ? true : false)
+
+#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+// Delays for the specified amount of milliseconds
+static void _Delay(unsigned int ms)
+{
+ LARGE_INTEGER freq, c1, c2;
+ __int64 x;
+
+ // Get High-Res Timer frequency
+ if (!QueryPerformanceFrequency(&freq))
+ return;
+
+ // Convert ms to High-Res Timer value
+ x = freq.QuadPart/1000*ms;
+
+ // Get first snapshot of High-Res Timer value
+ QueryPerformanceCounter(&c1);
+ do
+ {
+ // Get second snapshot
+ QueryPerformanceCounter(&c2);
+ }while(c2.QuadPart-c1.QuadPart < x);
+ // Loop while (second-first < x)
+}
+#endif
+
+// CProcessor::CProcessor
+// ======================
+// Class constructor:
+/////////////////////////
+CProcessor::CProcessor()
+{
+ uqwFrequency = 0;
+ memset(&CPUInfo, 0, sizeof(CPUInfo));
+}
+
+// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+// =========================================================================
+// Function to measure the current CPU frequency
+////////////////////////////////////////////////////////////////////////////
+F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+{
+#ifndef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+ return 0;
+#else
+ // If there are invalid measure time parameters, zero msecs for example,
+ // we've to exit the function
+ if (uiMeasureMSecs < 1)
+ {
+ // If theres already a measured frequency available, we return it
+ if (uqwFrequency > 0)
+ return uqwFrequency;
+ else
+ return 0;
+ }
+
+ // Now we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return 0;
+
+ // First we get the CPUID standard level 0x00000001
+ unsigned long reg;
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov reg, edx
+ }
+
+ // Then we check, if the RDTSC (Real Date Time Stamp Counter) is available.
+ // This function is necessary for our measure process.
+ if (!(reg & (1 << 4)))
+ return 0;
+
+ // After that we declare some vars and check the frequency of the high
+ // resolution timer for the measure process.
+ // If there's no high-res timer, we exit.
+ __int64 starttime, endtime, timedif, freq, start, end, dif;
+ if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq))
+ return 0;
+
+ // Now we can init the measure process. We set the process and thread priority
+ // to the highest available level (Realtime priority). Also we focus the
+ // first processor in the multiprocessor system.
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hThread = GetCurrentThread();
+ unsigned long dwCurPriorityClass = GetPriorityClass(hProcess);
+ int iCurThreadPriority = GetThreadPriority(hThread);
+ unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1;
+ GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask);
+
+ SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS);
+ SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
+ SetProcessAffinityMask(hProcess, dwNewMask);
+
+ // Now we call a CPUID to ensure, that all other prior called functions are
+ // completed now (serialization)
+ __asm cpuid
+
+ // We ask the high-res timer for the start time
+ QueryPerformanceCounter((LARGE_INTEGER *) &starttime);
+
+ // Then we get the current cpu clock and store it
+ __asm
+ {
+ rdtsc
+ mov dword ptr [start+4], edx
+ mov dword ptr [start], eax
+ }
+
+ // Now we wart for some msecs
+ _Delay(uiMeasureMSecs);
+// Sleep(uiMeasureMSecs);
+
+ // We ask for the end time
+ QueryPerformanceCounter((LARGE_INTEGER *) &endtime);
+
+ // And also for the end cpu clock
+ __asm
+ {
+ rdtsc
+ mov dword ptr [end+4], edx
+ mov dword ptr [end], eax
+ }
+
+ // Now we can restore the default process and thread priorities
+ SetProcessAffinityMask(hProcess, dwProcessMask);
+ SetThreadPriority(hThread, iCurThreadPriority);
+ SetPriorityClass(hProcess, dwCurPriorityClass);
+
+ // Then we calculate the time and clock differences
+ dif = end - start;
+ timedif = endtime - starttime;
+
+ // And finally the frequency is the clock difference divided by the time
+ // difference.
+ uqwFrequency = (F64)dif / (((F64)timedif) / freq);
+
+ // At last we just return the frequency that is also stored in the call
+ // member var uqwFrequency
+ return uqwFrequency;
+#endif
+}
+
+// bool CProcessor::AnalyzeIntelProcessor()
+// ========================================
+// Private class function for analyzing an Intel processor
+//////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeIntelProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, edxreg;
+
+ // First we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return false;
+
+ // Now we get the CPUID standard level 0x00000001
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then get the cpu model, family, type, stepping and brand id by masking
+ // the eax and ebx register
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+ CPUInfo.uiBrandID = ebxreg & 0xF;
+
+ // Now we can translate the type number to a more understandable string format
+ switch (CPUInfo.uiType)
+ {
+ case 0: // Type = 0: Original OEM processor
+ strcpy(CPUInfo.strType, "Original OEM"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 1: // Type = 1: Overdrive processor
+ strcpy(CPUInfo.strType, "Overdrive"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 2: // Type = 2: Dual-capable processor
+ strcpy(CPUInfo.strType, "Dual-capable"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 3: // Type = 3: Reserved for future use
+ strcpy(CPUInfo.strType, "Reserved"); /* Flawfinder: ignore */
+ break;
+ default: // This should be never called, cause we just mask 2 bits --> [0..3]
+ strcpy(CPUInfo.strType, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Then we translate the brand id:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 0: // Brand id = 0: Brand id not supported on this processor
+ strcpy(CPUInfo.strBrandID, "Not supported"); /* Flawfinder: ignore */
+ break;
+ case 1: // Brand id = 1: Intel Celeron (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Celeron"); /* Flawfinder: ignore */
+ break;
+ case 2: // Brand id = 2: Intel Pentium III (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III"); /* Flawfinder: ignore */
+ break;
+ case 3: // Brand id = 3: Model dependent
+ if (CPUInfo.uiModel == 6) // If the cpu model is Celeron (well, I'm NOT SURE!!!)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Celeron"); /* Flawfinder: ignore */
+ else
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III Xeon"); /* Flawfinder: ignore */
+ break;
+ case 4: // Brand id = 4: Intel Pentium III Tualatin (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium III"); /* Flawfinder: ignore */
+ break;
+ case 6: // Brand id = 6: Intel Pentium III mobile (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium III mobile"); /* Flawfinder: ignore */
+ break;
+ case 7: // Brand id = 7: Intel Celeron mobile (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Celeron mobile"); /* Flawfinder: ignore */
+ break;
+ case 8: // Brand id = 8: Intel Pentium 4 Willamette (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 9: // Brand id = 9: Intel Pentium 4 Northwood (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 0xA: // Brand id = 0xA: Intel Pentium 4 Northwood (0.13 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break; // No idea, where the difference to id=9 is
+ case 0xB: // Brand id = 0xB: Intel Pentium 4 Northwood Xeon (0.13 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4 Xeon"); /* Flawfinder: ignore */
+ break;
+ case 0xE: // Brand id = 0xE: Intel Pentium 4 Willamette Xeon (0.18 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium 4 Xeon"); /* Flawfinder: ignore */
+ break;
+ default: // Should be never called, but sure is sure
+ strcpy(CPUInfo.strBrandID, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Then we translate the cpu family
+ switch (CPUInfo.uiFamily)
+ {
+ case 3: // Family = 3: i386 (80386) processor family
+ strcpy(CPUInfo.strFamily, "Intel i386"); /* Flawfinder: ignore */
+ break;
+ case 4: // Family = 4: i486 (80486) processor family
+ strcpy(CPUInfo.strFamily, "Intel i486"); /* Flawfinder: ignore */
+ break;
+ case 5: // Family = 5: Pentium (80586) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium"); /* Flawfinder: ignore */
+ break;
+ case 6: // Family = 6: Pentium Pro (80686) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium Pro"); /* Flawfinder: ignore */
+ break;
+ case 15: // Family = 15: Extended family specific
+ // Masking the extended family
+ CPUInfo.uiExtendedFamily = (eaxreg >> 20) & 0xFF;
+ switch (CPUInfo.uiExtendedFamily)
+ {
+ case 0: // Family = 15, Ext. Family = 0: Pentium 4 (80786 ??) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 1: // Family = 15, Ext. Family = 1: McKinley (64-bit) processor family
+ strcpy(CPUInfo.strFamily, "Intel McKinley (IA-64)"); /* Flawfinder: ignore */
+ break;
+ default: // Sure is sure
+ strcpy(CPUInfo.strFamily, "Unknown Intel Pentium 4+"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // Failsave
+ strcpy(CPUInfo.strFamily, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we come to the big deal, the exact model name
+ switch (CPUInfo.uiFamily)
+ {
+ case 3: // i386 (80386) processor family
+ strcpy(CPUInfo.strModel, "Unknown Intel i386"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i386", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 4: // i486 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: i486 DX-25/33 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX-25/33"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX-25/33", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 1: // Model = 1: i486 DX-50 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX-50"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX-50", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: i486 SX processor model
+ strcpy(CPUInfo.strModel, "Intel i486 SX"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SX", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: i486 DX2 (with i487 numeric coprocessor) processor model
+ strcpy(CPUInfo.strModel, "Intel i486 487/DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX2 with i487 numeric coprocessor", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 4: // Model = 4: i486 SL processor model (never heard ?!?)
+ strcpy(CPUInfo.strModel, "Intel i486 SL"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SL", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 5: // Model = 5: i486 SX2 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 SX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: i486 write-back enhanced DX2 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 write-back enhanced DX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: i486 DX4 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: i486 write-back enhanced DX4 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown Intel i486"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 (Unknown model)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Pentium (80586) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium (P5 A-Step) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium (P5 A-Step)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium (P5 A-Step core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break; // Famous for the DIV bug, as far as I know
+ case 1: // Model = 1: Pentium 60/66 processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium 60/66 (P5)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 60/66 (P5 core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: Pentium 75-200 (P54C) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium 75-200 (P54C)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 75-200 (P54C core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: Pentium overdrive for 486 systems processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium for 486 system (P24T Overdrive)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium for 486 (P24T overdrive core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 4: // Model = 4: Pentium MMX processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium MMX (P55C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 7: Pentium processor model (don't know difference to Model=2)
+ strcpy(CPUInfo.strModel, "Intel Pentium (P54C)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium (P54C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 8: // Model = 8: Pentium MMX (0.25 micron) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C), 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium MMX (P55C core), 0.25 micron", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium (Unknown P5-model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 6: // Pentium Pro (80686) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium Pro (P6 A-Step) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6 A-Step)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (P6 A-Step core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 1: // Model = 1: Pentium Pro
+ strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (P6 core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 3: Pentium II (66 MHz FSB, I think) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium II Model 3, 0.28 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II (Model 3 core, 0.28 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 5: // Model = 5: Pentium II/Xeon/Celeron (0.25 micron) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium II Model 5/Xeon/Celeron, 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II/Xeon/Celeron (Model 5 core, 0.25 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 6: // Model = 6: Pentium II with internal L2 cache
+ strcpy(CPUInfo.strModel, "Intel Pentium II - internal L2 cache"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 7: Pentium III/Xeon (extern L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Pentium III Xeon - external L2 cache, 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium III/Pentium III Xeon (0.25 micron process) with external L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 8: // Model = 8: Pentium III/Xeon/Celeron (256 KB on-die L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/
+ // We want to know it exactly:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 1: // Model = 8, Brand id = 1: Celeron (on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 2: // Model = 8, Brand id = 2: Pentium III (on-die L2 cache) processor model (my current cpu :-))
+ strncat(strCPUName, "Intel Pentium III (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 8, Brand id = 3: Pentium III Xeon (on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Pentium III Xeon (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // ...²
+ strncat(strCPUName, "Intel Pentium III core (unknown model, 0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 0xA: // Model = 0xA: Pentium III/Xeon/Celeron (1 or 2 MB on-die L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/
+ // Exact detection:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 1: // Model = 0xA, Brand id = 1: Celeron (1 or 2 MB on-die L2 cache (does it exist??)) processor model
+ strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 2: // Model = 0xA, Brand id = 2: Pentium III (1 or 2 MB on-die L2 cache (never seen...)) processor model
+ strncat(strCPUName, "Intel Pentium III (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 0xA, Brand id = 3: Pentium III Xeon (1 or 2 MB on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Pentium III Xeon (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // Getting bored of this............
+ strncat(strCPUName, "Intel Pentium III core (unknown model, 0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 0xB: // Model = 0xB: Pentium III/Xeon/Celeron (Tualatin core, on-die cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.13 micron"); /*Flawfinder: ignore*/
+ // Omniscient: ;-)
+ switch (CPUInfo.uiBrandID)
+ {
+ case 3: // Model = 0xB, Brand id = 3: Celeron (Tualatin core) processor model
+ strncat(strCPUName, "Intel Celeron (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 4: // Model = 0xB, Brand id = 4: Pentium III (Tualatin core) processor model
+ strncat(strCPUName, "Intel Pentium III (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 0xB, Brand id = 7: Celeron mobile (Tualatin core) processor model
+ strncat(strCPUName, "Intel Celeron mobile (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // *bored*
+ strncat(strCPUName, "Intel Pentium III Tualatin core (unknown model, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ default: // *more bored*
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium Pro"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (Unknown model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 15: // Extended processor family
+ // Masking the extended model
+ CPUInfo.uiExtendedModel = (eaxreg >> 16) & 0xFF;
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium 4 Willamette (A-Step) core
+ if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette (A-Step)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium 4 Willamette (A-Step)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ }
+ else // else Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon (A-Step)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon (A-Step)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ case 1: // Model = 1: Pentium 4 Willamette core
+ if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ else // else Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ case 2: // Model = 2: Pentium 4 Northwood core
+ if (((CPUInfo.uiBrandID) == 9) || ((CPUInfo.uiBrandID) == 0xA)) // P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Northwood", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ else // Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood Xeon"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Northwood Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ default: // Silly stupid never used failsave option
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium 4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // *grmpf*
+ strcpy(CPUInfo.strModel, "Unknown Intel model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // After the long processor model block we now come to the processors serial
+ // number.
+ // First of all we check if the processor supports the serial number
+ if (CPUInfo.MaxSupportedLevel >= 3)
+ {
+ // If it supports the serial number CPUID level 0x00000003 we read the data
+ unsigned long sig1, sig2, sig3;
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov sig1, eax
+ mov eax, 3
+ cpuid
+ mov sig2, ecx
+ mov sig3, edx
+ }
+ // Then we convert the data to a readable string
+ snprintf(
+ CPUInfo.strProcessorSerial,
+ sizeof(CPUInfo.strProcessorSerial),
+ "%04lX-%04lX-%04lX-%04lX-%04lX-%04lX",
+ sig1 >> 16,
+ sig1 & 0xFFFF,
+ sig3 >> 16,
+ sig3 & 0xFFFF,
+ sig2 >> 16, sig2 & 0xFFFF); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // If there's no serial number support we just put "No serial number"
+ snprintf(
+ CPUInfo.strProcessorSerial,
+ sizeof(CPUInfo.strProcessorSerial),
+ "No Processor Serial Number"); /* Flawfinder: ignore */
+ }
+
+ // Now we get the standard processor extensions
+ GetStandardProcessorExtensions();
+
+ // And finally the processor configuration (caches, TLBs, ...) and translate
+ // the data to readable strings
+ GetStandardProcessorConfiguration();
+ TranslateProcessorConfiguration();
+
+ // At last...
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::AnalyzeAMDProcessor()
+// ======================================
+// Private class function for analyzing an AMD processor
+////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeAMDProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // First of all we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return 0;
+
+ // Now we get the CPUID standard level 0x00000001
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then we mask the model, family, stepping and type (AMD does not support brand id)
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+
+ // After that, we translate the processor type (see CProcessor::AnalyzeIntelProcessor()
+ // for further comments on this)
+ switch (CPUInfo.uiType)
+ {
+ case 0:
+ strcpy(CPUInfo.strType, "Original OEM"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 1:
+ strcpy(CPUInfo.strType, "Overdrive"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 2:
+ strcpy(CPUInfo.strType, "Dual-capable"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 3:
+ strcpy(CPUInfo.strType, "Reserved"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strType, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we check if the processor supports the brand id string extended CPUID level
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000004)
+ {
+ // If it supports the extended CPUID level 0x80000004 we read the data
+ char tmp[52]; /* Flawfinder: ignore */
+ memset(tmp, 0, sizeof(tmp));
+ __asm
+ {
+ mov eax, 0x80000002
+ cpuid
+ mov dword ptr [tmp], eax
+ mov dword ptr [tmp+4], ebx
+ mov dword ptr [tmp+8], ecx
+ mov dword ptr [tmp+12], edx
+ mov eax, 0x80000003
+ cpuid
+ mov dword ptr [tmp+16], eax
+ mov dword ptr [tmp+20], ebx
+ mov dword ptr [tmp+24], ecx
+ mov dword ptr [tmp+28], edx
+ mov eax, 0x80000004
+ cpuid
+ mov dword ptr [tmp+32], eax
+ mov dword ptr [tmp+36], ebx
+ mov dword ptr [tmp+40], ecx
+ mov dword ptr [tmp+44], edx
+ }
+ // And copy it to the brand id string
+ strncpy(CPUInfo.strBrandID, tmp,sizeof(CPUInfo.strBrandID-1)); /* Flawfinder: ignore */
+ CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID-1)]='\0';
+ }
+ else
+ {
+ // Or just tell there is no brand id string support
+ strcpy(CPUInfo.strBrandID, "Not supported"); /* Flawfinder: ignore */
+ }
+
+ // After that we translate the processor family
+ switch(CPUInfo.uiFamily)
+ {
+ case 4: // Family = 4: 486 (80486) or 5x86 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 3: // Thanks to AMD for this nice form of family
+ case 7: // detection.... *grmpf*
+ case 8:
+ case 9:
+ strcpy(CPUInfo.strFamily, "AMD 80486"); /* Flawfinder: ignore */
+ break;
+ case 0xE:
+ case 0xF:
+ strcpy(CPUInfo.strFamily, "AMD 5x86"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Family = 5: K5 or K6 processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ strcpy(CPUInfo.strFamily, "AMD K5"); /* Flawfinder: ignore */
+ break;
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ strcpy(CPUInfo.strFamily, "AMD K6"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 6: // Family = 6: K7 (Athlon, ...) processor family
+ strcpy(CPUInfo.strFamily, "AMD K7"); /* Flawfinder: ignore */
+ break;
+ default: // For security
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // After the family detection we come to the specific processor model
+ // detection
+ switch (CPUInfo.uiFamily)
+ {
+ case 4: // Family = 4: 486 (80486) or 5x85 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 3: // Model = 3: 80486 DX2
+ strcpy(CPUInfo.strModel, "AMD 80486 DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: 80486 write-back enhanced DX2
+ strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 write-back enhanced DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: 80486 DX4
+ strcpy(CPUInfo.strModel, "AMD 80486 DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: 80486 write-back enhanced DX4
+ strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 write-back enhanced DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xE: // Model = 0xE: 5x86
+ strcpy(CPUInfo.strModel, "AMD 5x86"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 5x86", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xF: // Model = 0xF: 5x86 write-back enhanced (oh my god.....)
+ strcpy(CPUInfo.strModel, "AMD 5x86 write-back enhanced"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 5x86 write-back enhanced", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD 80486 or 5x86 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 or 5x86 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Family = 5: K5 / K6 processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: K5 SSA 5 (Pentium Rating *ggg* 75, 90 and 100 Mhz)
+ strcpy(CPUInfo.strModel, "AMD K5 SSA5 (PR75, PR90, PR100)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 SSA5 (PR75, PR90, PR100)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 1: // Model = 1: K5 5k86 (PR 120 and 133 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR120, PR133)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR120, PR133)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: K5 5k86 (PR 166 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR166)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR166)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: K5 5k86 (PR 200 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR200)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR200)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 6: // Model = 6: K6
+ strcpy(CPUInfo.strModel, "AMD K6 (0.30 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6 (0.30 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: K6 (0.25 micron)
+ strcpy(CPUInfo.strModel, "AMD K6 (0.25 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6 (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: K6-2
+ strcpy(CPUInfo.strModel, "AMD K6-2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: K6-III
+ strcpy(CPUInfo.strModel, "AMD K6-III"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-III", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xD: // Model = 0xD: K6-2+ / K6-III+
+ strcpy(CPUInfo.strModel, "AMD K6-2+ or K6-III+ (0.18 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-2+ or K6-III+ (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1);
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD K5 or K6 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 or K6 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 6: // Family = 6: K7 processor family (AMDs first good processors)
+ switch (CPUInfo.uiModel)
+ {
+ case 1: // Athlon
+ strcpy(CPUInfo.strModel, "AMD Athlon (0.25 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 2: // Athlon (0.18 micron)
+ strcpy(CPUInfo.strModel, "AMD Athlon (0.18 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 3: // Duron (Spitfire core)
+ strcpy(CPUInfo.strModel, "AMD Duron (Spitfire)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Duron (Spitfire core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 4: // Athlon (Thunderbird core)
+ strcpy(CPUInfo.strModel, "AMD Athlon (Thunderbird)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (Thunderbird core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 6: // Athlon MP / Mobile Athlon (Palomino core)
+ strcpy(CPUInfo.strModel, "AMD Athlon MP/Mobile Athlon (Palomino)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon MP/Mobile Athlon (Palomino core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Mobile Duron (Morgan core)
+ strcpy(CPUInfo.strModel, "AMD Mobile Duron (Morgan)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Mobile Duron (Morgan core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD K7 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K7 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we read the standard processor extension that are stored in the same
+ // way the Intel standard extensions are
+ GetStandardProcessorExtensions();
+
+ // Then we check if theres an extended CPUID level support
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000001)
+ {
+ // If we can access the extended CPUID level 0x80000001 we get the
+ // edx register
+ __asm
+ {
+ mov eax, 0x80000001
+ cpuid
+ mov edxreg, edx
+ }
+
+ // Now we can mask some AMD specific cpu extensions
+ CPUInfo._Ext.EMMX_MultimediaExtensions = CheckBit(edxreg, 22);
+ CPUInfo._Ext.AA64_AMD64BitArchitecture = CheckBit(edxreg, 29);
+ CPUInfo._Ext._E3DNOW_InstructionExtensions = CheckBit(edxreg, 30);
+ CPUInfo._Ext._3DNOW_InstructionExtensions = CheckBit(edxreg, 31);
+ }
+
+ // After that we check if the processor supports the ext. CPUID level
+ // 0x80000006
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000006)
+ {
+ // If it's present, we read it out
+ __asm
+ {
+ mov eax, 0x80000005
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ mov edxreg, edx
+ }
+
+ // Then we mask the L1 Data TLB information
+ if ((ebxreg >> 16) && (eaxreg >> 16))
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF;
+ }
+ else if (eaxreg >> 16)
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF;
+ }
+ else if (ebxreg >> 16)
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (ebxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (ebxreg >> 16) & 0xFF;
+ }
+ if (CPUInfo._Data.uiAssociativeWays == 0xFF)
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+
+ // Now the L1 Instruction/Code TLB information
+ if ((ebxreg & 0xFFFF) && (eaxreg & 0xFFFF))
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = eaxreg & 0xFF;
+ }
+ else if (eaxreg & 0xFFFF)
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = eaxreg & 0xFF;
+ }
+ else if (ebxreg & 0xFFFF)
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (ebxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = ebxreg & 0xFF;
+ }
+ if (CPUInfo._Instruction.uiAssociativeWays == 0xFF)
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+
+ // Then we read the L1 data cache information
+ if ((ecxreg >> 24) > 0)
+ {
+ CPUInfo._L1.Data.bPresent = true;
+ snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", ecxreg >> 24); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = (ecxreg >> 15) & 0xFF;
+ CPUInfo._L1.Data.uiLineSize = ecxreg & 0xFF;
+ }
+ // After that we read the L2 instruction/code cache information
+ if ((edxreg >> 24) > 0)
+ {
+ CPUInfo._L1.Instruction.bPresent = true;
+ snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", edxreg >> 24); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = (edxreg >> 15) & 0xFF;
+ CPUInfo._L1.Instruction.uiLineSize = edxreg & 0xFF;
+ }
+
+ // Note: I'm not absolutely sure that the L1 page size code (the
+ // 'if/else if/else if' structs above) really detects the real page
+ // size for the TLB. Somebody should check it....
+
+ // Now we read the ext. CPUID level 0x80000006
+ __asm
+ {
+ mov eax, 0x80000006
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ }
+
+ // We only mask the unified L2 cache masks (never heard of an
+ // L2 cache that is divided in data and code parts)
+ if (((ecxreg >> 12) & 0xF) > 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", ecxreg >> 16); /*Flawfinder: ignore*/
+ switch ((ecxreg >> 12) & 0xF)
+ {
+ case 1:
+ CPUInfo._L2.uiAssociativeWays = 1;
+ break;
+ case 2:
+ CPUInfo._L2.uiAssociativeWays = 2;
+ break;
+ case 4:
+ CPUInfo._L2.uiAssociativeWays = 4;
+ break;
+ case 6:
+ CPUInfo._L2.uiAssociativeWays = 8;
+ break;
+ case 8:
+ CPUInfo._L2.uiAssociativeWays = 16;
+ break;
+ case 0xF:
+ CPUInfo._L2.uiAssociativeWays = (unsigned int) -1;
+ break;
+ default:
+ CPUInfo._L2.uiAssociativeWays = 0;
+ break;
+ }
+ CPUInfo._L2.uiLineSize = ecxreg & 0xFF;
+ }
+ }
+ else
+ {
+ // If we could not detect the ext. CPUID level 0x80000006 we
+ // try to read the standard processor configuration.
+ GetStandardProcessorConfiguration();
+ }
+ // After reading we translate the configuration to strings
+ TranslateProcessorConfiguration();
+
+ // And finally exit
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::AnalyzeUnknownProcessor()
+// ==========================================
+// Private class function to analyze an unknown (No Intel or AMD) processor
+///////////////////////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeUnknownProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg;
+
+ // We check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return false;
+
+ // First of all we read the standard CPUID level 0x00000001
+ // This level should be available on every x86-processor clone
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ }
+ // Then we mask the processor model, family, type and stepping
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+
+ // To have complete information we also mask the brand id
+ CPUInfo.uiBrandID = ebxreg & 0xF;
+
+ // Then we get the standard processor extensions
+ GetStandardProcessorExtensions();
+
+ // Now we mark everything we do not know as unknown
+ strcpy(strCPUName, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo._Data.strTLB, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._Instruction.strTLB, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo._Trace.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L1.Data.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L1.Instruction.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L2.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L3.strCache, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo.strProcessorSerial, "Unknown / Not supported"); /*Flawfinder: ignore*/
+
+ // For the family, model and brand id we can only print the numeric value
+ snprintf(CPUInfo.strBrandID, sizeof(CPUInfo.strBrandID), "Brand-ID number %d", CPUInfo.uiBrandID); /*Flawfinder: ignore*/
+ snprintf(CPUInfo.strFamily, sizeof(CPUInfo.strFamily), "Family number %d", CPUInfo.uiFamily); /*Flawfinder: ignore*/
+ snprintf(CPUInfo.strModel, sizeof(CPUInfo.strModel), "Model number %d", CPUInfo.uiModel); /*Flawfinder: ignore*/
+
+ // Nevertheless we can determine the processor type
+ switch (CPUInfo.uiType)
+ {
+ case 0:
+ strcpy(CPUInfo.strType, "Original OEM"); /*Flawfinder: ignore*/
+ break;
+ case 1:
+ strcpy(CPUInfo.strType, "Overdrive"); /*Flawfinder: ignore*/
+ break;
+ case 2:
+ strcpy(CPUInfo.strType, "Dual-capable"); /*Flawfinder: ignore*/
+ break;
+ case 3:
+ strcpy(CPUInfo.strType, "Reserved"); /*Flawfinder: ignore*/
+ break;
+ default:
+ strcpy(CPUInfo.strType, "Unknown"); /*Flawfinder: ignore*/
+ break;
+ }
+
+ // And thats it
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::CheckCPUIDPresence()
+// =====================================
+// This function checks if the CPUID command is available on the current
+// processor
+////////////////////////////////////////////////////////////////////////
+bool CProcessor::CheckCPUIDPresence()
+{
+#if LL_WINDOWS
+ unsigned long BitChanged;
+
+ // We've to check if we can toggle the flag register bit 21
+ // If we can't the processor does not support the CPUID command
+ __asm
+ {
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 0x00200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax,ebx
+ mov BitChanged, eax
+ }
+
+ return ((BitChanged) ? true : false);
+#else
+ return FALSE;
+#endif
+}
+
+// void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
+// ===============================================================
+// This function (or switch ?!) just translates a one-byte processor configuration
+// byte to understandable values
+//////////////////////////////////////////////////////////////////////////////////
+void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
+{
+ // First we ensure that there's only one single byte
+ cfg &= 0xFF;
+
+ // Then we do a big switch
+ switch(cfg)
+ {
+ case 0: // cfg = 0: Unused
+ break;
+ case 0x1: // cfg = 0x1: code TLB present, 4 KB pages, 4 ways, 32 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = 4;
+ CPUInfo._Instruction.uiEntries = 32;
+ break;
+ case 0x2: // cfg = 0x2: code TLB present, 4 MB pages, fully associative, 2 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = 4;
+ CPUInfo._Instruction.uiEntries = 2;
+ break;
+ case 0x3: // cfg = 0x3: data TLB present, 4 KB pages, 4 ways, 64 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = 4;
+ CPUInfo._Data.uiEntries = 64;
+ break;
+ case 0x4: // cfg = 0x4: data TLB present, 4 MB pages, 4 ways, 8 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = 4;
+ CPUInfo._Data.uiEntries = 8;
+ break;
+ case 0x6: // cfg = 0x6: code L1 cache present, 8 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Instruction.bPresent = true;
+ strcpy(CPUInfo._L1.Instruction.strSize, "8 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = 4;
+ CPUInfo._L1.Instruction.uiLineSize = 32;
+ break;
+ case 0x8: // cfg = 0x8: code L1 cache present, 16 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Instruction.bPresent = true;
+ strcpy(CPUInfo._L1.Instruction.strSize, "16 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = 4;
+ CPUInfo._L1.Instruction.uiLineSize = 32;
+ break;
+ case 0xA: // cfg = 0xA: data L1 cache present, 8 KB, 2 ways, 32 byte lines
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = 2;
+ CPUInfo._L1.Data.uiLineSize = 32;
+ break;
+ case 0xC: // cfg = 0xC: data L1 cache present, 16 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 32;
+ break;
+ case 0x22: // cfg = 0x22: code and data L3 cache present, 512 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "512 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 4;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x23: // cfg = 0x23: code and data L3 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "1024 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x25: // cfg = 0x25: code and data L3 cache present, 2048 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "2048 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x29: // cfg = 0x29: code and data L3 cache present, 4096 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "4096 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x40: // cfg = 0x40: no integrated L2 cache (P6 core) or L3 cache (P4 core)
+ break;
+ case 0x41: // cfg = 0x41: code and data L2 cache present, 128 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x42: // cfg = 0x42: code and data L2 cache present, 256 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x43: // cfg = 0x43: code and data L2 cache present, 512 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x44: // cfg = 0x44: code and data L2 cache present, 1024 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x45: // cfg = 0x45: code and data L2 cache present, 2048 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x50: // cfg = 0x50: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 64 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 64;
+ break;
+ case 0x51: // cfg = 0x51: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 128 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 128;
+ break;
+ case 0x52: // cfg = 0x52: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 256 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 256;
+ break;
+ case 0x5B: // cfg = 0x5B: data TLB present, 4 KB / 4 MB pages, fully associative, 64 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 64;
+ break;
+ case 0x5C: // cfg = 0x5C: data TLB present, 4 KB / 4 MB pages, fully associative, 128 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 128;
+ break;
+ case 0x5d: // cfg = 0x5D: data TLB present, 4 KB / 4 MB pages, fully associative, 256 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 256;
+ break;
+ case 0x66: // cfg = 0x66: data L1 cache present, 8 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x67: // cfg = 0x67: data L1 cache present, 16 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x68: // cfg = 0x68: data L1 cache present, 32 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "32 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x70: // cfg = 0x70: trace L1 cache present, 12 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "12 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x71: // cfg = 0x71: trace L1 cache present, 16 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "16 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x72: // cfg = 0x72: trace L1 cache present, 32 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "32 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x79: // cfg = 0x79: code and data L2 cache present, 128 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7A: // cfg = 0x7A: code and data L2 cache present, 256 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7B: // cfg = 0x7B: code and data L2 cache present, 512 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7C: // cfg = 0x7C: code and data L2 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x81: // cfg = 0x81: code and data L2 cache present, 128 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x82: // cfg = 0x82: code and data L2 cache present, 256 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x83: // cfg = 0x83: code and data L2 cache present, 512 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x84: // cfg = 0x84: code and data L2 cache present, 1024 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x85: // cfg = 0x85: code and data L2 cache present, 2048 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ }
+}
+
+FORCEINLINE static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
+{
+ // We define 0xFFFFFFFF (= -1) as fully associative
+ if (uiWays == ((unsigned int) -1))
+ strcpy(buf, "fully associative"); /* Flawfinder: ignore */
+ else
+ {
+ if (uiWays == 1) // A one way associative cache is just direct mapped
+ strcpy(buf, "direct mapped"); /* Flawfinder: ignore */
+ else if (uiWays == 0) // This should not happen...
+ strcpy(buf, "unknown associative ways"); /* Flawfinder: ignore */
+ else // The x-way associative cache
+ sprintf(buf, "%d ways associative", uiWays); /* Flawfinder: ignore */
+ }
+ // To ease the function use we return the buffer
+ return buf;
+}
+FORCEINLINE static void TranslateTLB(ProcessorTLB *tlb)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the TLB is present
+ if (tlb->bPresent)
+ snprintf(tlb->strTLB,sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries); /* Flawfinder: ignore */
+ else
+ strcpy(tlb->strTLB, "Not present"); /* Flawfinder: ignore */
+}
+FORCEINLINE static void TranslateCache(ProcessorCache *cache)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the cache is present
+ if (cache->bPresent)
+ {
+ // If present we construct the string
+ snprintf(cache->strCache, sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize); /* Flawfinder: ignore */
+ if (cache->bSectored)
+ strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // Else we just say "Not present"
+ strcpy(cache->strCache, "Not present"); /* Flawfinder: ignore */
+ }
+}
+
+// void CProcessor::TranslateProcessorConfiguration()
+// ==================================================
+// Private class function to translate the processor configuration values
+// to strings
+/////////////////////////////////////////////////////////////////////////
+void CProcessor::TranslateProcessorConfiguration()
+{
+ // We just call the small functions defined above
+ TranslateTLB(&CPUInfo._Data);
+ TranslateTLB(&CPUInfo._Instruction);
+
+ TranslateCache(&CPUInfo._Trace);
+
+ TranslateCache(&CPUInfo._L1.Instruction);
+ TranslateCache(&CPUInfo._L1.Data);
+ TranslateCache(&CPUInfo._L2);
+ TranslateCache(&CPUInfo._L3);
+}
+
+// void CProcessor::GetStandardProcessorConfiguration()
+// ====================================================
+// Private class function to read the standard processor configuration
+//////////////////////////////////////////////////////////////////////
+void CProcessor::GetStandardProcessorConfiguration()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // We check if the CPUID function is available
+ if (!CheckCPUIDPresence())
+ return;
+
+ // First we check if the processor supports the standard
+ // CPUID level 0x00000002
+ if (CPUInfo.MaxSupportedLevel >= 2)
+ {
+ // Now we go read the std. CPUID level 0x00000002 the first time
+ unsigned long count, num = 255;
+ for (count = 0; count < num; count++)
+ {
+ __asm
+ {
+ mov eax, 2
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ mov edxreg, edx
+ }
+ // We have to repeat this reading for 'num' times
+ num = eaxreg & 0xFF;
+
+ // Then we call the big decode switch function
+ DecodeProcessorConfiguration(eaxreg >> 8);
+ DecodeProcessorConfiguration(eaxreg >> 16);
+ DecodeProcessorConfiguration(eaxreg >> 24);
+
+ // If ebx contains additional data we also decode it
+ if ((ebxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(ebxreg);
+ DecodeProcessorConfiguration(ebxreg >> 8);
+ DecodeProcessorConfiguration(ebxreg >> 16);
+ DecodeProcessorConfiguration(ebxreg >> 24);
+ }
+ // And also the ecx register
+ if ((ecxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(ecxreg);
+ DecodeProcessorConfiguration(ecxreg >> 8);
+ DecodeProcessorConfiguration(ecxreg >> 16);
+ DecodeProcessorConfiguration(ecxreg >> 24);
+ }
+ // At last the edx processor register
+ if ((edxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(edxreg);
+ DecodeProcessorConfiguration(edxreg >> 8);
+ DecodeProcessorConfiguration(edxreg >> 16);
+ DecodeProcessorConfiguration(edxreg >> 24);
+ }
+ }
+ }
+#endif
+}
+
+// void CProcessor::GetStandardProcessorExtensions()
+// =================================================
+// Private class function to read the standard processor extensions
+///////////////////////////////////////////////////////////////////
+void CProcessor::GetStandardProcessorExtensions()
+{
+#if LL_WINDOWS
+ unsigned long ebxreg, edxreg;
+
+ // We check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return;
+ // We just get the standard CPUID level 0x00000001 which should be
+ // available on every x86 processor
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then we mask some bits
+ CPUInfo._Ext.FPU_FloatingPointUnit = CheckBit(edxreg, 0);
+ CPUInfo._Ext.VME_Virtual8086ModeEnhancements = CheckBit(edxreg, 1);
+ CPUInfo._Ext.DE_DebuggingExtensions = CheckBit(edxreg, 2);
+ CPUInfo._Ext.PSE_PageSizeExtensions = CheckBit(edxreg, 3);
+ CPUInfo._Ext.TSC_TimeStampCounter = CheckBit(edxreg, 4);
+ CPUInfo._Ext.MSR_ModelSpecificRegisters = CheckBit(edxreg, 5);
+ CPUInfo._Ext.PAE_PhysicalAddressExtension = CheckBit(edxreg, 6);
+ CPUInfo._Ext.MCE_MachineCheckException = CheckBit(edxreg, 7);
+ CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = CheckBit(edxreg, 8);
+ CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController = CheckBit(edxreg, 9);
+ CPUInfo._Ext.APIC_ID = (ebxreg >> 24) & 0xFF;
+ CPUInfo._Ext.SEP_FastSystemCall = CheckBit(edxreg, 11);
+ CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters = CheckBit(edxreg, 12);
+ CPUInfo._Ext.PGE_PTE_GlobalFlag = CheckBit(edxreg, 13);
+ CPUInfo._Ext.MCA_MachineCheckArchitecture = CheckBit(edxreg, 14);
+ CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions = CheckBit(edxreg, 15);
+ CPUInfo._Ext.FGPAT_PageAttributeTable = CheckBit(edxreg, 16);
+ CPUInfo._Ext.PSE36_36bitPageSizeExtension = CheckBit(edxreg, 17);
+ CPUInfo._Ext.PN_ProcessorSerialNumber = CheckBit(edxreg, 18);
+ CPUInfo._Ext.CLFSH_CFLUSH_Instruction = CheckBit(edxreg, 19);
+ CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize = (ebxreg >> 8) & 0xFF;
+ CPUInfo._Ext.DS_DebugStore = CheckBit(edxreg, 21);
+ CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl = CheckBit(edxreg, 22);
+ CPUInfo._Ext.MMX_MultimediaExtensions = CheckBit(edxreg, 23);
+ CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = CheckBit(edxreg, 24);
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions = CheckBit(edxreg, 25);
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = CheckBit(edxreg, 26);
+ CPUInfo._Ext.SS_SelfSnoop = CheckBit(edxreg, 27);
+ CPUInfo._Ext.HT_HyperThreading = CheckBit(edxreg, 28);
+ CPUInfo._Ext.HT_HyterThreadingSiblings = (ebxreg >> 16) & 0xFF;
+ CPUInfo._Ext.TM_ThermalMonitor = CheckBit(edxreg, 29);
+ CPUInfo._Ext.IA64_Intel64BitArchitecture = CheckBit(edxreg, 30);
+#endif
+}
+
+// const ProcessorInfo *CProcessor::GetCPUInfo()
+// =============================================
+// Calls all the other detection function to create an detailed
+// processor information
+///////////////////////////////////////////////////////////////
+const ProcessorInfo *CProcessor::GetCPUInfo()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // First of all we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return NULL;
+
+ // We read the standard CPUID level 0x00000000 which should
+ // be available on every x86 processor
+ __asm
+ {
+ mov eax, 0
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ mov ecxreg, ecx
+ }
+ // Then we connect the single register values to the vendor string
+ *((unsigned long *) CPUInfo.strVendor) = ebxreg;
+ *((unsigned long *) (CPUInfo.strVendor+4)) = edxreg;
+ *((unsigned long *) (CPUInfo.strVendor+8)) = ecxreg;
+
+ // We can also read the max. supported standard CPUID level
+ CPUInfo.MaxSupportedLevel = eaxreg & 0xFFFF;
+
+ // Then we read the ext. CPUID level 0x80000000
+ __asm
+ {
+ mov eax, 0x80000000
+ cpuid
+ mov eaxreg, eax
+ }
+ // ...to check the max. supportted extended CPUID level
+ CPUInfo.MaxSupportedExtendedLevel = eaxreg;
+
+ // Then we switch to the specific processor vendors
+ switch (ebxreg)
+ {
+ case 0x756E6547: // GenuineIntel
+ AnalyzeIntelProcessor();
+ break;
+ case 0x68747541: // AuthenticAMD
+ AnalyzeAMDProcessor();
+ break;
+ case 0x69727943: // CyrixInstead
+ // I really do not know anyone owning such a piece of crab
+ // So we analyze it as an unknown processor *ggggg*
+ default:
+ AnalyzeUnknownProcessor();
+ break;
+ }
+
+#endif
+ // After all we return the class CPUInfo member var
+ return (&CPUInfo);
+}
+
+#else
+// LL_DARWIN
+
+#include <mach/machine.h>
+#include <sys/sysctl.h>
+
+static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
+{
+ // We define 0xFFFFFFFF (= -1) as fully associative
+ if (uiWays == ((unsigned int) -1))
+ strcpy(buf, "fully associative"); /* Flawfinder: ignore */
+ else
+ {
+ if (uiWays == 1) // A one way associative cache is just direct mapped
+ strcpy(buf, "direct mapped"); /* Flawfinder: ignore */
+ else if (uiWays == 0) // This should not happen...
+ strcpy(buf, "unknown associative ways"); /* Flawfinder: ignore */
+ else // The x-way associative cache
+ sprintf(buf, "%d ways associative", uiWays); /* Flawfinder: ignore */
+ }
+ // To ease the function use we return the buffer
+ return buf;
+}
+static void TranslateTLB(ProcessorTLB *tlb)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the TLB is present
+ if (tlb->bPresent)
+ snprintf(tlb->strTLB, sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries); /* Flawfinder: ignore */
+ else
+ strcpy(tlb->strTLB, "Not present"); /* Flawfinder: ignore */
+}
+static void TranslateCache(ProcessorCache *cache)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the cache is present
+ if (cache->bPresent)
+ {
+ // If present we construct the string
+ snprintf(cache->strCache,sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize); /* Flawfinder: ignore */
+ if (cache->bSectored)
+ strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // Else we just say "Not present"
+ strcpy(cache->strCache, "Not present"); /* Flawfinder: ignore */
+ }
+}
+
+// void CProcessor::TranslateProcessorConfiguration()
+// ==================================================
+// Private class function to translate the processor configuration values
+// to strings
+/////////////////////////////////////////////////////////////////////////
+void CProcessor::TranslateProcessorConfiguration()
+{
+ // We just call the small functions defined above
+ TranslateTLB(&CPUInfo._Data);
+ TranslateTLB(&CPUInfo._Instruction);
+
+ TranslateCache(&CPUInfo._Trace);
+
+ TranslateCache(&CPUInfo._L1.Instruction);
+ TranslateCache(&CPUInfo._L1.Data);
+ TranslateCache(&CPUInfo._L2);
+ TranslateCache(&CPUInfo._L3);
+}
+
+// CProcessor::CProcessor
+// ======================
+// Class constructor:
+/////////////////////////
+CProcessor::CProcessor()
+{
+ uqwFrequency = 0;
+ memset(&CPUInfo, 0, sizeof(CPUInfo));
+}
+
+// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+// =========================================================================
+// Function to query the current CPU frequency
+////////////////////////////////////////////////////////////////////////////
+F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/)
+{
+ U64 frequency = 0;
+ size_t len = sizeof(frequency);
+
+ if(sysctlbyname("hw.cpufrequency", &frequency, &len, NULL, 0) == 0)
+ {
+ uqwFrequency = (F64)frequency;
+ }
+
+ return uqwFrequency;
+}
+
+static bool hasFeature(const char *name)
+{
+ bool result = false;
+ int val = 0;
+ size_t len = sizeof(val);
+
+ if(sysctlbyname(name, &val, &len, NULL, 0) == 0)
+ {
+ if(val != 0)
+ result = true;
+ }
+
+ return result;
+}
+
+// const ProcessorInfo *CProcessor::GetCPUInfo()
+// =============================================
+// Calls all the other detection function to create an detailed
+// processor information
+///////////////////////////////////////////////////////////////
+const ProcessorInfo *CProcessor::GetCPUInfo()
+{
+ int pagesize = 0;
+ int cachelinesize = 0;
+ int l1icachesize = 0;
+ int l1dcachesize = 0;
+ int l2settings = 0;
+ int l2cachesize = 0;
+ int l3settings = 0;
+ int l3cachesize = 0;
+ int ncpu = 0;
+ int cpusubtype = 0;
+
+ // sysctl knows all.
+ int mib[2];
+ size_t len;
+ mib[0] = CTL_HW;
+
+ mib[1] = HW_PAGESIZE;
+ len = sizeof(pagesize);
+ sysctl(mib, 2, &pagesize, &len, NULL, 0);
+
+ mib[1] = HW_CACHELINE;
+ len = sizeof(cachelinesize);
+ sysctl(mib, 2, &cachelinesize, &len, NULL, 0);
+
+ mib[1] = HW_L1ICACHESIZE;
+ len = sizeof(l1icachesize);
+ sysctl(mib, 2, &l1icachesize, &len, NULL, 0);
+
+ mib[1] = HW_L1DCACHESIZE;
+ len = sizeof(l1dcachesize);
+ sysctl(mib, 2, &l1dcachesize, &len, NULL, 0);
+
+ mib[1] = HW_L2SETTINGS;
+ len = sizeof(l2settings);
+ sysctl(mib, 2, &l2settings, &len, NULL, 0);
+
+ mib[1] = HW_L2CACHESIZE;
+ len = sizeof(l2cachesize);
+ sysctl(mib, 2, &l2cachesize, &len, NULL, 0);
+
+ mib[1] = HW_L3SETTINGS;
+ len = sizeof(l3settings);
+ sysctl(mib, 2, &l3settings, &len, NULL, 0);
+
+ mib[1] = HW_L3CACHESIZE;
+ len = sizeof(l3cachesize);
+ sysctl(mib, 2, &l3cachesize, &len, NULL, 0);
+
+ mib[1] = HW_NCPU;
+ len = sizeof(ncpu);
+ sysctl(mib, 2, &ncpu, &len, NULL, 0);
+
+ sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0);
+
+ strCPUName[0] = 0;
+
+ if((ncpu == 0) || (ncpu == 1))
+ {
+ // Uhhh...
+ }
+ else if(ncpu == 2)
+ {
+ strncat(strCPUName, "Dual ", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(strCPUName, sizeof(strCPUName), "%d x ", ncpu); /* Flawfinder: ignore */
+ }
+
+#if __ppc__
+ switch(cpusubtype)
+ {
+ case CPU_SUBTYPE_POWERPC_601:// ((cpu_subtype_t) 1)
+ strncat(strCPUName, "PowerPC 601", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+
+ break;
+ case CPU_SUBTYPE_POWERPC_602:// ((cpu_subtype_t) 2)
+ strncat(strCPUName, "PowerPC 602", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603:// ((cpu_subtype_t) 3)
+ strncat(strCPUName, "PowerPC 603", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603e:// ((cpu_subtype_t) 4)
+ strncat(strCPUName, "PowerPC 603e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603ev:// ((cpu_subtype_t) 5)
+ strncat(strCPUName, "PowerPC 603ev", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_604:// ((cpu_subtype_t) 6)
+ strncat(strCPUName, "PowerPC 604", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_604e:// ((cpu_subtype_t) 7)
+ strncat(strCPUName, "PowerPC 604e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_620:// ((cpu_subtype_t) 8)
+ strncat(strCPUName, "PowerPC 620", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_750:// ((cpu_subtype_t) 9)
+ strncat(strCPUName, "PowerPC 750", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G3", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_7400:// ((cpu_subtype_t) 10)
+ strncat(strCPUName, "PowerPC 7400", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_7450:// ((cpu_subtype_t) 11)
+ strncat(strCPUName, "PowerPC 7450", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_970:// ((cpu_subtype_t) 100)
+ strncat(strCPUName, "PowerPC 970", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G5", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+
+ default:
+ strncat(strCPUName, "PowerPC (Unknown)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // It's kinda like MMX or SSE...
+ CPUInfo._Ext.EMMX_MultimediaExtensions =
+ CPUInfo._Ext.MMX_MultimediaExtensions =
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions =
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.altivec");
+
+#endif
+
+#if __i386__
+ // MBW -- XXX -- TODO -- make this call AnalyzeIntelProcessor()?
+ switch(cpusubtype)
+ {
+ default:
+ strncat(strCPUName, "i386 (Unknown)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+
+ CPUInfo._Ext.EMMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); // MBW -- XXX -- this may be wrong...
+ CPUInfo._Ext.MMX_MultimediaExtensions = hasFeature("hw.optional.mmx");
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions = hasFeature("hw.optional.sse");
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.sse2");
+ CPUInfo._Ext.AA64_AMD64BitArchitecture = hasFeature("hw.optional.x86_64");
+
+#endif
+
+ // Terse CPU info uses this string...
+ strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1); /* Flawfinder: ignore */
+ CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
+
+ // Fun cache config stuff...
+
+ if(l1dcachesize != 0)
+ {
+ CPUInfo._L1.Data.bPresent = true;
+ snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", l1dcachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L1.Data.uiAssociativeWays = ???;
+ CPUInfo._L1.Data.uiLineSize = cachelinesize;
+ }
+
+ if(l1icachesize != 0)
+ {
+ CPUInfo._L1.Instruction.bPresent = true;
+ snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", l1icachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L1.Instruction.uiAssociativeWays = ???;
+ CPUInfo._L1.Instruction.uiLineSize = cachelinesize;
+ }
+
+ if(l2cachesize != 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l2cachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L2.uiAssociativeWays = ???;
+ CPUInfo._L2.uiLineSize = cachelinesize;
+ }
+
+ if(l3cachesize != 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l3cachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L2.uiAssociativeWays = ???;
+ CPUInfo._L2.uiLineSize = cachelinesize;
+ }
+
+ CPUInfo._Ext.FPU_FloatingPointUnit = hasFeature("hw.optional.floatingpoint");
+
+// printf("pagesize = 0x%x\n", pagesize);
+// printf("cachelinesize = 0x%x\n", cachelinesize);
+// printf("l1icachesize = 0x%x\n", l1icachesize);
+// printf("l1dcachesize = 0x%x\n", l1dcachesize);
+// printf("l2settings = 0x%x\n", l2settings);
+// printf("l2cachesize = 0x%x\n", l2cachesize);
+// printf("l3settings = 0x%x\n", l3settings);
+// printf("l3cachesize = 0x%x\n", l3cachesize);
+
+ // After reading we translate the configuration to strings
+ TranslateProcessorConfiguration();
+
+ // After all we return the class CPUInfo member var
+ return (&CPUInfo);
+}
+
+#endif // LL_DARWIN
+
+// bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen)
+// ======================================================================
+// Gets the frequency and processor information and writes it to a string
+/////////////////////////////////////////////////////////////////////////
+bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen)
+{
+#define LENCHECK len = (unsigned int) strlen(buf); if (len >= uiMaxLen) return false; strcpy(strBuffer, buf); strBuffer += len; /*Flawfinder: ignore*/
+#define COPYADD(str) strcpy(buf, str); LENCHECK; /* Flawfinder: ignore */
+#define FORMATADD(format, var) sprintf(buf, format, var); LENCHECK; /* Flawfinder: ignore */
+#define BOOLADD(str, boolvar) COPYADD(str); if (boolvar) { COPYADD(" Yes\n"); } else { COPYADD(" No\n"); }
+
+ char buf[1024]; /* Flawfinder: ignore */
+ unsigned int len;
+
+ // First we have to get the frequency
+ GetCPUFrequency(50);
+
+ // Then we get the processor information
+ GetCPUInfo();
+
+ // Now we construct the string (see the macros at function beginning)
+ strBuffer[0] = 0;
+
+ COPYADD("// CPU General Information\n//////////////////////////\n");
+ FORMATADD("Processor name: %s\n", strCPUName);
+ FORMATADD("Frequency: %.2f MHz\n\n", (float) uqwFrequency / 1000000.0f);
+ FORMATADD("Vendor: %s\n", CPUInfo.strVendor);
+ FORMATADD("Family: %s\n", CPUInfo.strFamily);
+ FORMATADD("Extended family: %d\n", CPUInfo.uiExtendedFamily);
+ FORMATADD("Model: %s\n", CPUInfo.strModel);
+ FORMATADD("Extended model: %d\n", CPUInfo.uiExtendedModel);
+ FORMATADD("Type: %s\n", CPUInfo.strType);
+ FORMATADD("Brand ID: %s\n", CPUInfo.strBrandID);
+ if (CPUInfo._Ext.PN_ProcessorSerialNumber)
+ {
+ FORMATADD("Processor Serial: %s\n", CPUInfo.strProcessorSerial);
+ }
+ else
+ {
+ COPYADD("Processor Serial: Disabled\n");
+ }
+
+ COPYADD("\n\n// CPU Configuration\n////////////////////\n");
+ FORMATADD("L1 instruction cache: %s\n", CPUInfo._L1.Instruction.strCache);
+ FORMATADD("L1 data cache: %s\n", CPUInfo._L1.Data.strCache);
+ FORMATADD("L2 cache: %s\n", CPUInfo._L2.strCache);
+ FORMATADD("L3 cache: %s\n", CPUInfo._L3.strCache);
+ FORMATADD("Trace cache: %s\n", CPUInfo._Trace.strCache);
+ FORMATADD("Instruction TLB: %s\n", CPUInfo._Instruction.strTLB);
+ FORMATADD("Data TLB: %s\n", CPUInfo._Data.strTLB);
+ FORMATADD("Max Supported CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedLevel);
+ FORMATADD("Max Supported Ext. CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedExtendedLevel);
+
+ COPYADD("\n\n// CPU Extensions\n/////////////////\n");
+ BOOLADD("AA64 AMD 64-bit Architecture: ", CPUInfo._Ext.AA64_AMD64BitArchitecture);
+ BOOLADD("ACPI Thermal Monitor And Clock Control: ", CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl);
+ BOOLADD("APIC Advanced Programmable Interrupt Controller: ", CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController);
+ FORMATADD(" APIC-ID: %d\n", CPUInfo._Ext.APIC_ID);
+ BOOLADD("CLFSH CLFLUSH Instruction Presence: ", CPUInfo._Ext.CLFSH_CFLUSH_Instruction);
+ FORMATADD(" CLFLUSH Instruction Cache Line Size: %d\n", CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize);
+ BOOLADD("CMOV Conditional Move And Compare Instructions: ", CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions);
+ BOOLADD("CX8 COMPXCHG8B Instruction: ", CPUInfo._Ext.CX8_COMPXCHG8B_Instruction);
+ BOOLADD("DE Debugging Extensions: ", CPUInfo._Ext.DE_DebuggingExtensions);
+ BOOLADD("DS Debug Store: ", CPUInfo._Ext.DS_DebugStore);
+ BOOLADD("FGPAT Page Attribute Table: ", CPUInfo._Ext.FGPAT_PageAttributeTable);
+ BOOLADD("FPU Floating Point Unit: ", CPUInfo._Ext.FPU_FloatingPointUnit);
+ BOOLADD("FXSR Fast Streaming SIMD Extensions Save/Restore:", CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore);
+ BOOLADD("HT Hyper Threading: ", CPUInfo._Ext.HT_HyperThreading);
+ BOOLADD("IA64 Intel 64-Bit Architecture: ", CPUInfo._Ext.IA64_Intel64BitArchitecture);
+ BOOLADD("MCA Machine Check Architecture: ", CPUInfo._Ext.MCA_MachineCheckArchitecture);
+ BOOLADD("MCE Machine Check Exception: ", CPUInfo._Ext.MCE_MachineCheckException);
+ BOOLADD("MMX Multimedia Extensions: ", CPUInfo._Ext.MMX_MultimediaExtensions);
+ BOOLADD("MMX+ Multimedia Extensions: ", CPUInfo._Ext.EMMX_MultimediaExtensions);
+ BOOLADD("MSR Model Specific Registers: ", CPUInfo._Ext.MSR_ModelSpecificRegisters);
+ BOOLADD("MTRR Memory Type Range Registers: ", CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters);
+ BOOLADD("PAE Physical Address Extension: ", CPUInfo._Ext.PAE_PhysicalAddressExtension);
+ BOOLADD("PGE PTE Global Flag: ", CPUInfo._Ext.PGE_PTE_GlobalFlag);
+ if (CPUInfo._Ext.PN_ProcessorSerialNumber)
+ {
+ FORMATADD("PN Processor Serial Number: %s\n", CPUInfo.strProcessorSerial);
+ }
+ else
+ {
+ COPYADD("PN Processor Serial Number: Disabled\n");
+ }
+ BOOLADD("PSE Page Size Extensions: ", CPUInfo._Ext.PSE_PageSizeExtensions);
+ BOOLADD("PSE36 36-bit Page Size Extension: ", CPUInfo._Ext.PSE36_36bitPageSizeExtension);
+ BOOLADD("SEP Fast System Call: ", CPUInfo._Ext.SEP_FastSystemCall);
+ BOOLADD("SS Self Snoop: ", CPUInfo._Ext.SS_SelfSnoop);
+ BOOLADD("SSE Streaming SIMD Extensions: ", CPUInfo._Ext.SSE_StreamingSIMD_Extensions);
+ BOOLADD("SSE2 Streaming SIMD 2 Extensions: ", CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions);
+ BOOLADD("TM Thermal Monitor: ", CPUInfo._Ext.TM_ThermalMonitor);
+ BOOLADD("TSC Time Stamp Counter: ", CPUInfo._Ext.TSC_TimeStampCounter);
+ BOOLADD("VME Virtual 8086 Mode Enhancements: ", CPUInfo._Ext.VME_Virtual8086ModeEnhancements);
+ BOOLADD("3DNow! Instructions: ", CPUInfo._Ext._3DNOW_InstructionExtensions);
+ BOOLADD("Enhanced 3DNow! Instructions: ", CPUInfo._Ext._E3DNOW_InstructionExtensions);
+
+ // Yippie!!!
+ return true;
+}
+
+// bool CProcessor::WriteInfoTextFile(const char *strFilename)
+// ===========================================================
+// Takes use of CProcessor::CPUInfoToText and saves the string to a
+// file
+///////////////////////////////////////////////////////////////////
+bool CProcessor::WriteInfoTextFile(const char *strFilename)
+{
+ char buf[16384]; /* Flawfinder: ignore */
+
+ // First we get the string
+ if (!CPUInfoToText(buf, 16383))
+ return false;
+
+ // Then we create a new file (CREATE_ALWAYS)
+ FILE *file = LLFile::fopen(strFilename, "w"); /* Flawfinder: ignore */
+ if (!file)
+ return false;
+
+ // After that we write the string to the file
+ unsigned long dwBytesToWrite, dwBytesWritten;
+ dwBytesToWrite = (unsigned long) strlen(buf); /*Flawfinder: ignore*/
+ dwBytesWritten = (unsigned long) fwrite(buf, 1, dwBytesToWrite, file);
+ fclose(file);
+ if (dwBytesToWrite != dwBytesWritten)
+ return false;
+
+ // Done
+ return true;
+}
diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h
new file mode 100644
index 0000000000..ad44e2ccb3
--- /dev/null
+++ b/indra/llcommon/llprocessor.h
@@ -0,0 +1,158 @@
+/**
+ * @file llprocessor.h
+ * @brief Code to figure out the processor. Originally by Benjamin Jurke.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Author: Benjamin Jurke
+// File history: 27.02.2002 File created.
+///////////////////////////////////////////
+
+
+#ifndef PROCESSOR_H
+#define PROCESSOR_H
+
+// Options:
+///////////
+#if LL_WINDOWS
+#define PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+#endif
+// Includes <windows.h> --> code gets os-dependend (Win32)
+
+
+typedef struct ProcessorExtensions
+{
+ bool FPU_FloatingPointUnit;
+ bool VME_Virtual8086ModeEnhancements;
+ bool DE_DebuggingExtensions;
+ bool PSE_PageSizeExtensions;
+ bool TSC_TimeStampCounter;
+ bool MSR_ModelSpecificRegisters;
+ bool PAE_PhysicalAddressExtension;
+ bool MCE_MachineCheckException;
+ bool CX8_COMPXCHG8B_Instruction;
+ bool APIC_AdvancedProgrammableInterruptController;
+ unsigned int APIC_ID;
+ bool SEP_FastSystemCall;
+ bool MTRR_MemoryTypeRangeRegisters;
+ bool PGE_PTE_GlobalFlag;
+ bool MCA_MachineCheckArchitecture;
+ bool CMOV_ConditionalMoveAndCompareInstructions;
+ bool FGPAT_PageAttributeTable;
+ bool PSE36_36bitPageSizeExtension;
+ bool PN_ProcessorSerialNumber;
+ bool CLFSH_CFLUSH_Instruction;
+ unsigned int CLFLUSH_InstructionCacheLineSize;
+ bool DS_DebugStore;
+ bool ACPI_ThermalMonitorAndClockControl;
+ bool EMMX_MultimediaExtensions;
+ bool MMX_MultimediaExtensions;
+ bool FXSR_FastStreamingSIMD_ExtensionsSaveRestore;
+ bool SSE_StreamingSIMD_Extensions;
+ bool SSE2_StreamingSIMD2_Extensions;
+ bool SS_SelfSnoop;
+ bool HT_HyperThreading;
+ unsigned int HT_HyterThreadingSiblings;
+ bool TM_ThermalMonitor;
+ bool IA64_Intel64BitArchitecture;
+ bool _3DNOW_InstructionExtensions;
+ bool _E3DNOW_InstructionExtensions;
+ bool AA64_AMD64BitArchitecture;
+} ProcessorExtensions;
+
+typedef struct ProcessorCache
+{
+ bool bPresent;
+ char strSize[32]; /* Flawfinder: ignore */
+ unsigned int uiAssociativeWays;
+ unsigned int uiLineSize;
+ bool bSectored;
+ char strCache[128]; /* Flawfinder: ignore */
+} ProcessorCache;
+
+typedef struct ProcessorL1Cache
+{
+ ProcessorCache Instruction;
+ ProcessorCache Data;
+} ProcessorL1Cache;
+
+typedef struct ProcessorTLB
+{
+ bool bPresent;
+ char strPageSize[32]; /* Flawfinder: ignore */
+ unsigned int uiAssociativeWays;
+ unsigned int uiEntries;
+ char strTLB[128]; /* Flawfinder: ignore */
+} ProcessorTLB;
+
+typedef struct ProcessorInfo
+{
+ char strVendor[16]; /* Flawfinder: ignore */
+ unsigned int uiFamily;
+ unsigned int uiExtendedFamily;
+ char strFamily[64]; /* Flawfinder: ignore */
+ unsigned int uiModel;
+ unsigned int uiExtendedModel;
+ char strModel[128]; /* Flawfinder: ignore */
+ unsigned int uiStepping;
+ unsigned int uiType;
+ char strType[64]; /* Flawfinder: ignore */
+ unsigned int uiBrandID;
+ char strBrandID[64]; /* Flawfinder: ignore */
+ char strProcessorSerial[64]; /* Flawfinder: ignore */
+ unsigned long MaxSupportedLevel;
+ unsigned long MaxSupportedExtendedLevel;
+ ProcessorExtensions _Ext;
+ ProcessorL1Cache _L1;
+ ProcessorCache _L2;
+ ProcessorCache _L3;
+ ProcessorCache _Trace;
+ ProcessorTLB _Instruction;
+ ProcessorTLB _Data;
+} ProcessorInfo;
+
+
+// CProcessor
+// ==========
+// Class for detecting the processor name, type and available
+// extensions as long as it's speed.
+/////////////////////////////////////////////////////////////
+class CProcessor
+{
+// Constructor / Destructor:
+////////////////////////////
+public:
+ CProcessor();
+
+// Private vars:
+////////////////
+public:
+ F64 uqwFrequency;
+ char strCPUName[128]; /* Flawfinder: ignore */
+ ProcessorInfo CPUInfo;
+
+// Private functions:
+/////////////////////
+private:
+ bool AnalyzeIntelProcessor();
+ bool AnalyzeAMDProcessor();
+ bool AnalyzeUnknownProcessor();
+ bool CheckCPUIDPresence();
+ void DecodeProcessorConfiguration(unsigned int cfg);
+ void TranslateProcessorConfiguration();
+ void GetStandardProcessorConfiguration();
+ void GetStandardProcessorExtensions();
+
+// Public functions:
+////////////////////
+public:
+ F64 GetCPUFrequency(unsigned int uiMeasureMSecs);
+ const ProcessorInfo *GetCPUInfo();
+ bool CPUInfoToText(char *strBuffer, unsigned int uiMaxLen);
+ bool WriteInfoTextFile(const char *strFilename);
+};
+
+
+#endif
diff --git a/indra/llcommon/llptrskiplist.h b/indra/llcommon/llptrskiplist.h
new file mode 100644
index 0000000000..fd4dcdf87b
--- /dev/null
+++ b/indra/llcommon/llptrskiplist.h
@@ -0,0 +1,704 @@
+/**
+ * @file llptrskiplist.h
+ * @brief Skip list implementation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPTRSKIPLIST_H
+#define LL_LLPTRSKIPLIST_H
+
+#include "llerror.h"
+//#include "vmath.h"
+
+/////////////////////////////////////////////
+//
+// LLPtrSkipList implementation - skip list for pointers to objects
+//
+
+template <class DATA_TYPE, S32 BINARY_DEPTH = 8>
+class LLPtrSkipList
+{
+public:
+ friend class LLPtrSkipNode;
+
+ // basic constructor
+ LLPtrSkipList();
+ // basic constructor including sorter
+ LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
+ BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second));
+ ~LLPtrSkipList();
+
+ inline void setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second));
+ inline void setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second));
+
+ inline BOOL addData(DATA_TYPE *data);
+
+ inline BOOL checkData(const DATA_TYPE *data);
+
+ inline S32 getLength(); // returns number of items in the list - NOT constant time!
+
+ inline BOOL removeData(const DATA_TYPE *data);
+
+ // note that b_sort is ignored
+ inline BOOL moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort);
+
+ inline BOOL moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort);
+
+ // resort -- use when the value we're sorting by changes
+ /* IW 12/6/02 - This doesn't work!
+ Instead, remove the data BEFORE you change it
+ Then re-insert it after you change it
+ BOOL resortData(DATA_TYPE *data)
+ */
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ inline BOOL deleteData(const DATA_TYPE *data);
+
+ // remove all nodes from the list and delete data
+ inline void deleteAllData();
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE *getNextData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // delete the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getFirstData();
+
+ // TRUE if nodes are not in sorted order
+ inline BOOL corrupt();
+
+protected:
+ class LLPtrSkipNode
+ {
+ public:
+ LLPtrSkipNode()
+ : mData(NULL)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ LLPtrSkipNode(DATA_TYPE *data)
+ : mData(data)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ ~LLPtrSkipNode()
+ {
+ if (mData)
+ {
+ llerror("Attempting to call LLPtrSkipNode destructor with a non-null mDatap!", 1);
+ }
+ }
+
+ // delete associated data and NULLs out pointer
+ void deleteData()
+ {
+ delete mData;
+ mData = NULL;
+ }
+
+ // NULLs out pointer
+ void removeData()
+ {
+ mData = NULL;
+ }
+
+ DATA_TYPE *mData;
+ LLPtrSkipNode *mForward[BINARY_DEPTH];
+ };
+
+ static BOOL defaultEquals(const DATA_TYPE *first, const DATA_TYPE *second)
+ {
+ return first == second;
+ }
+
+
+ LLPtrSkipNode mHead;
+ LLPtrSkipNode *mUpdate[BINARY_DEPTH];
+ LLPtrSkipNode *mCurrentp;
+ LLPtrSkipNode *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const DATA_TYPE *first, const DATA_TYPE *second);
+ BOOL (*mEquals)(const DATA_TYPE *first, const DATA_TYPE *second);
+};
+
+
+// basic constructor
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList()
+ : mInsertFirst(NULL), mEquals(defaultEquals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// basic constructor including sorter
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
+ BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second))
+ :mInsertFirst(insert_first), mEquals(equals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::~LLPtrSkipList()
+{
+ removeAllNodes();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second))
+{
+ mInsertFirst = insert_first;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second))
+{
+ mEquals = equals;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::addData(DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipNode *snode = new LLPtrSkipNode(data);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return TRUE;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mData, data);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// returns number of items in the list
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ for (LLPtrSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+ return length;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ current->removeData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// note that b_sort is ignored
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort)
+{
+ BOOL removed = removeData(data);
+ BOOL added = newlist->addData(data);
+ return removed && added;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort)
+{
+ if (mCurrentOperatingp)
+ {
+ mCurrentp = mCurrentOperatingp->mForward[0];
+ BOOL removed = removeData(mCurrentOperatingp);
+ BOOL added = newlist->addData(mCurrentOperatingp);
+ mCurrentOperatingp = mCurrentp;
+ return removed && added;
+ }
+ return FALSE;
+}
+
+// resort -- use when the value we're sorting by changes
+/* IW 12/6/02 - This doesn't work!
+ Instead, remove the data BEFORE you change it
+ Then re-insert it after you change it
+BOOL resortData(DATA_TYPE *data)
+{
+ removeData(data);
+ addData(data);
+}
+*/
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
+{
+ LLPtrSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = current->mForward[0];
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = current->mForward[0];
+ }
+
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ current->deleteData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// remove all nodes from the list and delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteAllData()
+{
+ LLPtrSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = *mCurrentp->mForward;
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = *mCurrentp->mForward;
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mData);
+ }
+}
+
+// delete the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mData);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::corrupt()
+{
+ LLPtrSkipNode *previous = mHead.mForward[0];
+
+ // Empty lists are not corrupt.
+ if (!previous) return FALSE;
+
+ LLPtrSkipNode *current = previous->mForward[0];
+ while(current)
+ {
+ if (!mInsertFirst(previous->mData, current->mData))
+ {
+ // prev shouldn't be in front of cur!
+ return TRUE;
+ }
+ current = current->mForward[0];
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/indra/llcommon/llptrskipmap.h b/indra/llcommon/llptrskipmap.h
new file mode 100644
index 0000000000..63668c34fe
--- /dev/null
+++ b/indra/llcommon/llptrskipmap.h
@@ -0,0 +1,1219 @@
+/**
+ * @file llptrskipmap.h
+ * @brief Just like a LLSkipMap, but since it's pointers, you can call
+ * deleteAllData
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLPTRSKIPMAP_H
+#define LL_LLPTRSKIPMAP_H
+
+#include "llerror.h"
+#include "llrand.h"
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH = 8>
+class LLPtrSkipMapNode
+{
+public:
+ LLPtrSkipMapNode()
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mIndex;
+
+ for (i = 0; i < (S32)sizeof(INDEX_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+
+ zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLPtrSkipMapNode(const INDEX_T &index)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLPtrSkipMapNode(const INDEX_T &index, DATA_T datap)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ mData = datap;
+ }
+
+ ~LLPtrSkipMapNode()
+ {
+ }
+
+ // delete associated data and NULLs out pointer
+ void deleteData()
+ {
+ delete mData;
+ mData = 0;
+ }
+
+ // NULLs out pointer
+ void removeData()
+ {
+ mData = 0;
+ }
+
+ INDEX_T mIndex;
+ DATA_T mData;
+ LLPtrSkipMapNode *mForward[BINARY_DEPTH];
+
+private:
+ // Disallow copying of LLPtrSkipMapNodes by not implementing these methods.
+ LLPtrSkipMapNode(const LLPtrSkipMapNode &);
+ LLPtrSkipMapNode &operator=(const LLPtrSkipMapNode &rhs);
+};
+
+//---------------------------------------------------------------------------
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH = 8>
+class LLPtrSkipMap
+{
+public:
+ typedef BOOL (*compare)(const DATA_T& first, const DATA_T& second);
+ typedef compare insert_func;
+ typedef compare equals_func;
+
+ void init();
+
+ // basic constructor
+ LLPtrSkipMap();
+
+ // basic constructor including sorter
+ LLPtrSkipMap(insert_func insert_first, equals_func equals);
+
+ ~LLPtrSkipMap();
+
+ void setInsertFirst(insert_func insert_first);
+ void setEquals(equals_func equals);
+
+ DATA_T &addData(const INDEX_T &index, DATA_T datap);
+ DATA_T &addData(const INDEX_T &index);
+ DATA_T &getData(const INDEX_T &index);
+ DATA_T &operator[](const INDEX_T &index);
+
+ // If index present, returns data.
+ // If index not present, adds <index,NULL> and returns NULL.
+ DATA_T &getData(const INDEX_T &index, BOOL &b_new_entry);
+
+ // returns data entry before and after index
+ BOOL getInterval(const INDEX_T &index, INDEX_T &index_before, INDEX_T &index_after,
+ DATA_T &data_before, DATA_T &data_after );
+
+ // Returns TRUE if data present in map.
+ BOOL checkData(const INDEX_T &index);
+
+ // Returns TRUE if key is present in map. This is useful if you
+ // are potentially storing NULL pointers in the map
+ BOOL checkKey(const INDEX_T &index);
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ DATA_T getIfThere(const INDEX_T &index);
+
+ INDEX_T reverseLookup(const DATA_T datap);
+
+ // returns number of items in the list
+ S32 getLength(); // WARNING! getLength is O(n), not O(1)!
+
+ BOOL removeData(const INDEX_T &index);
+ BOOL deleteData(const INDEX_T &index);
+
+ // remove all nodes from the list but do not delete data
+ void removeAllData();
+ void deleteAllData();
+
+ // place mCurrentp on first node
+ void resetList();
+
+ // return the data currently pointed to
+ DATA_T getCurrentDataWithoutIncrement();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_T getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ DATA_T getNextData();
+
+ INDEX_T getNextKey();
+
+ // return the key currently pointed to
+ INDEX_T getCurrentKeyWithoutIncrement();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void removeCurrentData();
+
+ void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_T getFirstData();
+
+ INDEX_T getFirstKey();
+
+ static BOOL defaultEquals(const INDEX_T &first, const INDEX_T &second)
+ {
+ return first == second;
+ }
+
+private:
+ // don't generate implicit copy constructor or copy assignment
+ LLPtrSkipMap(const LLPtrSkipMap &);
+ LLPtrSkipMap &operator=(const LLPtrSkipMap &);
+
+private:
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mUpdate[BINARY_DEPTH];
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mCurrentp;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const INDEX_T &first, const INDEX_T &second);
+ BOOL (*mEquals)(const INDEX_T &first, const INDEX_T &second);
+ S32 mNumberOfSteps;
+};
+
+//////////////////////////////////////////////////
+//
+// LLPtrSkipMap implementation
+//
+//
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::LLPtrSkipMap()
+ : mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::LLPtrSkipMap(insert_func insert_first,
+ equals_func equals)
+: mInsertFirst(insert_first),
+ mEquals(equals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::~LLPtrSkipMap()
+{
+ removeAllData();
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
+{
+ mInsertFirst = insert_first;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::setEquals(equals_func equals)
+{
+ mEquals = equals;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::addData(const INDEX_T &index, DATA_T datap)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // replace the existing data if a node is already there
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+ current->mData = datap;
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ {
+ break;
+ }
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index, datap);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::addData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+ mNumberOfSteps++;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getInterval(const INDEX_T &index,
+ INDEX_T &index_before,
+ INDEX_T &index_after,
+ DATA_T &data_before,
+ DATA_T &data_after)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ BOOL both_found = TRUE;
+
+ if (current != &mHead)
+ {
+ index_before = current->mIndex;
+ data_before = current->mData;
+ }
+ else
+ {
+ data_before = 0;
+ index_before = 0;
+ both_found = FALSE;
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ data_after = current->mData;
+ index_after = current->mIndex;
+ }
+ else
+ {
+ data_after = 0;
+ index_after = 0;
+ both_found = FALSE;
+ }
+
+ return both_found;
+}
+
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::operator[](const INDEX_T &index)
+{
+
+ return getData(index);
+}
+
+// If index present, returns data.
+// If index not present, adds <index,NULL> and returns NULL.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getData(const INDEX_T &index, BOOL &b_new_entry)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+ b_new_entry = TRUE;
+ addData(index);
+
+ return current->mData;
+}
+
+// Returns TRUE if data present in map.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::checkData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ // Gets rid of some compiler ambiguity for the LLPointer<> templated class.
+ if (current->mData)
+ {
+ return mEquals(current->mIndex, index);
+ }
+ }
+
+ return FALSE;
+}
+
+// Returns TRUE if key is present in map. This is useful if you
+// are potentially storing NULL pointers in the map
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::checkKey(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mIndex, index);
+ }
+
+ return FALSE;
+}
+
+// If there, returns the data.
+// If not, returns NULL.
+// Never adds entries to the map.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getIfThere(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ if (mEquals(current->mIndex, index))
+ {
+ return current->mData;
+ }
+ }
+
+ // Avoid Linux compiler warning on returning NULL.
+ return (DATA_T)0;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::reverseLookup(const DATA_T datap)
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+
+ while (current)
+ {
+ if (datap == current->mData)
+ {
+
+ return current->mIndex;
+ }
+ current = *current->mForward;
+ }
+
+ // not found! return NULL
+ return INDEX_T();
+}
+
+// returns number of items in the list
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline S32 LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>* temp;
+ for (temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+
+ return length;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ // clean up cuurent
+ current->removeData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ // clean up cuurent
+ current->deleteData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+// remove all nodes from the list but do not delete data
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeAllData()
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteAllData()
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// return the data currently pointed to
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentDataWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes warning
+ return (DATA_T)0; // equivalent, but no warning
+ }
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes warning
+ return (DATA_T)0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_T)0; // equivalent, but removes warning
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getNextKey()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+// return the key currently pointed to
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentKeyWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (INDEX_T)0; // equivalent, but removes warning
+ }
+}
+
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mIndex);
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mIndex);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_T)0; // equivalent, but removes warning
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getFirstKey()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
new file mode 100644
index 0000000000..bc41927d38
--- /dev/null
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -0,0 +1,491 @@
+/**
+ * @file llqueuedthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llqueuedthread.h"
+#include "llstl.h"
+
+//============================================================================
+
+// MAIN THREAD
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runalways) :
+ LLThread(name),
+ mThreaded(threaded),
+ mRunAlways(runalways),
+ mIdleThread(TRUE),
+ mNextHandle(0)
+{
+ if (mThreaded)
+ {
+ start();
+ }
+}
+
+// MAIN THREAD
+LLQueuedThread::~LLQueuedThread()
+{
+ setQuitting();
+
+ unpause(); // MAIN THREAD
+ if (mThreaded)
+ {
+ S32 timeout = 100;
+ for ( ; timeout>0; timeout--)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ ms_sleep(100);
+ LLThread::yield();
+ }
+ if (timeout == 0)
+ {
+ llwarns << "~LLQueuedThread (" << mName << ") timed out!" << llendl;
+ }
+ }
+ else
+ {
+ mStatus = STOPPED;
+ }
+
+ QueuedRequest* req;
+ while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
+ {
+ req->deleteRequest();
+ }
+
+ // ~LLThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+// MAIN THREAD
+void LLQueuedThread::update(U32 ms_elapsed)
+{
+ updateQueue(0);
+}
+
+void LLQueuedThread::updateQueue(S32 inc)
+{
+ // If mRunAlways == TRUE, unpause the thread whenever we put something into the queue.
+ // If mRunAlways == FALSE, we only unpause the thread when updateQueue() is called from the main loop (i.e. between rendered frames)
+
+ if (inc == 0) // Frame Update
+ {
+ if (mThreaded)
+ {
+ unpause();
+ wake(); // Wake the thread up if necessary.
+ }
+ else
+ {
+ while (processNextRequest() > 0)
+ ;
+ }
+ }
+ else
+ {
+ // Something has been added to the queue
+ if (mRunAlways)
+ {
+ if (mThreaded)
+ {
+ wake(); // Wake the thread up if necessary.
+ }
+ else
+ {
+ while(processNextRequest() > 0)
+ ;
+ }
+ }
+ }
+}
+
+//virtual
+// May be called from any thread
+S32 LLQueuedThread::getPending(bool child_thread)
+{
+ S32 res;
+ lockData();
+ res = mRequestQueue.size();
+ unlockData();
+ return res;
+}
+
+// MAIN thread
+void LLQueuedThread::waitOnPending()
+{
+ while(1)
+ {
+ updateQueue(0);
+
+ if (mIdleThread)
+ {
+ break;
+ }
+ if (mThreaded)
+ {
+ yield();
+ }
+ }
+ return;
+}
+
+// MAIN thread
+void LLQueuedThread::printQueueStats()
+{
+ lockData();
+ if (!mRequestQueue.empty())
+ {
+ QueuedRequest *req = *mRequestQueue.begin();
+ llinfos << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << llendl;
+ }
+ else
+ {
+ llinfos << "Queued Thread Idle" << llendl;
+ }
+ unlockData();
+}
+
+// MAIN thread
+LLQueuedThread::handle_t LLQueuedThread::generateHandle()
+{
+ lockData();
+ while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
+ {
+ mNextHandle++;
+ }
+ unlockData();
+ return mNextHandle++;
+}
+
+// MAIN thread
+bool LLQueuedThread::addRequest(QueuedRequest* req)
+{
+ if (mStatus == QUITTING)
+ {
+ return false;
+ }
+
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+ mRequestQueue.insert(req);
+ mRequestHash.insert(req);
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Added req [%08d]",handle) << llendl;
+#endif
+ unlockData();
+
+ updateQueue(1);
+
+ return true;
+}
+
+// MAIN thread
+bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
+{
+ llassert (handle != nullHandle())
+ bool res = false;
+ bool waspaused = isPaused();
+ bool done = false;
+ while(!done)
+ {
+ updateQueue(0); // unpauses
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (!req)
+ {
+ done = true; // request does not exist
+ }
+ else if (req->getStatus() == STATUS_COMPLETE)
+ {
+ res = true;
+ if (auto_complete)
+ {
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ }
+ done = true;
+ }
+ unlockData();
+
+ if (!done && mThreaded)
+ {
+ yield();
+ }
+ }
+ if (waspaused)
+ {
+ pause();
+ }
+ return res;
+}
+
+// MAIN thread
+LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
+{
+ if (handle == nullHandle())
+ {
+ return 0;
+ }
+ lockData();
+ QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle);
+ unlockData();
+ return res;
+}
+
+LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->getStatus();
+ }
+ unlockData();
+ return res;
+}
+
+LLQueuedThread::status_t LLQueuedThread::abortRequest(handle_t handle, U32 flags)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->abortRequest(flags);
+ if ((flags & AUTO_COMPLETE) && (res == STATUS_COMPLETE))
+ {
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ }
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Aborted req [%08d]",handle) << llendl;
+#endif
+ }
+ unlockData();
+ return res;
+}
+
+// MAIN thread
+LLQueuedThread::status_t LLQueuedThread::setFlags(handle_t handle, U32 flags)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->setFlags(flags);
+ }
+ unlockData();
+ return res;
+}
+
+void LLQueuedThread::setPriority(handle_t handle, U32 priority)
+{
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req && (req->getStatus() == STATUS_QUEUED))
+ {
+ llverify(mRequestQueue.erase(req) == 1);
+ req->setPriority(priority);
+ mRequestQueue.insert(req);
+ }
+ unlockData();
+}
+
+bool LLQueuedThread::completeRequest(handle_t handle)
+{
+ bool res = false;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ llassert(req->getStatus() != STATUS_QUEUED && req->getStatus() != STATUS_ABORT);
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
+#endif
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ res = true;
+ }
+ unlockData();
+ return res;
+}
+
+bool LLQueuedThread::check()
+{
+#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging
+ for (int i=0; i<REQUEST_HASH_SIZE; i++)
+ {
+ LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i);
+ while (entry)
+ {
+ if (entry->getHashKey() > mNextHandle)
+ {
+ llerrs << "Hash Error" << llendl;
+ return false;
+ }
+ entry = entry->getNextEntry();
+ }
+ }
+#endif
+ return true;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+int LLQueuedThread::processNextRequest()
+{
+ QueuedRequest *req = 0;
+ // Get next request from pool
+ lockData();
+ while(1)
+ {
+ if (!mRequestQueue.empty())
+ {
+ req = *mRequestQueue.begin();
+ mRequestQueue.erase(mRequestQueue.begin());
+ }
+ if (req && req->getStatus() == STATUS_ABORT)
+ {
+ req->setStatus(STATUS_ABORTED);
+ req = 0;
+ }
+ else
+ {
+ llassert (!req || req->getStatus() == STATUS_QUEUED)
+ break;
+ }
+ }
+ if (req)
+ {
+ req->setStatus(STATUS_INPROGRESS);
+ }
+ unlockData();
+
+ // This is the only place we will cal req->setStatus() after
+ // it has initially been seet to STATUS_QUEUED, so it is
+ // safe to access req.
+ if (req)
+ {
+ // process request
+ bool complete = processRequest(req);
+
+ if (complete)
+ {
+ lockData();
+ req->setStatus(STATUS_COMPLETE);
+ req->finishRequest();
+ if (req->getFlags() & AUTO_COMPLETE)
+ {
+ llverify(mRequestHash.erase(req))
+ req->deleteRequest();
+// check();
+ }
+ unlockData();
+ }
+ else
+ {
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+ mRequestQueue.insert(req);
+ unlockData();
+ }
+ }
+
+ int res;
+ if (getPending(true) == 0)
+ {
+ if (isQuitting())
+ {
+ res = -1; // exit thread
+ }
+ else
+ {
+ res = 0;
+ }
+ }
+ else
+ {
+ res = 1;
+ }
+ return res;
+}
+
+bool LLQueuedThread::runCondition()
+{
+ // mRunCondition must be locked here
+ return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE;
+}
+
+void LLQueuedThread::run()
+{
+ llinfos << "QUEUED THREAD STARTING" << llendl;
+
+ while (1)
+ {
+ // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
+ checkPause();
+
+ if(isQuitting())
+ break;
+
+ //llinfos << "QUEUED THREAD RUNNING, queue size = " << mRequestQueue.size() << llendl;
+
+ mIdleThread = FALSE;
+
+ int res = processNextRequest();
+ if (res == 0)
+ {
+ mIdleThread = TRUE;
+ }
+
+ if (res < 0) // finished working and want to exit
+ {
+ break;
+ }
+ }
+
+ llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
+}
+
+//============================================================================
+
+LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
+ LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
+ mStatus(STATUS_UNKNOWN),
+ mPriority(priority),
+ mFlags(flags)
+{
+}
+
+LLQueuedThread::QueuedRequest::~QueuedRequest()
+{
+ if (mStatus != STATUS_DELETE)
+ {
+ llerrs << "Attemt to directly delete a LLQueuedThread::QueuedRequest; use deleteRequest()" << llendl;
+ }
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::finishRequest()
+{
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::deleteRequest()
+{
+ setStatus(STATUS_DELETE);
+ delete this;
+}
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
new file mode 100644
index 0000000000..beff473f52
--- /dev/null
+++ b/indra/llcommon/llqueuedthread.h
@@ -0,0 +1,202 @@
+/**
+ * @file llqueuedthread.h
+ * @brief
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUEUEDTHREAD_H
+#define LL_LLQUEUEDTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llapr.h"
+
+#include "llthread.h"
+#include "llsimplehash.h"
+
+//============================================================================
+// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
+// It is assumed that LLQueuedThreads are rarely created/destroyed.
+
+class LLQueuedThread : public LLThread
+{
+ //------------------------------------------------------------------------
+public:
+ enum priority_t {
+ PRIORITY_IMMEDIATE = 0x7FFFFFFF,
+ PRIORITY_URGENT = 0x40000000,
+ PRIORITY_HIGH = 0x30000000,
+ PRIORITY_NORMAL = 0x20000000,
+ PRIORITY_LOW = 0x10000000,
+ PRIORITY_LOWBITS = 0x0FFFFFFF
+ };
+ enum status_t {
+ STATUS_EXPIRED = -1,
+ STATUS_UNKNOWN = 0,
+ STATUS_QUEUED = 1,
+ STATUS_INPROGRESS = 2,
+ STATUS_COMPLETE = 3,
+ STATUS_ABORT = 4,
+ STATUS_ABORTED = 5,
+ STATUS_DELETE = 6
+ };
+ enum flags_t {
+ AUTO_COMPLETE = 1,
+ AUTO_DELETE = 2 // child-class dependent
+ };
+
+ typedef U32 handle_t;
+
+ //------------------------------------------------------------------------
+public:
+
+ class QueuedRequest : public LLSimpleHashEntry<handle_t>
+ {
+ friend class LLQueuedThread;
+
+ protected:
+ ~QueuedRequest(); // use deleteRequest()
+
+ public:
+ QueuedRequest(handle_t handle, U32 priority, U32 flags = 0);
+
+ status_t getStatus()
+ {
+ return mStatus;
+ }
+ U32 getPriority() const
+ {
+ return mPriority;
+ }
+ U32 getFlags() const
+ {
+ return mFlags;
+ }
+ bool higherPriority(const QueuedRequest& second) const
+ {
+ if ( mPriority == second.mPriority)
+ return mHashKey < second.mHashKey;
+ else
+ return mPriority > second.mPriority;
+ }
+
+ protected:
+ status_t setStatus(status_t newstatus)
+ {
+ status_t oldstatus = mStatus;
+ mStatus = newstatus;
+ return oldstatus;
+ }
+ status_t abortRequest(U32 flags)
+ {
+ // NOTE: flags are |'d
+ if (mStatus == STATUS_QUEUED)
+ {
+ setStatus(STATUS_ABORT);
+ }
+ mFlags |= flags;
+ status_t status = mStatus;
+ return status;
+ }
+ status_t setFlags(U32 flags)
+ {
+ // NOTE: flags are |'d
+ mFlags |= flags;
+ status_t status = mStatus;
+ return status;
+ }
+
+ virtual void finishRequest(); // Always called when after has been processed
+ virtual void deleteRequest(); // Only method to delete a request
+
+ void setPriority(U32 pri)
+ {
+ // Only do this on a request that is not in a queued list!
+ mPriority = pri;
+ };
+
+ protected:
+ LLAtomic32<status_t> mStatus;
+ U32 mPriority;
+ U32 mFlags;
+ };
+
+protected:
+ struct queued_request_less
+ {
+ bool operator()(const QueuedRequest* lhs, const QueuedRequest* rhs) const
+ {
+ return lhs->higherPriority(*rhs); // higher priority in front of queue (set)
+ }
+ };
+
+ //------------------------------------------------------------------------
+
+public:
+ static handle_t nullHandle() { return handle_t(0); }
+
+public:
+ LLQueuedThread(const std::string& name, bool threaded = TRUE, bool runalways = TRUE);
+ virtual ~LLQueuedThread();
+
+private:
+ // No copy constructor or copy assignment
+ LLQueuedThread(const LLQueuedThread&);
+ LLQueuedThread& operator=(const LLQueuedThread&);
+
+ virtual bool runCondition(void);
+ virtual void run(void);
+
+protected:
+ handle_t generateHandle();
+ bool addRequest(QueuedRequest* req);
+ int processNextRequest(void);
+
+ virtual bool processRequest(QueuedRequest* req) = 0;
+
+public:
+ bool waitForResult(handle_t handle, bool auto_complete = true);
+
+ void update(U32 ms_elapsed);
+ void updateQueue(S32 inc);
+ void waitOnPending();
+ void printQueueStats();
+
+ S32 getPending(bool child_thread = false);
+ bool getThreaded() { return mThreaded ? true : false; }
+ bool getRunAlways() { return mRunAlways ? true : false; }
+
+ // Request accessors
+ status_t getRequestStatus(handle_t handle);
+ status_t abortRequest(handle_t handle, U32 flags = 0);
+ status_t setFlags(handle_t handle, U32 flags);
+ void setPriority(handle_t handle, U32 priority);
+ bool completeRequest(handle_t handle);
+ // This is public for support classes like LLWorkerThread,
+ // but generally the methods above should be used.
+ QueuedRequest* getRequest(handle_t handle);
+
+ // debug (see source)
+ bool check();
+
+protected:
+ BOOL mThreaded; // if false, run on main thread and do updates during update()
+ BOOL mRunAlways; // if false, only wake the threads when updateClass() is called
+ LLAtomic32<BOOL> mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
+
+ typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
+ request_queue_t mRequestQueue;
+
+ enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2
+ typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t;
+ request_hash_t mRequestHash;
+
+ handle_t mNextHandle;
+};
+
+#endif // LL_LLQUEUEDTHREAD_H
diff --git a/indra/llcommon/llrun.cpp b/indra/llcommon/llrun.cpp
new file mode 100644
index 0000000000..0e6a80e766
--- /dev/null
+++ b/indra/llcommon/llrun.cpp
@@ -0,0 +1,156 @@
+/**
+ * @file llrun.cpp
+ * @author Phoenix
+ * @date 2006-02-16
+ * @brief Implementation of the LLRunner and related classes
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llrun.h"
+
+#include "llframetimer.h"
+
+static const LLRunner::run_handle_t INVALID_RUN_HANDLE = 0;
+
+/**
+ * LLRunner
+ */
+LLRunner::LLRunner() :
+ mNextHandle(1)
+{
+}
+
+LLRunner::~LLRunner()
+{
+ mRunOnce.clear();
+ mRunEvery.clear();
+}
+
+S32 LLRunner::run()
+{
+ // We collect all of the runnables which should be run. Since the
+ // runnables are allowed to adjust the run list, we need to copy
+ // them into a temporary structure which then iterates over them
+ // to call out of this method into the runnables.
+ F64 now = LLFrameTimer::getTotalSeconds();
+ run_list_t run_now;
+
+ // Collect the run once. We erase the matching ones now because
+ // it's easier. If we find a reason to keep them around for a
+ // while, we can restructure this method.
+ LLRunner::run_list_t::iterator iter = mRunOnce.begin();
+ for( ; iter != mRunOnce.end(); )
+ {
+ if(now > (*iter).mNextRunAt)
+ {
+ run_now.push_back(*iter);
+ iter = mRunOnce.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ // Collect the ones that repeat.
+ iter = mRunEvery.begin();
+ LLRunner::run_list_t::iterator end = mRunEvery.end();
+ for( ; iter != end; ++iter )
+ {
+ if(now > (*iter).mNextRunAt)
+ {
+ (*iter).mNextRunAt = now + (*iter).mIncrement;
+ run_now.push_back(*iter);
+ }
+ }
+
+ // Now, run them.
+ iter = run_now.begin();
+ end = run_now.end();
+ for( ; iter != end; ++iter )
+ {
+ (*iter).mRunnable->run(this, (*iter).mHandle);
+ }
+ return run_now.size();
+}
+
+LLRunner::run_handle_t LLRunner::addRunnable(
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 seconds)
+{
+ if(!runnable) return INVALID_RUN_HANDLE;
+ run_handle_t handle = mNextHandle++;
+ F64 next_run = LLFrameTimer::getTotalSeconds() + seconds;
+ LLRunInfo info(handle, runnable, schedule, next_run, seconds);
+ switch(schedule)
+ {
+ case RUN_IN:
+ // We could optimize this a bit by sorting this on entry.
+ mRunOnce.push_back(info);
+ break;
+ case RUN_EVERY:
+ mRunEvery.push_back(info);
+ break;
+ default:
+ handle = INVALID_RUN_HANDLE;
+ break;
+ }
+ return handle;
+}
+
+LLRunner::run_ptr_t LLRunner::removeRunnable(LLRunner::run_handle_t handle)
+{
+ LLRunner::run_ptr_t rv;
+ LLRunner::run_list_t::iterator iter = mRunOnce.begin();
+ LLRunner::run_list_t::iterator end = mRunOnce.end();
+ for( ; iter != end; ++iter)
+ {
+ if((*iter).mHandle == handle)
+ {
+ rv = (*iter).mRunnable;
+ mRunOnce.erase(iter);
+ return rv;
+ }
+ }
+
+ iter = mRunEvery.begin();
+ end = mRunEvery.end();
+ for( ; iter != end; ++iter)
+ {
+ if((*iter).mHandle == handle)
+ {
+ rv = (*iter).mRunnable;
+ mRunEvery.erase(iter);
+ return rv;
+ }
+ }
+ return rv;
+}
+
+/**
+ * LLRunner::LLRunInfo
+ */
+LLRunner::LLRunInfo::LLRunInfo(
+ run_handle_t handle,
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 next_run_after,
+ F64 increment) :
+ mHandle(handle),
+ mRunnable(runnable),
+ mSchedule(schedule),
+ mNextRunAt(next_run_after),
+ mIncrement(increment)
+{
+}
+
+LLRunnable::LLRunnable()
+{ }
+
+// virtual
+LLRunnable::~LLRunnable()
+{ }
diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h
new file mode 100644
index 0000000000..8aa747ebab
--- /dev/null
+++ b/indra/llcommon/llrun.h
@@ -0,0 +1,144 @@
+/**
+ * @file llrun.h
+ * @author Phoenix
+ * @date 2006-02-16
+ * @brief Declaration of LLRunner and LLRunnable classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLRUN_H
+#define LL_LLRUN_H
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+class LLRunnable;
+
+/**
+ * @class LLRunner
+ * @brief This class manages a set of LLRunnable objects.
+ *
+ * An instance of this class has a collection of LLRunnable objects
+ * which are scheduled to run on a repeating or one time basis.
+ * @see LLRunnable
+ */
+class LLRunner
+{
+public:
+ /**
+ * @brief The pointer to a runnable.
+ */
+ typedef boost::shared_ptr<LLRunnable> run_ptr_t;
+
+ /**
+ * @brief The handle for use in the API.
+ */
+ typedef S64 run_handle_t;
+
+ /**
+ * @brief Constructor.
+ */
+ LLRunner();
+
+ /**
+ * @brief Destructor.
+ */
+ ~LLRunner();
+
+ /**
+ * @brief Enumeration which specifies when to run.
+ */
+ enum ERunSchedule
+ {
+ // The runnable will run in N seconds
+ RUN_IN,
+
+ // The run every N seconds
+ RUN_EVERY,
+
+ // A count of the run types
+ RUN_SCHEDULE_COUNT
+ };
+
+ /**
+ * @brief Run the runnables which are scheduled to run
+ *
+ * @return Returns the number of runnables run.
+ */
+ S32 run();
+
+ /**
+ * @brief Add a runnable to the run list.
+ *
+ * The handle of the runnable is unique to each addition. If the
+ * same runnable is added a second time with the same or different
+ * schedule, this method will return a new handle.
+ * @param runnable The runnable to run() on schedule.
+ * @param schedule Specifies the run schedule.
+ * @param seconds When to run the runnable as interpreted by schedule.
+ * @return Returns the handle to the runnable. handle == 0 means failure.
+ */
+ run_handle_t addRunnable(
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 seconds);
+
+ /**
+ * @brief Remove the specified runnable.
+ *
+ * @param handle The handle of the runnable to remove.
+ * @return Returns the pointer to the runnable removed which may
+ * be empty.
+ */
+ run_ptr_t removeRunnable(run_handle_t handle);
+
+protected:
+ struct LLRunInfo
+ {
+ run_handle_t mHandle;
+ run_ptr_t mRunnable;
+ ERunSchedule mSchedule;
+ F64 mNextRunAt;
+ F64 mIncrement;
+ LLRunInfo(
+ run_handle_t handle,
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 next_run_at,
+ F64 increment);
+ };
+ typedef std::vector<LLRunInfo> run_list_t;
+ run_list_t mRunOnce;
+ run_list_t mRunEvery;
+ run_handle_t mNextHandle;
+};
+
+
+/**
+ * @class LLRunnable
+ * @brief Abstract base class for running some scheduled process.
+ *
+ * Users of the LLRunner class are expected to derive a concrete
+ * implementation of this class which overrides the run() method to do
+ * something useful.
+ * @see LLRunner
+ */
+class LLRunnable
+{
+public:
+ LLRunnable();
+ virtual ~LLRunnable();
+
+ /**
+ * @brief Do the process.
+ *
+ * This method will be called from the LLRunner according to
+ * @param runner The Runner which call run().
+ * @param handle The handle this run instance is run under.
+ */
+ virtual void run(LLRunner* runner, S64 handle) = 0;
+};
+
+#endif // LL_LLRUN_H
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
new file mode 100644
index 0000000000..25bd7ceac8
--- /dev/null
+++ b/indra/llcommon/llsd.cpp
@@ -0,0 +1,731 @@
+/**
+ * @file llsd.cpp
+ * @brief LLSD flexible data system
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llsd.h"
+
+#include <math.h>
+#include "../llmath/llmath.h"
+
+namespace {
+ class ImplMap;
+ class ImplArray;
+}
+
+class LLSD::Impl
+ /**< This class is the abstract base class of the implementation of LLSD
+ It provides the reference counting implementation, and the default
+ implementation of most methods for most data types. It also serves
+ as a working implementation of the Undefined type.
+
+ */
+{
+private:
+ U32 mUseCount;
+
+protected:
+ Impl();
+
+ enum StaticAllocationMarker { STATIC };
+ Impl(StaticAllocationMarker);
+ ///< This constructor is used for static objects and causes the
+ // suppresses adjusting the debugging counters when they are
+ // finally initialized.
+
+ virtual ~Impl();
+
+ bool shared() const { return mUseCount > 1; }
+
+public:
+ static void reset(Impl*& var, Impl* impl);
+ ///< safely set var to refer to the new impl (possibly shared)
+
+ static Impl& safe( Impl*);
+ static const Impl& safe(const Impl*);
+ ///< since a NULL Impl* is used for undefined, this ensures there is
+ // always an object you call virtual member functions on
+
+ virtual ImplMap& makeMap(Impl*& var);
+ virtual ImplArray& makeArray(Impl*& var);
+ ///< sure var is a modifiable, non-shared map or array
+
+ virtual LLSD::Type type() const { return LLSD::TypeUndefined; }
+
+ static void assignUndefined(LLSD::Impl*& var);
+ static void assign(LLSD::Impl*& var, const LLSD::Impl* other);
+
+ virtual void assign(Impl*& var, LLSD::Boolean);
+ virtual void assign(Impl*& var, LLSD::Integer);
+ virtual void assign(Impl*& var, LLSD::Real);
+ virtual void assign(Impl*& var, const LLSD::String&);
+ virtual void assign(Impl*& var, const LLSD::UUID&);
+ virtual void assign(Impl*& var, const LLSD::Date&);
+ virtual void assign(Impl*& var, const LLSD::URI&);
+ virtual void assign(Impl*& var, const LLSD::Binary&);
+ ///< If the receiver is the right type and unshared, these are simple
+ // data assignments, othewise the default implementation handless
+ // constructing the proper Impl subclass
+
+ virtual Boolean asBoolean() const { return false; }
+ virtual Integer asInteger() const { return 0; }
+ virtual Real asReal() const { return 0.0; }
+ virtual String asString() const { return std::string(); }
+ virtual UUID asUUID() const { return LLUUID(); }
+ virtual Date asDate() const { return LLDate(); }
+ virtual URI asURI() const { return LLURI(); }
+ virtual Binary asBinary() const { return std::vector<U8>(); }
+
+ virtual bool has(const String&) const { return false; }
+ virtual LLSD get(const String&) const { return LLSD(); }
+ virtual void erase(const String&) { }
+ virtual const LLSD& ref(const String&) const{ return undef(); }
+
+ virtual int size() const { return 0; }
+ virtual LLSD get(Integer) const { return LLSD(); }
+ virtual void erase(Integer) { }
+ virtual const LLSD& ref(Integer) const { return undef(); }
+
+ virtual LLSD::map_const_iterator beginMap() const { return LLSD::map_const_iterator(); }
+ virtual LLSD::map_const_iterator endMap() const { return LLSD::map_const_iterator(); }
+ virtual LLSD::array_const_iterator beginArray() const { return LLSD::array_const_iterator(); }
+ virtual LLSD::array_const_iterator endArray() const { return LLSD::array_const_iterator(); }
+
+ static const LLSD& undef();
+
+ static U32 sAllocationCount;
+ static U32 sOutstandingCount;
+};
+
+namespace {
+ template<LLSD::Type T, class Data, class DataRef = Data>
+ class ImplBase : public LLSD::Impl
+ ///< This class handles most of the work for a subclass of Impl
+ // for a given simple data type. Subclasses of this provide the
+ // conversion functions and a constructor.
+ {
+ protected:
+ Data mValue;
+
+ typedef ImplBase Base;
+
+ public:
+ ImplBase(DataRef value) : mValue(value) { }
+
+ virtual LLSD::Type type() const { return T; }
+
+ virtual void assign(LLSD::Impl*& var, DataRef value) {
+ if (shared())
+ {
+ Impl::assign(var, value);
+ }
+ else
+ {
+ mValue = value;
+ }
+ }
+ };
+
+
+ class ImplBoolean
+ : public ImplBase<LLSD::TypeBoolean, LLSD::Boolean>
+ {
+ public:
+ ImplBoolean(LLSD::Boolean v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return mValue; }
+ virtual LLSD::Integer asInteger() const { return mValue ? 1 : 0; }
+ virtual LLSD::Real asReal() const { return mValue ? 1 : 0; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::String ImplBoolean::asString() const
+ { return mValue ? "true" : ""; }
+
+
+ class ImplInteger
+ : public ImplBase<LLSD::TypeInteger, LLSD::Integer>
+ {
+ public:
+ ImplInteger(LLSD::Integer v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return mValue != 0; }
+ virtual LLSD::Integer asInteger() const { return mValue; }
+ virtual LLSD::Real asReal() const { return mValue; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::String ImplInteger::asString() const
+ { return llformat("%d", mValue); }
+
+
+ class ImplReal
+ : public ImplBase<LLSD::TypeReal, LLSD::Real>
+ {
+ public:
+ ImplReal(LLSD::Real v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const;
+ virtual LLSD::Integer asInteger() const;
+ virtual LLSD::Real asReal() const { return mValue; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::Boolean ImplReal::asBoolean() const
+ { return !llisnan(mValue) && mValue != 0.0; }
+
+ LLSD::Integer ImplReal::asInteger() const
+ { return !llisnan(mValue) ? (LLSD::Integer)mValue : 0; }
+
+ LLSD::String ImplReal::asString() const
+ { return llformat("%lg", mValue); }
+
+
+ class ImplString
+ : public ImplBase<LLSD::TypeString, LLSD::String, const LLSD::String&>
+ {
+ public:
+ ImplString(const LLSD::String& v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return !mValue.empty(); }
+ virtual LLSD::Integer asInteger() const;
+ virtual LLSD::Real asReal() const;
+ virtual LLSD::String asString() const { return mValue; }
+ virtual LLSD::UUID asUUID() const { return LLUUID(mValue); }
+ virtual LLSD::Date asDate() const { return LLDate(mValue); }
+ virtual LLSD::URI asURI() const { return LLURI(mValue); }
+ };
+
+ LLSD::Integer ImplString::asInteger() const
+ {
+ // This must treat "1.23" not as an error, but as a number, which is
+ // then truncated down to an integer. Hence, this code doesn't call
+ // std::istringstream::operator>>(int&), which would not consume the
+ // ".23" portion.
+
+ return (int)asReal();
+ }
+
+ LLSD::Real ImplString::asReal() const
+ {
+ F64 v = 0.0;
+ std::istringstream i_stream(mValue);
+ i_stream >> v;
+
+ // we would probably like to ignore all trailing whitespace as
+ // well, but for now, simply eat the next character, and make
+ // sure we reached the end of the string.
+ // *NOTE: gcc 2.95 does not generate an eof() event on the
+ // stream operation above, so we manually get here to force it
+ // across platforms.
+ int c = i_stream.get();
+ return ((EOF ==c) ? v : 0.0);
+ }
+
+
+ class ImplUUID
+ : public ImplBase<LLSD::TypeUUID, LLSD::UUID, const LLSD::UUID&>
+ {
+ public:
+ ImplUUID(const LLSD::UUID& v) : Base(v) { }
+
+ virtual LLSD::String asString() const{ return mValue.getString(); }
+ virtual LLSD::UUID asUUID() const { return mValue; }
+ };
+
+
+ class ImplDate
+ : public ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>
+ {
+ public:
+ ImplDate(const LLSD::Date& v)
+ : ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>(v)
+ { }
+
+ virtual LLSD::Integer asInteger() const
+ {
+ return (LLSD::Integer)(mValue.secondsSinceEpoch());
+ }
+ virtual LLSD::Real asReal() const
+ {
+ return mValue.secondsSinceEpoch();
+ }
+ virtual LLSD::String asString() const{ return mValue.asString(); }
+ virtual LLSD::Date asDate() const { return mValue; }
+ };
+
+
+ class ImplURI
+ : public ImplBase<LLSD::TypeURI, LLSD::URI, const LLSD::URI&>
+ {
+ public:
+ ImplURI(const LLSD::URI& v) : Base(v) { }
+
+ virtual LLSD::String asString() const{ return mValue.asString(); }
+ virtual LLSD::URI asURI() const { return mValue; }
+ };
+
+
+ class ImplBinary
+ : public ImplBase<LLSD::TypeBinary, LLSD::Binary, const LLSD::Binary&>
+ {
+ public:
+ ImplBinary(const LLSD::Binary& v) : Base(v) { }
+
+ virtual LLSD::Binary asBinary() const{ return mValue; }
+ };
+
+
+ class ImplMap : public LLSD::Impl
+ {
+ private:
+ typedef std::map<LLSD::String, LLSD> DataMap;
+
+ DataMap mData;
+
+ protected:
+ ImplMap(const DataMap& data) : mData(data) { }
+
+ public:
+ ImplMap() { }
+
+ virtual ImplMap& makeMap(LLSD::Impl*&);
+
+ virtual LLSD::Type type() const { return LLSD::TypeMap; }
+
+ virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+
+ virtual bool has(const LLSD::String&) const;
+ virtual LLSD get(const LLSD::String&) const;
+ void insert(const LLSD::String& k, const LLSD& v);
+ virtual void erase(const LLSD::String&);
+ LLSD& ref(const LLSD::String&);
+ virtual const LLSD& ref(const LLSD::String&) const;
+
+ virtual int size() const { return mData.size(); }
+
+ LLSD::map_iterator beginMap() { return mData.begin(); }
+ LLSD::map_iterator endMap() { return mData.end(); }
+ virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); }
+ virtual LLSD::map_const_iterator endMap() const { return mData.end(); }
+ };
+
+ ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
+ {
+ if (shared())
+ {
+ ImplMap* i = new ImplMap(mData);
+ Impl::assign(var, i);
+ return *i;
+ }
+ else
+ {
+ return *this;
+ }
+ }
+
+ bool ImplMap::has(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.find(k);
+ return i != mData.end();
+ }
+
+ LLSD ImplMap::get(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.find(k);
+ return (i != mData.end()) ? i->second : LLSD();
+ }
+
+ void ImplMap::insert(const LLSD::String& k, const LLSD& v)
+ {
+ mData.insert(DataMap::value_type(k, v));
+ }
+
+ void ImplMap::erase(const LLSD::String& k)
+ {
+ mData.erase(k);
+ }
+
+ LLSD& ImplMap::ref(const LLSD::String& k)
+ {
+ return mData[k];
+ }
+
+ const LLSD& ImplMap::ref(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.lower_bound(k);
+ if (i == mData.end() || mData.key_comp()(k, i->first))
+ {
+ return undef();
+ }
+
+ return i->second;
+ }
+
+ class ImplArray : public LLSD::Impl
+ {
+ private:
+ typedef std::vector<LLSD> DataVector;
+
+ DataVector mData;
+
+ protected:
+ ImplArray(const DataVector& data) : mData(data) { }
+
+ public:
+ ImplArray() { }
+
+ virtual ImplArray& makeArray(Impl*&);
+
+ virtual LLSD::Type type() const { return LLSD::TypeArray; }
+
+ virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+
+ virtual int size() const;
+ virtual LLSD get(LLSD::Integer) const;
+ void set(LLSD::Integer, const LLSD&);
+ void insert(LLSD::Integer, const LLSD&);
+ void append(const LLSD&);
+ virtual void erase(LLSD::Integer);
+ LLSD& ref(LLSD::Integer);
+ virtual const LLSD& ref(LLSD::Integer) const;
+
+ LLSD::array_iterator beginArray() { return mData.begin(); }
+ LLSD::array_iterator endArray() { return mData.end(); }
+ virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); }
+ virtual LLSD::array_const_iterator endArray() const { return mData.end(); }
+ };
+
+ ImplArray& ImplArray::makeArray(Impl*& var)
+ {
+ if (shared())
+ {
+ ImplArray* i = new ImplArray(mData);
+ Impl::assign(var, i);
+ return *i;
+ }
+ else
+ {
+ return *this;
+ }
+ }
+
+ int ImplArray::size() const { return mData.size(); }
+
+ LLSD ImplArray::get(LLSD::Integer i) const
+ {
+ if (i < 0) { return LLSD(); }
+ DataVector::size_type index = i;
+
+ return (index < mData.size()) ? mData[index] : LLSD();
+ }
+
+ void ImplArray::set(LLSD::Integer i, const LLSD& v)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ mData.resize(index + 1);
+ }
+
+ mData[index] = v;
+ }
+
+ void ImplArray::insert(LLSD::Integer i, const LLSD& v)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ mData.resize(index + 1);
+ }
+
+ mData.insert(mData.begin() + index, v);
+ }
+
+ void ImplArray::append(const LLSD& v)
+ {
+ mData.push_back(v);
+ }
+
+ void ImplArray::erase(LLSD::Integer i)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index < mData.size())
+ {
+ mData.erase(mData.begin() + index);
+ }
+ }
+
+ LLSD& ImplArray::ref(LLSD::Integer i)
+ {
+ DataVector::size_type index = i >= 0 ? i : 0;
+
+ if (index >= mData.size())
+ {
+ mData.resize(i + 1);
+ }
+
+ return mData[index];
+ }
+
+ const LLSD& ImplArray::ref(LLSD::Integer i) const
+ {
+ if (i < 0) { return undef(); }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ return undef();
+ }
+
+ return mData[index];
+ }
+}
+
+LLSD::Impl::Impl()
+ : mUseCount(0)
+{
+ ++sAllocationCount;
+ ++sOutstandingCount;
+}
+
+LLSD::Impl::Impl(StaticAllocationMarker)
+ : mUseCount(0)
+{
+}
+
+LLSD::Impl::~Impl()
+{
+ --sOutstandingCount;
+}
+
+void LLSD::Impl::reset(Impl*& var, Impl* impl)
+{
+ if (impl) ++impl->mUseCount;
+ if (var && --var->mUseCount == 0)
+ {
+ delete var;
+ }
+ var = impl;
+}
+
+LLSD::Impl& LLSD::Impl::safe(Impl* impl)
+{
+ static Impl theUndefined(STATIC);
+ return impl ? *impl : theUndefined;
+}
+
+const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
+{
+ static Impl theUndefined(STATIC);
+ return impl ? *impl : theUndefined;
+}
+
+ImplMap& LLSD::Impl::makeMap(Impl*& var)
+{
+ ImplMap* im = new ImplMap;
+ reset(var, im);
+ return *im;
+}
+
+ImplArray& LLSD::Impl::makeArray(Impl*& var)
+{
+ ImplArray* ia = new ImplArray;
+ reset(var, ia);
+ return *ia;
+}
+
+
+void LLSD::Impl::assign(Impl*& var, const Impl* other)
+{
+ reset(var, const_cast<Impl*>(other));
+}
+
+void LLSD::Impl::assignUndefined(Impl*& var)
+{
+ reset(var, 0);
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Boolean v)
+{
+ reset(var, new ImplBoolean(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Integer v)
+{
+ reset(var, new ImplInteger(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Real v)
+{
+ reset(var, new ImplReal(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::String& v)
+{
+ reset(var, new ImplString(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::UUID& v)
+{
+ reset(var, new ImplUUID(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::Date& v)
+{
+ reset(var, new ImplDate(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::URI& v)
+{
+ reset(var, new ImplURI(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::Binary& v)
+{
+ reset(var, new ImplBinary(v));
+}
+
+
+const LLSD& LLSD::Impl::undef()
+{
+ static const LLSD immutableUndefined;
+ return immutableUndefined;
+}
+
+U32 LLSD::Impl::sAllocationCount = 0;
+U32 LLSD::Impl::sOutstandingCount = 0;
+
+
+
+namespace {
+ inline LLSD::Impl& safe(LLSD::Impl* impl)
+ { return LLSD::Impl::safe(impl); }
+
+ inline const LLSD::Impl& safe(const LLSD::Impl* impl)
+ { return LLSD::Impl::safe(impl); }
+
+ inline ImplMap& makeMap(LLSD::Impl*& var)
+ { return safe(var).makeMap(var); }
+
+ inline ImplArray& makeArray(LLSD::Impl*& var)
+ { return safe(var).makeArray(var); }
+}
+
+
+LLSD::LLSD() : impl(0) { }
+LLSD::~LLSD() { Impl::reset(impl, 0); }
+
+LLSD::LLSD(const LLSD& other) : impl(0) { assign(other); }
+void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); }
+
+
+void LLSD::clear() { Impl::assignUndefined(impl); }
+
+LLSD::Type LLSD::type() const { return safe(impl).type(); }
+
+// Scaler Constructors
+LLSD::LLSD(Boolean v) : impl(0) { assign(v); }
+LLSD::LLSD(Integer v) : impl(0) { assign(v); }
+LLSD::LLSD(Real v) : impl(0) { assign(v); }
+LLSD::LLSD(const UUID& v) : impl(0) { assign(v); }
+LLSD::LLSD(const String& v) : impl(0) { assign(v); }
+LLSD::LLSD(const Date& v) : impl(0) { assign(v); }
+LLSD::LLSD(const URI& v) : impl(0) { assign(v); }
+LLSD::LLSD(const Binary& v) : impl(0) { assign(v); }
+
+// Convenience Constructors
+LLSD::LLSD(F32 v) : impl(0) { assign((Real)v); }
+
+// Scalar Assignment
+void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); }
+void LLSD::assign(Integer v) { safe(impl).assign(impl, v); }
+void LLSD::assign(Real v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const String& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const UUID& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const Date& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const URI& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const Binary& v) { safe(impl).assign(impl, v); }
+
+// Scalar Accessors
+LLSD::Boolean LLSD::asBoolean() const { return safe(impl).asBoolean(); }
+LLSD::Integer LLSD::asInteger() const { return safe(impl).asInteger(); }
+LLSD::Real LLSD::asReal() const { return safe(impl).asReal(); }
+LLSD::String LLSD::asString() const { return safe(impl).asString(); }
+LLSD::UUID LLSD::asUUID() const { return safe(impl).asUUID(); }
+LLSD::Date LLSD::asDate() const { return safe(impl).asDate(); }
+LLSD::URI LLSD::asURI() const { return safe(impl).asURI(); }
+LLSD::Binary LLSD::asBinary() const { return safe(impl).asBinary(); }
+
+// const char * helpers
+LLSD::LLSD(const char* v) : impl(0) { assign(v); }
+void LLSD::assign(const char* v)
+{
+ if(v) assign(std::string(v));
+ else assign(std::string());
+}
+
+
+LLSD LLSD::emptyMap()
+{
+ LLSD v;
+ makeMap(v.impl);
+ return v;
+}
+
+bool LLSD::has(const String& k) const { return safe(impl).has(k); }
+LLSD LLSD::get(const String& k) const { return safe(impl).get(k); }
+
+void LLSD::insert(const String& k, const LLSD& v)
+ { makeMap(impl).insert(k, v); }
+void LLSD::erase(const String& k) { makeMap(impl).erase(k); }
+
+LLSD& LLSD::operator[](const String& k)
+ { return makeMap(impl).ref(k); }
+const LLSD& LLSD::operator[](const String& k) const
+ { return safe(impl).ref(k); }
+
+
+LLSD LLSD::emptyArray()
+{
+ LLSD v;
+ makeArray(v.impl);
+ return v;
+}
+
+int LLSD::size() const { return safe(impl).size(); }
+
+LLSD LLSD::get(Integer i) const { return safe(impl).get(i); }
+void LLSD::set(Integer i, const LLSD& v){ makeArray(impl).set(i, v); }
+
+void LLSD::insert(Integer i, const LLSD& v)
+ { makeArray(impl).insert(i, v); }
+void LLSD::append(const LLSD& v) { makeArray(impl).append(v); }
+void LLSD::erase(Integer i) { makeArray(impl).erase(i); }
+
+LLSD& LLSD::operator[](Integer i)
+ { return makeArray(impl).ref(i); }
+const LLSD& LLSD::operator[](Integer i) const
+ { return safe(impl).ref(i); }
+
+U32 LLSD::allocationCount() { return Impl::sAllocationCount; }
+U32 LLSD::outstandingCount() { return Impl::sOutstandingCount; }
+
+LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); }
+LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); }
+LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); }
+LLSD::map_const_iterator LLSD::endMap() const { return safe(impl).endMap(); }
+
+LLSD::array_iterator LLSD::beginArray() { return makeArray(impl).beginArray(); }
+LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); }
+LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); }
+LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); }
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
new file mode 100644
index 0000000000..97bd6fe01c
--- /dev/null
+++ b/indra/llcommon/llsd.h
@@ -0,0 +1,366 @@
+/**
+ * @file llsd.h
+ * @brief LLSD flexible data system.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSD_NEW_H
+#define LL_LLSD_NEW_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "stdtypes.h"
+
+#include "lldate.h"
+#include "lluri.h"
+#include "../llmath/lluuid.h"
+
+/**
+ LLSD provides a flexible data system similar to the data facilities of
+ dynamic languages like Perl and Python. It is created to support exchange
+ of structured data between loosly coupled systems. (Here, "loosly coupled"
+ means not compiled together into the same module.)
+
+ Data in such exchanges must be highly tollerant of changes on either side
+ such as:
+ - recompilation
+ - implementation in a different langauge
+ - addition of extra parameters
+ - execution of older versions (with fewer parameters)
+
+ To this aim, the C++ API of LLSD strives to be very easy to use, and to
+ default to "the right thing" whereever possible. It is extremely tollerant
+ of errors and unexpected situations.
+
+ The fundimental class is LLSD. LLSD is a value holding object. It holds
+ one value that is either undefined, one of the scalar types, or a map or an
+ array. LLSD objects have value semantics (copying them copies the value,
+ though it can be considered efficient, due to shareing.), and mutable.
+
+ Undefined is the singular value given to LLSD objects that are not
+ initialized with any data. It is also used as the return value for
+ operations that return an LLSD,
+
+ The sclar data types are:
+ - Boolean - true or false
+ - Integer - a 32 bit signed integer
+ - Real - a 64 IEEE 754 floating point value
+ - UUID - a 128 unique value
+ - String - a sequence of zero or more Unicode chracters
+ - Date - an absolute point in time, UTC,
+ with resolution to the second
+ - URI - a String that is a URI
+ - Binary - a sequence of zero or more octets (unsigned bytes)
+
+ A map is a dictionary mapping String keys to LLSD values. The keys are
+ unique within a map, and have only one value (though that value could be
+ an LLSD array).
+
+ An array is a sequence of zero or more LLSD values.
+
+ @nosubgrouping
+*/
+
+class LLSD
+{
+public:
+ LLSD(); ///< initially Undefined
+ ~LLSD(); ///< this class may NOT be subclassed
+
+ /** @name Copyable and Assignable */
+ //@{
+ LLSD(const LLSD&);
+ void assign(const LLSD& other);
+ LLSD& operator=(const LLSD& other) { assign(other); return *this; }
+
+ //@}
+
+ void clear(); ///< resets to Undefined
+
+
+ /** @name Scalar Types
+ The scalar types, and how they map onto C++
+ */
+ //@{
+ typedef bool Boolean;
+ typedef S32 Integer;
+ typedef F64 Real;
+ typedef std::string String;
+ typedef LLUUID UUID;
+ typedef LLDate Date;
+ typedef LLURI URI;
+ typedef std::vector<U8> Binary;
+ //@}
+
+ /** @name Scalar Constructors */
+ //@{
+ LLSD(Boolean);
+ LLSD(Integer);
+ LLSD(Real);
+ LLSD(const String&);
+ LLSD(const UUID&);
+ LLSD(const Date&);
+ LLSD(const URI&);
+ LLSD(const Binary&);
+ //@}
+
+ /** @name Convenience Constructors */
+ //@{
+ LLSD(F32); // F32 -> Real
+ //@}
+
+ /** @name Scalar Assignment */
+ //@{
+ void assign(Boolean);
+ void assign(Integer);
+ void assign(Real);
+ void assign(const String&);
+ void assign(const UUID&);
+ void assign(const Date&);
+ void assign(const URI&);
+ void assign(const Binary&);
+
+ LLSD& operator=(Boolean v) { assign(v); return *this; }
+ LLSD& operator=(Integer v) { assign(v); return *this; }
+ LLSD& operator=(Real v) { assign(v); return *this; }
+ LLSD& operator=(const String& v) { assign(v); return *this; }
+ LLSD& operator=(const UUID& v) { assign(v); return *this; }
+ LLSD& operator=(const Date& v) { assign(v); return *this; }
+ LLSD& operator=(const URI& v) { assign(v); return *this; }
+ LLSD& operator=(const Binary& v) { assign(v); return *this; }
+ //@}
+
+ /**
+ @name Scalar Accessors
+ @brief Fetch a scalar value, converting if needed and possible
+
+ Conversion among the basic types, Boolean, Integer, Real and String, is
+ fully defined. Each type can be converted to another with a reasonable
+ interpretation. These conversions can be used as a convenience even
+ when you know the data is in one format, but you want it in another. Of
+ course, many of these conversions lose information.
+
+ Note: These conversions are not the same as Perl's. In particular, when
+ converting a String to a Boolean, only the empty string converts to
+ false. Converting the String "0" to Boolean results in true.
+
+ Conversion to and from UUID, Date, and URI is only defined to and from
+ String. Conversion is defined to be information preserving for valid
+ values of those types. These conversions can be used when one needs to
+ convert data to or from another system that cannot handle these types
+ natively, but can handle strings.
+
+ Conversion to and from Binary isn't defined.
+
+ Conversion of the Undefined value to any scalar type results in a
+ reasonable null or zero value for the type.
+ */
+ //@{
+ Boolean asBoolean() const;
+ Integer asInteger() const;
+ Real asReal() const;
+ String asString() const;
+ UUID asUUID() const;
+ Date asDate() const;
+ URI asURI() const;
+ Binary asBinary() const;
+
+ operator Boolean() const { return asBoolean(); }
+ operator Integer() const { return asInteger(); }
+ operator Real() const { return asReal(); }
+ operator String() const { return asString(); }
+ operator UUID() const { return asUUID(); }
+ operator Date() const { return asDate(); }
+ operator URI() const { return asURI(); }
+ operator Binary() const { return asBinary(); }
+
+ // This is needed because most platforms do not automatically
+ // convert the boolean negation as a bool in an if statement.
+ bool operator!() const {return !asBoolean();}
+ //@}
+
+ /** @name Character Pointer Helpers
+ These are helper routines to make working with char* the same as easy as
+ working with strings.
+ */
+ //@{
+ LLSD(const char*);
+ void assign(const char*);
+ LLSD& operator=(const char* v) { assign(v); return *this; }
+ //@}
+
+ /** @name Map Values */
+ //@{
+ static LLSD emptyMap();
+
+ bool has(const String&) const;
+ LLSD get(const String&) const;
+ void insert(const String&, const LLSD&);
+ void erase(const String&);
+
+ LLSD& operator[](const String&);
+ LLSD& operator[](const char* c) { return (*this)[String(c)]; }
+ const LLSD& operator[](const String&) const;
+ const LLSD& operator[](const char* c) const { return (*this)[String(c)]; }
+ //@}
+
+ /** @name Array Values */
+ //@{
+ static LLSD emptyArray();
+
+ LLSD get(Integer) const;
+ void set(Integer, const LLSD&);
+ void insert(Integer, const LLSD&);
+ void append(const LLSD&);
+ void erase(Integer);
+
+ const LLSD& operator[](Integer) const;
+ LLSD& operator[](Integer);
+ //@}
+
+ /** @name Iterators */
+ //@{
+ int size() const;
+
+ typedef std::map<String, LLSD>::iterator map_iterator;
+ typedef std::map<String, LLSD>::const_iterator map_const_iterator;
+
+ map_iterator beginMap();
+ map_iterator endMap();
+ map_const_iterator beginMap() const;
+ map_const_iterator endMap() const;
+
+ typedef std::vector<LLSD>::iterator array_iterator;
+ typedef std::vector<LLSD>::const_iterator array_const_iterator;
+
+ array_iterator beginArray();
+ array_iterator endArray();
+ array_const_iterator beginArray() const;
+ array_const_iterator endArray() const;
+ //@}
+
+ /** @name Type Testing */
+ //@{
+ enum Type {
+ TypeUndefined,
+ TypeBoolean,
+ TypeInteger,
+ TypeReal,
+ TypeString,
+ TypeUUID,
+ TypeDate,
+ TypeURI,
+ TypeBinary,
+ TypeMap,
+ TypeArray
+ };
+
+ Type type() const;
+
+ bool isUndefined() const { return type() == TypeUndefined; }
+ bool isDefined() const { return type() != TypeUndefined; }
+ bool isBoolean() const { return type() == TypeBoolean; }
+ bool isInteger() const { return type() == TypeInteger; }
+ bool isReal() const { return type() == TypeReal; }
+ bool isString() const { return type() == TypeString; }
+ bool isUUID() const { return type() == TypeUUID; }
+ bool isDate() const { return type() == TypeDate; }
+ bool isURI() const { return type() == TypeURI; }
+ bool isBinary() const { return type() == TypeBinary; }
+ bool isMap() const { return type() == TypeMap; }
+ bool isArray() const { return type() == TypeArray; }
+ //@}
+
+ /** @name Automatic Cast Protection
+ These are not implemented on purpose. Without them, C++ can perform
+ some conversions that are clearly not what the programmer intended.
+
+ If you get a linker error about these being missing, you have made
+ mistake in your code. DO NOT IMPLEMENT THESE FUNCTIONS as a fix.
+
+ All of thse problems stem from trying to support char* in LLSD or in
+ std::string. There are too many automatic casts that will lead to
+ using an arbitrary pointer or scalar type to std::string.
+ */
+ //@{
+ LLSD(const void*); ///< construct from aribrary pointers
+ void assign(const void*); ///< assign from arbitrary pointers
+ LLSD& operator=(const void*); ///< assign from arbitrary pointers
+
+ bool has(Integer) const; ///< has only works for Maps
+ //@}
+
+ /** @name Implementation */
+ //@{
+public:
+ class Impl;
+private:
+ Impl* impl;
+ //@}
+
+ /** @name Unit Testing Interface */
+ //@{
+public:
+ static U32 allocationCount(); ///< how many Impls have been made
+ static U32 outstandingCount(); ///< how many Impls are still alive
+ //@}
+};
+
+struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>
+{
+ LLSD::Boolean operator()(const LLSD& sd) const
+ {
+ return sd.asBoolean();
+ }
+};
+struct llsd_select_integer : public std::unary_function<LLSD, LLSD::Integer>
+{
+ LLSD::Integer operator()(const LLSD& sd) const
+ {
+ return sd.asInteger();
+ }
+};
+struct llsd_select_real : public std::unary_function<LLSD, LLSD::Real>
+{
+ LLSD::Real operator()(const LLSD& sd) const
+ {
+ return sd.asReal();
+ }
+};
+struct llsd_select_float : public std::unary_function<LLSD, F32>
+{
+ F32 operator()(const LLSD& sd) const
+ {
+ return (F32)sd.asReal();
+ }
+};
+struct llsd_select_uuid : public std::unary_function<LLSD, LLSD::UUID>
+{
+ LLSD::UUID operator()(const LLSD& sd) const
+ {
+ return sd.asUUID();
+ }
+};
+struct llsd_select_string : public std::unary_function<LLSD, LLSD::String>
+{
+ LLSD::String operator()(const LLSD& sd) const
+ {
+ return sd.asString();
+ }
+};
+
+
+/** QUESTIONS & TO DOS
+ - Would Binary be more convenient as usigned char* buffer semantics?
+ - Should Binary be convertable to/from String, and if so how?
+ - as UTF8 encoded strings (making not like UUID<->String)
+ - as Base64 or Base96 encoded (making like UUID<->String)
+ - Conversions to std::string and LLUUID do not result in easy assignment
+ to std::string, LLString or LLUUID due to non-unique conversion paths
+*/
+
+#endif // LL_LLSD_NEW_H
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
new file mode 100644
index 0000000000..fb8efc91ca
--- /dev/null
+++ b/indra/llcommon/llsdserialize.cpp
@@ -0,0 +1,1621 @@
+/**
+ * @file llsdserialize.cpp
+ * @author Phoenix
+ * @date 2006-03-05
+ * @brief Implementation of LLSD parsers and formatters
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize.h"
+#include "llmemory.h"
+#include "llstreamtools.h" // for fullread
+
+#include <iostream>
+#include "apr-1/apr_base64.h"
+
+#if !LL_WINDOWS
+#include <netinet/in.h> // htonl & ntohl
+#endif
+
+#include "lldate.h"
+#include "llsd.h"
+#include "lluri.h"
+
+// File constants
+static const int MAX_HDR_LEN = 20;
+static const char LEGACY_NON_HEADER[] = "<llsd>";
+
+//static
+const char* LLSDSerialize::LLSDBinaryHeader = "LLSD/Binary";
+
+//static
+const char* LLSDSerialize::LLSDXMLHeader = "LLSD/XML";
+
+// virtual
+LLSDParser::~LLSDParser()
+{ }
+
+// virtual
+LLSDNotationParser::~LLSDNotationParser()
+{ }
+
+
+// static
+void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options)
+{
+ LLPointer<LLSDFormatter> f = NULL;
+
+ switch (type)
+ {
+ case LLSD_BINARY:
+ str << "<? " << LLSDBinaryHeader << " ?>\n";
+ f = new LLSDBinaryFormatter;
+ break;
+
+ case LLSD_XML:
+ str << "<? " << LLSDXMLHeader << " ?>\n";
+ f = new LLSDXMLFormatter;
+ break;
+
+ default:
+ llwarns << "serialize request for unkown ELLSD_Serialize" << llendl;
+ }
+
+ if (f.notNull())
+ {
+ f->format(sd, str, options);
+ }
+}
+
+// static
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
+{
+ LLPointer<LLSDParser> p = NULL;
+ char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
+ int i;
+ int inbuf = 0;
+ bool legacy_no_header = false;
+ bool fail_if_not_legacy = false;
+ std::string header = "";
+
+ /*
+ * Get the first line before anything.
+ */
+ str.get(hdr_buf, MAX_HDR_LEN, '\n');
+ if (str.fail())
+ {
+ str.clear();
+ fail_if_not_legacy = true;
+ }
+
+ if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ {
+ legacy_no_header = true;
+ inbuf = str.gcount();
+ }
+ else
+ {
+ if (fail_if_not_legacy)
+ goto fail;
+ /*
+ * Remove the newline chars
+ */
+ for (i = 0; i < MAX_HDR_LEN; i++)
+ {
+ if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' ||
+ hdr_buf[i] == '\n')
+ {
+ hdr_buf[i] = 0;
+ break;
+ }
+ }
+ header = hdr_buf;
+
+ std::string::size_type start = std::string::npos;
+ std::string::size_type end = std::string::npos;
+ start = header.find_first_not_of("<? ");
+ if (start != std::string::npos)
+ {
+ end = header.find_first_of(" ?", start);
+ }
+ if ((start == std::string::npos) || (end == std::string::npos))
+ goto fail;
+
+ header = header.substr(start, end - start);
+ ws(str);
+ }
+ /*
+ * Create the parser as appropriate
+ */
+ if (legacy_no_header)
+ {
+ LLSDXMLParser *x = new LLSDXMLParser;
+ x->parsePart(hdr_buf, inbuf);
+ p = x;
+ }
+ else if (header == LLSDBinaryHeader)
+ {
+ p = new LLSDBinaryParser;
+ }
+ else if (header == LLSDXMLHeader)
+ {
+ p = new LLSDXMLParser;
+ }
+ else
+ {
+ llwarns << "deserialize request for unknown ELLSD_Serialize" << llendl;
+ }
+
+ if (p.notNull())
+ {
+ p->parse(str, sd);
+ return true;
+ }
+
+fail:
+ llwarns << "deserialize LLSD parse failure" << llendl;
+ return false;
+}
+
+/**
+ * Endian handlers
+ */
+#if LL_BIG_ENDIAN
+U64 ll_htonll(U64 hostlonglong) { return hostlonglong; }
+U64 ll_ntohll(U64 netlonglong) { return netlonglong; }
+F64 ll_htond(F64 hostlonglong) { return hostlonglong; }
+F64 ll_ntohd(F64 netlonglong) { return netlonglong; }
+#else
+// I read some comments one a indicating that doing an integer add
+// here would be faster than a bitwise or. For now, the or has
+// programmer clarity, since the intended outcome matches the
+// operation.
+U64 ll_htonll(U64 hostlonglong)
+{
+ return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32));
+}
+U64 ll_ntohll(U64 netlonglong)
+{
+ return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32));
+}
+union LLEndianSwapper
+{
+ F64 d;
+ U64 i;
+};
+F64 ll_htond(F64 hostdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = hostdouble;
+ tmp.i = ll_htonll(tmp.i);
+ return tmp.d;
+}
+F64 ll_ntohd(F64 netdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = netdouble;
+ tmp.i = ll_ntohll(tmp.i);
+ return tmp.d;
+}
+#endif
+
+/**
+ * Local functions.
+ */
+bool deserialize_string(std::istream& str, std::string& value);
+bool deserialize_string_delim(std::istream& str, std::string& value, char d);
+bool deserialize_string_raw(std::istream& str, std::string& value);
+void serialize_string(const std::string& value, std::ostream& str);
+
+/**
+ * Local constants.
+ */
+static const std::string NOTATION_TRUE_SERIAL("true");
+static const std::string NOTATION_FALSE_SERIAL("false");
+
+static const char BINARY_TRUE_SERIAL = '1';
+static const char BINARY_FALSE_SERIAL = '0';
+
+static const S32 NOTATION_PARSE_FAILURE = -1;
+
+/**
+ * LLSDParser
+ */
+LLSDParser::LLSDParser()
+{
+}
+
+/**
+ * LLSDNotationParser
+ */
+// virtual
+S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
+{
+ // map: { string:object, string:object }
+ // array: [ object, object, object ]
+ // undef: !
+ // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
+ // integer: i####
+ // real: r####
+ // uuid: u####
+ // string: "g'day" | 'have a "nice" day' | s(size)"raw data"
+ // uri: l"escaped"
+ // date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
+ // binary: b##"ff3120ab1" | b(size)"raw data"
+ char c;
+ c = istr.peek();
+ while(isspace(c))
+ {
+ // pop the whitespace.
+ c = istr.get();
+ c = istr.peek();
+ continue;
+ }
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading map." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading array." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '!':
+ c = istr.get();
+ data.clear();
+ break;
+
+ case '0':
+ c = istr.get();
+ data = false;
+ break;
+
+ case 'F':
+ case 'f':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = false;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case '1':
+ c = istr.get();
+ data = true;
+ break;
+
+ case 'T':
+ case 't':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = true;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case 'i':
+ {
+ c = istr.get();
+ S32 integer = 0;
+ istr >> integer;
+ data = integer;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ c = istr.get();
+ F64 real = 0.0;
+ istr >> real;
+ data = real;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ c = istr.get();
+ LLUUID id;
+ istr >> id;
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\"':
+ case '\'':
+ case 's':
+ parseString(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading string." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case 'l':
+ {
+ c = istr.get(); // pop the 'l'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLURI(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ c = istr.get(); // pop the 'd'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLDate(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ parseBinary(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading data." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ default:
+ data.clear();
+ parse_count = NOTATION_PARSE_FAILURE;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDNotationParser::parse(std::istream& istr)
+{
+ LLSDNotationParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDNotationParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ // map: { string:object, string:object }
+ map = LLSD::emptyMap();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '{')
+ {
+ // eat commas, white
+ bool found_name = false;
+ std::string name;
+ c = istr.get();
+ while(c != '}' && istr.good())
+ {
+ if(!found_name)
+ {
+ if((c == '\"') || (c == '\'') || (c == 's'))
+ {
+ istr.putback(c);
+ found_name = true;
+ deserialize_string(istr, name);
+ }
+ c = istr.get();
+ }
+ else
+ {
+ if(isspace(c) || (c == ':'))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ LLSD child;
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ map.insert(name, child);
+ }
+ else
+ {
+ map.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ found_name = false;
+ c = istr.get();
+ }
+ }
+ }
+ return parse_count;
+}
+
+S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ // array: [ object, object, object ]
+ array = LLSD::emptyArray();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '[')
+ {
+ // eat commas, white
+ c = istr.get();
+ while((c != ']') && istr.good())
+ {
+ LLSD child;
+ if(isspace(c) || (c == ','))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ array.append(child);
+ }
+ else
+ {
+ array.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ c = istr.get();
+ }
+ }
+ return parse_count;
+}
+
+void LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
+{
+ std::string value;
+ if(deserialize_string(istr, value))
+ {
+ data = value;
+ }
+ else
+ {
+ // failed to parse.
+ data.clear();
+ }
+}
+
+void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
+{
+ // binary: b##"ff3120ab1"
+ // or: b(len)"..."
+
+ // I want to manually control those values here to make sure the
+ // parser doesn't break when someone changes a constant somewhere
+ // else.
+ const U32 BINARY_BUFFER_SIZE = 256;
+ const U32 STREAM_GET_COUNT = 255;
+
+ // need to read the base out.
+ char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ char c = istr.get();
+ if((c == '"') && (0 == strncmp("b(", buf, 2)))
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ S32 len = strtol(buf + 2, NULL, 0);
+ std::vector<U8> value;
+ if(len)
+ {
+ value.resize(len);
+ fullread(istr, (char *)&value[0], len);
+ }
+ c = istr.get(); // strip off the trailing double-quote
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b64", buf, 3)))
+ {
+ // *FIX: A bit inefficient, but works for now. To make the
+ // format better, I would need to add a hint into the
+ // serialization format that indicated how long it was.
+ std::stringstream coded_stream;
+ istr.get(*(coded_stream.rdbuf()), '\"');
+ c = istr.get();
+ std::string encoded(coded_stream.str());
+ S32 len = apr_base64_decode_len(encoded.c_str());
+ std::vector<U8> value;
+ value.resize(len);
+ len = apr_base64_decode_binary(&value[0], encoded.c_str());
+ value.resize(len);
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b16", buf, 3)))
+ {
+ // yay, base 16. We pop the next character which is either a
+ // double quote or base 16 data. If it's a double quote, we're
+ // done parsing. If it's not, put the data back, and read the
+ // stream until the next double quote.
+ char* read; /*Flawfinder: ignore*/
+ U8 byte;
+ U8 byte_buffer[BINARY_BUFFER_SIZE];
+ U8* write;
+ std::vector<U8> value;
+ c = istr.get();
+ while(c != '"')
+ {
+ istr.putback(c);
+ read = buf;
+ write = byte_buffer;
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ c = istr.get();
+ while(*read != '\0') /*Flawfinder: ignore*/
+ {
+ byte = hex_as_nybble(*read++);
+ byte = byte << 4;
+ byte |= hex_as_nybble(*read++);
+ *write++ = byte;
+ }
+ // copy the data out of the byte buffer
+ value.insert(value.end(), byte_buffer, write);
+ }
+ data = value;
+ }
+ else
+ {
+ data.clear();
+ }
+}
+
+
+/**
+ * LLSDBinaryParser
+ */
+LLSDBinaryParser::LLSDBinaryParser()
+{
+}
+
+// virtual
+LLSDBinaryParser::~LLSDBinaryParser()
+{
+}
+
+// virtual
+S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
+{
+/**
+ * Undefined: '!'<br>
+ * Boolean: 't' for true 'f' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * strings also secretly support the notation format
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as s + 4 byte integer size + string or in the
+ * notation format.
+ */
+ char c;
+ c = istr.get();
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary map." << llendl;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary array." << llendl;
+ }
+ break;
+
+ case '!':
+ data.clear();
+ break;
+
+ case '0':
+ data = false;
+ break;
+
+ case '1':
+ data = true;
+ break;
+
+ case 'i':
+ {
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ data = (S32)ntohl(value_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ F64 real_nbo = 0.0;
+ istr.read((char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
+ data = ll_ntohd(real_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ LLUUID id;
+ istr.read((char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\'':
+ case '"':
+ {
+ std::string value;
+ deserialize_string_delim(istr, value, c);
+ data = value;
+ break;
+ }
+
+ case 's':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary string." << llendl;
+ }
+ break;
+ }
+
+ case 'l':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = LLURI(value);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ F64 real = 0.0;
+ istr.read((char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
+ data = LLDate(real);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ U32 size_nbo = 0;
+ istr.read((char*)&size_nbo, sizeof(U32));
+ S32 size = (S32)ntohl(size_nbo);
+ std::vector<U8> value;
+ if(size)
+ {
+ value.resize(size);
+ istr.read((char*)&value[0], size); /*Flawfinder: ignore*/
+ }
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary." << llendl;
+ }
+ break;
+ }
+
+ default:
+ --parse_count;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDBinaryParser::parse(std::istream& istr)
+{
+ LLSDBinaryParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDBinaryParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ map = LLSD::emptyMap();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.get();
+ while(c != '}' && (count < size) && istr.good())
+ {
+ std::string name;
+ switch(c)
+ {
+ case 'k':
+ parseString(istr, name);
+ break;
+ case '\'':
+ case '"':
+ deserialize_string_delim(istr, name, c);
+ break;
+ }
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ map.insert(name, child);
+ }
+ ++count;
+ c = istr.get();
+ }
+ return parse_count;
+}
+
+S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ array = LLSD::emptyArray();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+
+ // *FIX: This would be a good place to reserve some space in the
+ // array...
+
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.peek();
+ while((c != ']') && (count < size) && istr.good())
+ {
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ array.append(child);
+ }
+ ++count;
+ c = istr.peek();
+ }
+ c = istr.get();
+ return parse_count;
+}
+
+void LLSDBinaryParser::parseString(
+ std::istream& istr,
+ std::string& value) const
+{
+ // *FIX: This is memory inefficient.
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ std::vector<char> buf;
+ buf.resize(size);
+ istr.read(&buf[0], size); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+}
+
+
+/**
+ * LLSDFormatter
+ */
+LLSDFormatter::LLSDFormatter() :
+ mBoolAlpha(false)
+{
+}
+
+// virtual
+LLSDFormatter::~LLSDFormatter()
+{ }
+
+void LLSDFormatter::boolalpha(bool alpha)
+{
+ mBoolAlpha = alpha;
+}
+
+void LLSDFormatter::realFormat(const std::string& format)
+{
+ mRealFormat = format;
+}
+
+void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const
+{
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, mRealFormat.c_str(), real);
+ ostr << buffer;
+}
+
+/**
+ * LLSDNotationFormatter
+ */
+LLSDNotationFormatter::LLSDNotationFormatter()
+{
+}
+
+// virtual
+LLSDNotationFormatter::~LLSDNotationFormatter()
+{ }
+
+// static
+std::string LLSDNotationFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream ostr;
+ serialize_string(in, ostr);
+ return ostr.str();
+}
+
+// virtual
+S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr << "{";
+ bool need_comma = false;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ ostr << '\'';
+ serialize_string((*iter).first, ostr);
+ ostr << "':";
+ format_count += format((*iter).second, ostr);
+ }
+ ostr << "}";
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr << "[";
+ bool need_comma = false;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ format_count += format(*iter, ostr);
+ }
+ ostr << "]";
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr << "!";
+ break;
+
+ case LLSD::TypeBoolean:
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean()
+ ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL);
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << "i" << data.asInteger();
+ break;
+
+ case LLSD::TypeReal:
+ ostr << "r";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ break;
+
+ case LLSD::TypeUUID:
+ ostr << "u" << data.asUUID();
+ break;
+
+ case LLSD::TypeString:
+ ostr << '\'';
+ serialize_string(data.asString(), ostr);
+ ostr << '\'';
+ break;
+
+ case LLSD::TypeDate:
+ ostr << "d\"" << data.asDate() << "\"";
+ break;
+
+ case LLSD::TypeURI:
+ ostr << "l\"";
+ serialize_string(data.asString(), ostr);
+ ostr << "\"";
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ std::vector<U8> buffer = data.asBinary();
+ ostr << "b(" << buffer.size() << ")\"";
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ ostr << "\"";
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr << "!";
+ break;
+ }
+ return format_count;
+}
+
+
+/**
+ * LLSDBinaryFormatter
+ */
+LLSDBinaryFormatter::LLSDBinaryFormatter()
+{
+}
+
+// virtual
+LLSDBinaryFormatter::~LLSDBinaryFormatter()
+{ }
+
+// virtual
+S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr.put('{');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr.put('k');
+ formatString((*iter).first, ostr);
+ format_count += format((*iter).second, ostr);
+ }
+ ostr.put('}');
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr.put('[');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format(*iter, ostr);
+ }
+ ostr.put(']');
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr.put('!');
+ break;
+
+ case LLSD::TypeBoolean:
+ if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL);
+ else ostr.put(BINARY_FALSE_SERIAL);
+ break;
+
+ case LLSD::TypeInteger:
+ {
+ ostr.put('i');
+ U32 value_nbo = htonl(data.asInteger());
+ ostr.write((const char*)(&value_nbo), sizeof(U32));
+ break;
+ }
+
+ case LLSD::TypeReal:
+ {
+ ostr.put('r');
+ F64 value_nbo = ll_htond(data.asReal());
+ ostr.write((const char*)(&value_nbo), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeUUID:
+ ostr.put('u');
+ ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES);
+ break;
+
+ case LLSD::TypeString:
+ ostr.put('s');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeDate:
+ {
+ ostr.put('d');
+ F64 value = data.asReal();
+ ostr.write((const char*)(&value), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeURI:
+ ostr.put('l');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ ostr.put('b');
+ std::vector<U8> buffer = data.asBinary();
+ U32 size_nbo = htonl(buffer.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr.put('!');
+ break;
+ }
+ return format_count;
+}
+
+void LLSDBinaryFormatter::formatString(
+ const std::string& string,
+ std::ostream& ostr) const
+{
+ U32 size_nbo = htonl(string.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ ostr.write(string.c_str(), string.size());
+}
+
+/**
+ * local functions
+ */
+bool deserialize_string(std::istream& str, std::string& value)
+{
+ char c = str.get();
+ if (str.fail())
+ {
+ // No data in stream, bail out
+ return false;
+ }
+
+ bool rv = false;
+ switch(c)
+ {
+ case '\'':
+ case '"':
+ rv = deserialize_string_delim(str, value, c);
+ break;
+ case 's':
+ rv = deserialize_string_raw(str, value);
+ break;
+ default:
+ break;
+ }
+ return rv;
+}
+
+bool deserialize_string_delim(
+ std::istream& str,
+ std::string& value,
+ char delim)
+{
+ std::ostringstream write_buffer;
+ bool found_escape = false;
+ bool found_hex = false;
+ bool found_digit = false;
+ U8 byte = 0;
+
+ while (true)
+ {
+ char next_char = str.get();
+
+ if(str.fail())
+ {
+ // If our stream is empty, break out
+ value = write_buffer.str();
+ return false;
+ }
+
+ if(found_escape)
+ {
+ // next character(s) is a special sequence.
+ if(found_hex)
+ {
+ if(found_digit)
+ {
+ found_digit = false;
+ found_hex = false;
+ found_escape = false;
+ byte = byte << 4;
+ byte |= hex_as_nybble(next_char);
+ write_buffer << byte;
+ byte = 0;
+ }
+ else
+ {
+ // next character is the first nybble of
+ //
+ found_digit = true;
+ byte = hex_as_nybble(next_char);
+ }
+ }
+ else if(next_char == 'x')
+ {
+ found_hex = true;
+ }
+ else
+ {
+ switch(next_char)
+ {
+ case 'a':
+ write_buffer << '\a';
+ break;
+ case 'b':
+ write_buffer << '\b';
+ break;
+ case 'f':
+ write_buffer << '\f';
+ break;
+ case 'n':
+ write_buffer << '\n';
+ break;
+ case 'r':
+ write_buffer << '\r';
+ break;
+ case 't':
+ write_buffer << '\t';
+ break;
+ case 'v':
+ write_buffer << '\v';
+ break;
+ default:
+ write_buffer << next_char;
+ break;
+ }
+ found_escape = false;
+ }
+ }
+ else if(next_char == '\\')
+ {
+ found_escape = true;
+ }
+ else if(next_char == delim)
+ {
+ break;
+ }
+ else
+ {
+ write_buffer << next_char;
+ }
+ }
+
+ value = write_buffer.str();
+ return true;
+}
+
+bool deserialize_string_raw(std::istream& str, std::string& value)
+{
+ bool ok = false;
+ const S32 BUF_LEN = 20;
+ char buf[BUF_LEN]; /* Flawfinder: ignore */
+ str.get(buf, BUF_LEN - 1, ')');
+ char c = str.get();
+ c = str.get();
+ if(((c == '"') || (c == '\'')) && (buf[0] == '('))
+ {
+ // We probably have a valid raw string. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ // *FIX: This is memory inefficient.
+ S32 len = strtol(buf + 1, NULL, 0);
+ std::vector<char> buf;
+ buf.resize(len);
+ str.read(&buf[0], len); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+ c = str.get();
+ if((c == '"') || (c == '\''))
+ {
+ ok = true;
+ }
+ }
+ return ok;
+}
+
+static const char* NOTATION_STRING_CHARACTERS[256] =
+{
+ "\\x00", // 0
+ "\\x01", // 1
+ "\\x02", // 2
+ "\\x03", // 3
+ "\\x04", // 4
+ "\\x05", // 5
+ "\\x06", // 6
+ "\\a", // 7
+ "\\b", // 8
+ "\\t", // 9
+ "\\n", // 10
+ "\\v", // 11
+ "\\f", // 12
+ "\\r", // 13
+ "\\x0e", // 14
+ "\\x0f", // 15
+ "\\x10", // 16
+ "\\x11", // 17
+ "\\x12", // 18
+ "\\x13", // 19
+ "\\x14", // 20
+ "\\x15", // 21
+ "\\x16", // 22
+ "\\x17", // 23
+ "\\x18", // 24
+ "\\x19", // 25
+ "\\x1a", // 26
+ "\\x1b", // 27
+ "\\x1c", // 28
+ "\\x1d", // 29
+ "\\x1e", // 30
+ "\\x1f", // 31
+ " ", // 32
+ "!", // 33
+ "\"", // 34
+ "#", // 35
+ "$", // 36
+ "%", // 37
+ "&", // 38
+ "\\'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "<", // 60
+ "=", // 61
+ ">", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "[", // 91
+ "\\\\", // 92
+ "]", // 93
+ "^", // 94
+ "_", // 95
+ "`", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "{", // 123
+ "|", // 124
+ "}", // 125
+ "~", // 126
+ "\\x7f", // 127
+ "\\x80", // 128
+ "\\x81", // 129
+ "\\x82", // 130
+ "\\x83", // 131
+ "\\x84", // 132
+ "\\x85", // 133
+ "\\x86", // 134
+ "\\x87", // 135
+ "\\x88", // 136
+ "\\x89", // 137
+ "\\x8a", // 138
+ "\\x8b", // 139
+ "\\x8c", // 140
+ "\\x8d", // 141
+ "\\x8e", // 142
+ "\\x8f", // 143
+ "\\x90", // 144
+ "\\x91", // 145
+ "\\x92", // 146
+ "\\x93", // 147
+ "\\x94", // 148
+ "\\x95", // 149
+ "\\x96", // 150
+ "\\x97", // 151
+ "\\x98", // 152
+ "\\x99", // 153
+ "\\x9a", // 154
+ "\\x9b", // 155
+ "\\x9c", // 156
+ "\\x9d", // 157
+ "\\x9e", // 158
+ "\\x9f", // 159
+ "\\xa0", // 160
+ "\\xa1", // 161
+ "\\xa2", // 162
+ "\\xa3", // 163
+ "\\xa4", // 164
+ "\\xa5", // 165
+ "\\xa6", // 166
+ "\\xa7", // 167
+ "\\xa8", // 168
+ "\\xa9", // 169
+ "\\xaa", // 170
+ "\\xab", // 171
+ "\\xac", // 172
+ "\\xad", // 173
+ "\\xae", // 174
+ "\\xaf", // 175
+ "\\xb0", // 176
+ "\\xb1", // 177
+ "\\xb2", // 178
+ "\\xb3", // 179
+ "\\xb4", // 180
+ "\\xb5", // 181
+ "\\xb6", // 182
+ "\\xb7", // 183
+ "\\xb8", // 184
+ "\\xb9", // 185
+ "\\xba", // 186
+ "\\xbb", // 187
+ "\\xbc", // 188
+ "\\xbd", // 189
+ "\\xbe", // 190
+ "\\xbf", // 191
+ "\\xc0", // 192
+ "\\xc1", // 193
+ "\\xc2", // 194
+ "\\xc3", // 195
+ "\\xc4", // 196
+ "\\xc5", // 197
+ "\\xc6", // 198
+ "\\xc7", // 199
+ "\\xc8", // 200
+ "\\xc9", // 201
+ "\\xca", // 202
+ "\\xcb", // 203
+ "\\xcc", // 204
+ "\\xcd", // 205
+ "\\xce", // 206
+ "\\xcf", // 207
+ "\\xd0", // 208
+ "\\xd1", // 209
+ "\\xd2", // 210
+ "\\xd3", // 211
+ "\\xd4", // 212
+ "\\xd5", // 213
+ "\\xd6", // 214
+ "\\xd7", // 215
+ "\\xd8", // 216
+ "\\xd9", // 217
+ "\\xda", // 218
+ "\\xdb", // 219
+ "\\xdc", // 220
+ "\\xdd", // 221
+ "\\xde", // 222
+ "\\xdf", // 223
+ "\\xe0", // 224
+ "\\xe1", // 225
+ "\\xe2", // 226
+ "\\xe3", // 227
+ "\\xe4", // 228
+ "\\xe5", // 229
+ "\\xe6", // 230
+ "\\xe7", // 231
+ "\\xe8", // 232
+ "\\xe9", // 233
+ "\\xea", // 234
+ "\\xeb", // 235
+ "\\xec", // 236
+ "\\xed", // 237
+ "\\xee", // 238
+ "\\xef", // 239
+ "\\xf0", // 240
+ "\\xf1", // 241
+ "\\xf2", // 242
+ "\\xf3", // 243
+ "\\xf4", // 244
+ "\\xf5", // 245
+ "\\xf6", // 246
+ "\\xf7", // 247
+ "\\xf8", // 248
+ "\\xf9", // 249
+ "\\xfa", // 250
+ "\\xfb", // 251
+ "\\xfc", // 252
+ "\\xfd", // 253
+ "\\xfe", // 254
+ "\\xff" // 255
+};
+
+void serialize_string(const std::string& value, std::ostream& str)
+{
+ std::string::const_iterator it = value.begin();
+ std::string::const_iterator end = value.end();
+ U8 c;
+ for(; it != end; ++it)
+ {
+ c = (U8)(*it);
+ str << NOTATION_STRING_CHARACTERS[c];
+ }
+}
+
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
new file mode 100644
index 0000000000..bc6817c732
--- /dev/null
+++ b/indra/llcommon/llsdserialize.h
@@ -0,0 +1,592 @@
+/**
+ * @file llsdserialize.h
+ * @author Phoenix
+ * @date 2006-02-26
+ * @brief Declaration of parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDSERIALIZE_H
+#define LL_LLSDSERIALIZE_H
+
+#include <iosfwd>
+#include "llsd.h"
+#include "llmemory.h"
+
+/**
+ * @class LLSDParser
+ * @brief Abstract base class for simple LLSD parsers.
+ */
+class LLSDParser : public LLRefCount
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const = 0;
+
+protected:
+
+};
+
+/**
+ * @class LLSDNotationParser
+ * @brief Parser which handles the original notation format for LLSD.
+ */
+class LLSDNotationParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDNotationParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDNotationParser() {}
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data. Undefined on failure.
+ * @return Returns the number of LLSD objects parsed into
+ * data. Returns -1 on parse failure.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Simple notation parse.
+ *
+ * This simplified parser cannot not distinguish between a failed
+ * parse and a parse which yields a single undefined LLSD. You can
+ * use this if error checking will be implicit in the use of the
+ * results of the parse.
+ * @param istr The input stream.
+ * @return Returns the parsed LLSD object.
+ */
+ static LLSD parse(std::istream& istr);
+
+private:
+ /**
+ * @brief Parse a map from the istream
+ *
+ * @param istr The input stream.
+ * @param map The map to add the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseMap(std::istream& istr, LLSD& map) const;
+
+ /**
+ * @brief Parse an array from the istream.
+ *
+ * @param istr The input stream.
+ * @param array The array to append the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseArray(std::istream& istr, LLSD& array) const;
+
+ /**
+ * @brief Parse a string from the istream and assign it to data.
+ *
+ * @param istr The input stream.
+ * @param data[out] The data to assign.
+ */
+ void parseString(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Parse binary data from the stream.
+ *
+ * @param istr The input stream.
+ * @param data[out] The data to assign.
+ */
+ void parseBinary(std::istream& istr, LLSD& data) const;
+};
+
+/**
+ * @class LLSDXMLParser
+ * @brief Parser which handles XML format LLSD.
+ */
+class LLSDXMLParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDXMLParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDXMLParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns the number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+private:
+ class Impl;
+ Impl& impl;
+
+ void parsePart(const char *buf, int len);
+ friend class LLSDSerialize;
+};
+
+/**
+ * @class LLSDBinaryParser
+ * @brief Parser which handles binary formatted LLSD.
+ */
+class LLSDBinaryParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDBinaryParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDBinaryParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns the number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Simple notation parse.
+ *
+ * This simplified parser cannot not distinguish between a failed
+ * parse and a parse which yields a single undefined LLSD. You can
+ * use this if error checking will be implicit in the use of the
+ * results of the parse.
+ * @param istr The input stream.
+ * @return Returns the parsed LLSD object.
+ */
+ static LLSD parse(std::istream& istr);
+
+private:
+ /**
+ * @brief Parse a map from the istream
+ *
+ * @param istr The input stream.
+ * @param map The map to add the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseMap(std::istream& istr, LLSD& map) const;
+
+ /**
+ * @brief Parse an array from the istream.
+ *
+ * @param istr The input stream.
+ * @param array The array to append the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseArray(std::istream& istr, LLSD& array) const;
+
+ /**
+ * @brief Parse a string from the istream and assign it to data.
+ *
+ * @param istr The input stream.
+ * @param value[out] The string to assign.
+ */
+ void parseString(std::istream& istr, std::string& value) const;
+};
+
+
+/**
+ * @class LLSDFormatter
+ * @brief Abstract base class for formatting LLSD.
+ */
+class LLSDFormatter : public LLRefCount
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDFormatter();
+
+public:
+ /**
+ * Options for output
+ */
+ enum e_formatter_options_type
+ {
+ OPTIONS_NONE = 0,
+ OPTIONS_PRETTY = 1
+ } EFormatterOptions;
+
+ /**
+ * @brief Constructor
+ */
+ LLSDFormatter();
+
+ /**
+ * @brief Set the boolean serialization format.
+ *
+ * @param alpha Serializes boolean as alpha if true.
+ */
+ void boolalpha(bool alpha);
+
+ /**
+ * @brief Set the real format
+ *
+ * By default, the formatter will use default double serialization
+ * which is frequently frustrating for many applications. You can
+ * set the precision on the stream independently, but that still
+ * might not work depending on the value.
+ * EXAMPLES:<br>
+ * %.2f<br>
+ * @param format A format string which follows the printf format
+ * rules. Specify an empty string to return to default formatting.
+ */
+ void realFormat(const std::string& format);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const = 0;
+
+protected:
+ /**
+ * @brief Helper method which appropriately obeys the real format.
+ *
+ * @param real The real value to format.
+ * @param ostr The destination stream for the data.
+ */
+ void formatReal(LLSD::Real real, std::ostream& ostr) const;
+
+protected:
+ bool mBoolAlpha;
+ std::string mRealFormat;
+};
+
+
+/**
+ * @class LLSDNotationFormatter
+ * @brief Formatter which outputs the original notation format for LLSD.
+ */
+class LLSDNotationFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDNotationFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDNotationFormatter();
+
+ /**
+ * @brief Helper static method to return a notation escaped string
+ *
+ * This method will return the notation escaped string, but not
+ * the surrounding serialization identifiers such as a double or
+ * single quote. It will be up to the caller to embed those as
+ * appropriate.
+ * @param in The raw, unescaped string.
+ * @return Returns an escaped string appropriate for serialization.
+ */
+ static std::string escapeString(const std::string& in);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+};
+
+
+/**
+ * @class LLSDXMLFormatter
+ * @brief Formatter which outputs the LLSD as XML.
+ */
+class LLSDXMLFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDXMLFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDXMLFormatter();
+
+ /**
+ * @brief Helper static method to return an xml escaped string
+ *
+ * @param in A valid UTF-8 string.
+ * @return Returns an escaped string appropriate for serialization.
+ */
+ static std::string escapeString(const std::string& in);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+
+protected:
+
+ /**
+ * @brief Implementation to format the data. This is called recursively.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ S32 format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const;
+};
+
+
+/**
+ * @class LLSDBinaryFormatter
+ * @brief Formatter which outputs the LLSD as a binary notation format.
+ *
+ * The binary format is a compact and efficient representation of
+ * structured data useful for when transmitting over a small data pipe
+ * or when transmission frequency is very high.<br>
+ *
+ * The normal boolalpha and real format commands are ignored.<br>
+ *
+ * All integers are transmitted in network byte order. The format is:<br>
+ * Undefined: '!'<br>
+ * Boolean: character '1' for true character '0' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as 'k' + 4 byte integer size + string
+ */
+class LLSDBinaryFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDBinaryFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDBinaryFormatter();
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+
+protected:
+ /**
+ * @brief Helper method to serialize strings
+ *
+ * This method serializes a network byte order size and the raw
+ * string contents.
+ * @param string The string to write.
+ * @param ostr The destination stream for the data.
+ */
+ void formatString(const std::string& string, std::ostream& ostr) const;
+};
+
+
+/**
+ * @class LLSDNotationStreamFormatter
+ * @brief Formatter which is specialized for use on streams which
+ * outputs the original notation format for LLSD.
+ *
+ * This class is useful for doing inline stream operations. For example:
+ *
+ * <code>
+ * LLSD sd;<br>
+ * sd["foo"] = "bar";<br>
+ * std::stringstream params;<br>
+ * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd)
+ * << "]";
+ * </code>
+ */
+template <class Formatter>
+class LLSDOStreamer : public Formatter
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDOStreamer(const LLSD& data, U32 options = LLSDFormatter::OPTIONS_NONE) :
+ mSD(data), mOptions(options) {}
+
+ /**
+ * @brief Stream operator.
+ *
+ * Use this inline during construction during a stream operation.
+ * @param str The destination stream for serialized output.
+ * @param The formatter which will output it's LLSD.
+ * @return Returns the stream passed in after streaming mSD.
+ */
+ friend std::ostream& operator<<(
+ std::ostream& str,
+ const LLSDOStreamer<Formatter>& formatter)
+ {
+ formatter.format(formatter.mSD, str, formatter.mOptions);
+ return str;
+ }
+
+protected:
+ LLSD mSD;
+ U32 mOptions;
+};
+
+typedef LLSDOStreamer<LLSDNotationFormatter> LLSDNotationStreamer;
+typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer;
+
+/**
+ * @class LLSDSerialize
+ * @Serializer / deserializer for the various LLSD formats
+ */
+class LLSDSerialize
+{
+public:
+ enum ELLSD_Serialize
+ {
+ LLSD_BINARY, LLSD_XML
+ };
+
+ /*
+ * Generic in/outs
+ */
+ static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize,
+ U32 options = LLSDFormatter::OPTIONS_NONE);
+ static bool deserialize(LLSD& sd, std::istream& str);
+
+ /*
+ * Notation Methods
+ */
+ static S32 toNotation(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 fromNotation(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDNotationParser> p = new LLSDNotationParser;
+ return p->parse(str, sd);
+ }
+
+ /*
+ * XML Methods
+ */
+ static S32 toXML(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 toPrettyXML(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY);
+ }
+ static S32 fromXML(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDXMLParser> p = new LLSDXMLParser;
+ return p->parse(str, sd);
+ }
+
+ /*
+ * Binary Methods
+ */
+ static S32 toBinary(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDBinaryFormatter> f = new LLSDBinaryFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 fromBinary(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser;
+ return p->parse(str, sd);
+ }
+private:
+ static const char *LLSDBinaryHeader;
+ static const char *LLSDXMLHeader;
+};
+
+#endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
new file mode 100644
index 0000000000..2824d0f73c
--- /dev/null
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -0,0 +1,706 @@
+/**
+ * @file llsdserialize_xml.cpp
+ * @brief XML parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize_xml.h"
+
+#include <iostream>
+#include <deque>
+
+#include "apr-1/apr_base64.h"
+
+extern "C"
+{
+#include "expat/expat.h"
+}
+
+/**
+ * LLSDXMLFormatter
+ */
+LLSDXMLFormatter::LLSDXMLFormatter()
+{
+}
+
+// virtual
+LLSDXMLFormatter::~LLSDXMLFormatter()
+{
+}
+
+// virtual
+S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ std::streamsize old_precision = ostr.precision(25);
+
+ LLString post = "";
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ post = "\n";
+ }
+ ostr << "<llsd>" << post;
+ S32 rv = format_impl(data, ostr, options, 1);
+ ostr << "</llsd>\n";
+
+ ostr.precision(old_precision);
+ return rv;
+}
+
+S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const
+{
+ S32 format_count = 1;
+ LLString pre = "";
+ LLString post = "";
+
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ for (U32 i = 0; i < level; i++)
+ {
+ pre += " ";
+ }
+ post = "\n";
+ }
+
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ if(0 == data.size())
+ {
+ ostr << pre << "<map />" << post;
+ }
+ else
+ {
+ ostr << pre << "<map>" << post;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr << pre << "<key>" << escapeString((*iter).first) << "</key>" << post;
+ format_count += format_impl((*iter).second, ostr, options, level + 1);
+ }
+ ostr << pre << "</map>" << post;
+ }
+ break;
+
+ case LLSD::TypeArray:
+ if(0 == data.size())
+ {
+ ostr << pre << "<array />" << post;
+ }
+ else
+ {
+ ostr << pre << "<array>" << post;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format_impl(*iter, ostr, options, level + 1);
+ }
+ ostr << pre << "</array>" << post;
+ }
+ break;
+
+ case LLSD::TypeUndefined:
+ ostr << pre << "<undef />" << post;
+ break;
+
+ case LLSD::TypeBoolean:
+ ostr << pre << "<boolean>";
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean() ? "true" : "false");
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ ostr << "</boolean>" << post;
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << pre << "<integer>" << data.asInteger() << "</integer>" << post;
+ break;
+
+ case LLSD::TypeReal:
+ ostr << pre << "<real>";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ ostr << "</real>" << post;
+ break;
+
+ case LLSD::TypeUUID:
+ if(data.asUUID().isNull()) ostr << pre << "<uuid />" << post;
+ else ostr << pre << "<uuid>" << data.asUUID() << "</uuid>" << post;
+ break;
+
+ case LLSD::TypeString:
+ if(data.asString().empty()) ostr << pre << "<string />" << post;
+ else ostr << pre << "<string>" << escapeString(data.asString()) <<"</string>" << post;
+ break;
+
+ case LLSD::TypeDate:
+ ostr << pre << "<date>" << data.asDate() << "</date>" << post;
+ break;
+
+ case LLSD::TypeURI:
+ ostr << pre << "<uri>" << escapeString(data.asString()) << "</uri>" << post;
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ LLSD::Binary buffer = data.asBinary();
+ if(buffer.empty())
+ {
+ ostr << pre << "<binary />" << post;
+ }
+ else
+ {
+ // *FIX: memory inefficient.
+ ostr << pre << "<binary encoding=\"base64\">";
+ int b64_buffer_length = apr_base64_encode_len(buffer.size());
+ char* b64_buffer = new char[b64_buffer_length];
+ b64_buffer_length = apr_base64_encode_binary(
+ b64_buffer,
+ &buffer[0],
+ buffer.size());
+ ostr.write(b64_buffer, b64_buffer_length - 1);
+ delete[] b64_buffer;
+ ostr << "</binary>" << post;
+ }
+ break;
+ }
+ default:
+ // *NOTE: This should never happen.
+ ostr << pre << "<undef />" << post;
+ break;
+ }
+ return format_count;
+}
+
+// static
+std::string LLSDXMLFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream out;
+ std::string::const_iterator it = in.begin();
+ std::string::const_iterator end = in.end();
+ for(; it != end; ++it)
+ {
+ switch((*it))
+ {
+ case '<':
+ out << "&lt;";
+ break;
+ case '>':
+ out << "&gt;";
+ break;
+ case '&':
+ out << "&amp;";
+ break;
+ case '\'':
+ out << "&apos;";
+ break;
+ case '"':
+ out << "&quot;";
+ break;
+ default:
+ out << (*it);
+ break;
+ }
+ }
+ return out.str();
+}
+
+
+
+class LLSDXMLParser::Impl
+{
+public:
+ Impl();
+ ~Impl();
+
+ LLSD parse(std::istream& input);
+
+ void parsePart(const char *buf, int len);
+
+private:
+ void reset();
+
+ void startElementHandler(const XML_Char* name, const XML_Char** attributes);
+ void endElementHandler(const XML_Char* name);
+ void characterDataHandler(const XML_Char* data, int length);
+
+ static void sStartElementHandler(
+ void* userData, const XML_Char* name, const XML_Char** attributes);
+ static void sEndElementHandler(
+ void* userData, const XML_Char* name);
+ static void sCharacterDataHandler(
+ void* userData, const XML_Char* data, int length);
+
+ void startSkipping();
+
+ enum Element {
+ ELEMENT_LLSD,
+ ELEMENT_UNDEF,
+ ELEMENT_BOOL,
+ ELEMENT_INTEGER,
+ ELEMENT_REAL,
+ ELEMENT_STRING,
+ ELEMENT_UUID,
+ ELEMENT_DATE,
+ ELEMENT_URI,
+ ELEMENT_BINARY,
+ ELEMENT_MAP,
+ ELEMENT_ARRAY,
+ ELEMENT_KEY,
+ ELEMENT_UNKNOWN
+ };
+ static Element readElement(const XML_Char* name);
+
+ static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs);
+
+
+ XML_Parser mParser;
+
+ LLSD mResult;
+
+ bool mInLLSDElement;
+ bool mGracefullStop;
+
+ typedef std::deque<LLSD*> LLSDRefStack;
+ LLSDRefStack mStack;
+
+ int mDepth;
+ bool mSkipping;
+ int mSkipThrough;
+
+ std::string mCurrentKey;
+ std::ostringstream mCurrentContent;
+
+ bool mPreStaged;
+};
+
+
+LLSDXMLParser::Impl::Impl()
+{
+ mParser = XML_ParserCreate(NULL);
+ mPreStaged = false;
+ reset();
+}
+
+LLSDXMLParser::Impl::~Impl()
+{
+ XML_ParserFree(mParser);
+}
+
+bool is_eol(char c)
+{
+ return (c == '\n' || c == '\r');
+}
+
+void clear_eol(std::istream& input)
+{
+ char c = input.peek();
+ while (input.good() && is_eol(c))
+ {
+ input.get(c);
+ c = input.peek();
+ }
+}
+
+static unsigned get_till_eol(std::istream& input, char *buf, unsigned bufsize)
+{
+ unsigned count = 0;
+ while (count < bufsize && input.good())
+ {
+ input.get(buf[count]);
+ count++;
+ if (is_eol(buf[count - 1]))
+ break;
+ }
+ return count;
+}
+
+LLSD LLSDXMLParser::Impl::parse(std::istream& input)
+{
+ reset();
+ XML_Status status;
+
+ static const int BUFFER_SIZE = 1024;
+ void* buffer = NULL;
+ int count = 0;
+ while (input.good() && !input.eof())
+ {
+ buffer = XML_GetBuffer(mParser, BUFFER_SIZE);
+
+ /*
+ * If we happened to end our last buffer right at the end of the llsd, but the
+ * stream is still going we will get a null buffer here. Check for mGracefullStop.
+ */
+ if (!buffer)
+ {
+ break;
+ }
+ count = get_till_eol(input, (char *)buffer, BUFFER_SIZE);
+ if (!count)
+ {
+ break;
+ }
+ status = XML_ParseBuffer(mParser, count, false);
+
+ if (status == XML_STATUS_ERROR)
+ {
+ break;
+ }
+ }
+
+ // FIXME: This code is buggy - if the stream was empty or not good, there
+ // is not buffer to parse, both the call to XML_ParseBuffer and the buffer
+ // manipulations are illegal
+ // futhermore, it isn't clear that the expat buffer semantics are preserved
+
+ status = XML_ParseBuffer(mParser, 0, true);
+ if (status == XML_STATUS_ERROR && !mGracefullStop)
+ {
+ ((char*) buffer)[count? count - 1 : 0] = '\0';
+ llinfos << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << llendl;
+ return LLSD();
+ }
+
+ clear_eol(input);
+ return mResult;
+}
+
+void LLSDXMLParser::Impl::reset()
+{
+ if (mPreStaged)
+ {
+ mPreStaged = false;
+ return;
+ }
+
+ mResult.clear();
+
+ mInLLSDElement = false;
+ mDepth = 0;
+
+ mGracefullStop = false;
+
+ mStack.clear();
+
+ mSkipping = false;
+
+#if( LL_WINDOWS || __GNUC__ > 2)
+ mCurrentKey.clear();
+#else
+ mCurrentKey = std::string();
+#endif
+
+
+ XML_ParserReset(mParser, "utf-8");
+ XML_SetUserData(mParser, this);
+ XML_SetElementHandler(mParser, sStartElementHandler, sEndElementHandler);
+ XML_SetCharacterDataHandler(mParser, sCharacterDataHandler);
+}
+
+
+void LLSDXMLParser::Impl::startSkipping()
+{
+ mSkipping = true;
+ mSkipThrough = mDepth;
+}
+
+const XML_Char*
+LLSDXMLParser::Impl::findAttribute(const XML_Char* name, const XML_Char** pairs)
+{
+ while (NULL != pairs && NULL != *pairs)
+ {
+ if(0 == strcmp(name, *pairs))
+ {
+ return *(pairs + 1);
+ }
+ pairs += 2;
+ }
+ return NULL;
+}
+
+void LLSDXMLParser::Impl::parsePart(const char *buf, int len)
+{
+ void * buffer = XML_GetBuffer(mParser, len);
+ if (buffer != NULL && buf != NULL)
+ {
+ memcpy(buffer, buf, len);
+ }
+ XML_ParseBuffer(mParser, len, false);
+
+ mPreStaged = true;
+}
+
+void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Char** attributes)
+{
+ mDepth += 1;
+ if (mSkipping)
+ {
+ return;
+ }
+
+ Element element = readElement(name);
+ mCurrentContent.str("");
+
+ switch (element)
+ {
+ case ELEMENT_LLSD:
+ if (mInLLSDElement) { return startSkipping(); }
+ mInLLSDElement = true;
+ return;
+
+ case ELEMENT_KEY:
+ if (mStack.empty() || !(mStack.back()->isMap()))
+ {
+ return startSkipping();
+ }
+ return;
+
+ case ELEMENT_BINARY:
+ {
+ const XML_Char* encoding = findAttribute("encoding", attributes);
+ if(encoding && strcmp("base64", encoding) != 0) { return startSkipping(); }
+ break;
+ }
+
+ default:
+ // all rest are values, fall through
+ ;
+ }
+
+
+ if (!mInLLSDElement) { return startSkipping(); }
+
+ if (mStack.empty())
+ {
+ mStack.push_back(&mResult);
+ }
+ else if (mStack.back()->isMap())
+ {
+ if (mCurrentKey.empty()) { return startSkipping(); }
+
+ LLSD& map = *mStack.back();
+ LLSD& newElement = map[mCurrentKey];
+ mStack.push_back(&newElement);
+
+#if( LL_WINDOWS || __GNUC__ > 2)
+ mCurrentKey.clear();
+#else
+ mCurrentKey = std::string();
+#endif
+ }
+ else if (mStack.back()->isArray())
+ {
+ LLSD& array = *mStack.back();
+ array.append(LLSD());
+ LLSD& newElement = array[array.size()-1];
+ mStack.push_back(&newElement);
+ }
+ else {
+ // improperly nested value in a non-structure
+ return startSkipping();
+ }
+
+ switch (element)
+ {
+ case ELEMENT_MAP:
+ *mStack.back() = LLSD::emptyMap();
+ break;
+
+ case ELEMENT_ARRAY:
+ *mStack.back() = LLSD::emptyArray();
+ break;
+
+ default:
+ // all the other values will be set in the end element handler
+ ;
+ }
+}
+
+void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
+{
+ mDepth -= 1;
+ if (mSkipping)
+ {
+ if (mDepth < mSkipThrough)
+ {
+ mSkipping = false;
+ }
+ return;
+ }
+
+ Element element = readElement(name);
+
+ switch (element)
+ {
+ case ELEMENT_LLSD:
+ if (mInLLSDElement)
+ {
+ mInLLSDElement = false;
+ mGracefullStop = true;
+ XML_StopParser(mParser, false);
+ }
+ return;
+
+ case ELEMENT_KEY:
+ mCurrentKey = mCurrentContent.str();
+ return;
+
+ default:
+ // all rest are values, fall through
+ ;
+ }
+
+ if (!mInLLSDElement) { return; }
+
+ LLSD& value = *mStack.back();
+ mStack.pop_back();
+
+ std::string content = mCurrentContent.str();
+ mCurrentContent.str("");
+
+ switch (element)
+ {
+ case ELEMENT_UNDEF:
+ value.clear();
+ break;
+
+ case ELEMENT_BOOL:
+ value = content == "true" || content == "1";
+ break;
+
+ case ELEMENT_INTEGER:
+ value = LLSD(content).asInteger();
+ break;
+
+ case ELEMENT_REAL:
+ value = LLSD(content).asReal();
+ break;
+
+ case ELEMENT_STRING:
+ value = content;
+ break;
+
+ case ELEMENT_UUID:
+ value = LLSD(content).asUUID();
+ break;
+
+ case ELEMENT_DATE:
+ value = LLSD(content).asDate();
+ break;
+
+ case ELEMENT_URI:
+ value = LLSD(content).asURI();
+ break;
+
+ case ELEMENT_BINARY:
+ {
+ S32 len = apr_base64_decode_len(content.c_str());
+ std::vector<U8> data;
+ data.resize(len);
+ len = apr_base64_decode_binary(&data[0], content.c_str());
+ data.resize(len);
+ value = data;
+ break;
+ }
+
+ case ELEMENT_UNKNOWN:
+ value.clear();
+ break;
+
+ default:
+ // other values, map and array, have already been set
+ break;
+ }
+}
+
+void LLSDXMLParser::Impl::characterDataHandler(const XML_Char* data, int length)
+{
+ mCurrentContent.write(data, length);
+}
+
+
+void LLSDXMLParser::Impl::sStartElementHandler(
+ void* userData, const XML_Char* name, const XML_Char** attributes)
+{
+ ((LLSDXMLParser::Impl*)userData)->startElementHandler(name, attributes);
+}
+
+void LLSDXMLParser::Impl::sEndElementHandler(
+ void* userData, const XML_Char* name)
+{
+ ((LLSDXMLParser::Impl*)userData)->endElementHandler(name);
+}
+
+void LLSDXMLParser::Impl::sCharacterDataHandler(
+ void* userData, const XML_Char* data, int length)
+{
+ ((LLSDXMLParser::Impl*)userData)->characterDataHandler(data, length);
+}
+
+
+LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* name)
+{
+ if (strcmp(name, "llsd") == 0) { return ELEMENT_LLSD; }
+ if (strcmp(name, "undef") == 0) { return ELEMENT_UNDEF; }
+ if (strcmp(name, "boolean") == 0) { return ELEMENT_BOOL; }
+ if (strcmp(name, "integer") == 0) { return ELEMENT_INTEGER; }
+ if (strcmp(name, "real") == 0) { return ELEMENT_REAL; }
+ if (strcmp(name, "string") == 0) { return ELEMENT_STRING; }
+ if (strcmp(name, "uuid") == 0) { return ELEMENT_UUID; }
+ if (strcmp(name, "date") == 0) { return ELEMENT_DATE; }
+ if (strcmp(name, "uri") == 0) { return ELEMENT_URI; }
+ if (strcmp(name, "binary") == 0) { return ELEMENT_BINARY; }
+ if (strcmp(name, "map") == 0) { return ELEMENT_MAP; }
+ if (strcmp(name, "array") == 0) { return ELEMENT_ARRAY; }
+ if (strcmp(name, "key") == 0) { return ELEMENT_KEY; }
+
+ return ELEMENT_UNKNOWN;
+}
+
+
+
+
+
+
+
+LLSDXMLParser::LLSDXMLParser()
+ : impl(* new Impl)
+{
+}
+
+LLSDXMLParser::~LLSDXMLParser()
+{
+ delete &impl;
+}
+
+void LLSDXMLParser::parsePart(const char *buf, int len)
+{
+ impl.parsePart(buf, len);
+}
+
+// virtual
+S32 LLSDXMLParser::parse(std::istream& input, LLSD& data) const
+{
+ data = impl.parse(input);
+ return 0;
+}
diff --git a/indra/llcommon/llsdserialize_xml.h b/indra/llcommon/llsdserialize_xml.h
new file mode 100644
index 0000000000..f4aa7b92a4
--- /dev/null
+++ b/indra/llcommon/llsdserialize_xml.h
@@ -0,0 +1,17 @@
+/**
+ * @file llsdserialize_xml.h
+ * @brief XML parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDSERIALIZE_XML_H
+#define LL_LLSDSERIALIZE_XML_H
+
+#include "llsdserialize.h"
+
+// all the XML class definitions are in llsdserialze.h for now
+
+#endif // LL_LLSDSERIALIZE_XML_H
+
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
new file mode 100644
index 0000000000..9305f82e05
--- /dev/null
+++ b/indra/llcommon/llsdutil.cpp
@@ -0,0 +1,177 @@
+/**
+ * @file llsdutil.cpp
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Implementation of classes, functions, etc, for using structured data.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_LINUX
+#include <netinet/in.h>
+#endif
+#if LL_DARWIN
+#include <arpa/inet.h>
+#endif
+
+#include "linden_common.h"
+#include "llsdutil.h"
+
+// vector3
+LLSD ll_sd_from_vector3(const LLVector3& vec)
+{
+ LLSD rv;
+ rv.append((F64)vec.mV[VX]);
+ rv.append((F64)vec.mV[VY]);
+ rv.append((F64)vec.mV[VZ]);
+ return rv;
+}
+
+LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index)
+{
+ LLVector3 rv;
+ rv.mV[VX] = (F32)sd[start_index].asReal();
+ rv.mV[VY] = (F32)sd[++start_index].asReal();
+ rv.mV[VZ] = (F32)sd[++start_index].asReal();
+ return rv;
+}
+
+// vector3d
+LLSD ll_sd_from_vector3d(const LLVector3d& vec)
+{
+ LLSD rv;
+ rv.append(vec.mdV[VX]);
+ rv.append(vec.mdV[VY]);
+ rv.append(vec.mdV[VZ]);
+ return rv;
+}
+
+LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index)
+{
+ LLVector3d rv;
+ rv.mdV[VX] = sd[start_index].asReal();
+ rv.mdV[VY] = sd[++start_index].asReal();
+ rv.mdV[VZ] = sd[++start_index].asReal();
+ return rv;
+}
+
+//vector2
+LLSD ll_sd_from_vector2(const LLVector2& vec)
+{
+ LLSD rv;
+ rv.append((F64)vec.mV[VX]);
+ rv.append((F64)vec.mV[VY]);
+ return rv;
+}
+
+LLVector2 ll_vector2_from_sd(const LLSD& sd)
+{
+ LLVector2 rv;
+ rv.mV[VX] = (F32)sd[0].asReal();
+ rv.mV[VY] = (F32)sd[1].asReal();
+ return rv;
+}
+
+// Quaternion
+LLSD ll_sd_from_quaternion(const LLQuaternion& quat)
+{
+ LLSD rv;
+ rv.append((F64)quat.mQ[VX]);
+ rv.append((F64)quat.mQ[VY]);
+ rv.append((F64)quat.mQ[VZ]);
+ rv.append((F64)quat.mQ[VW]);
+ return rv;
+}
+
+LLQuaternion ll_quaternion_from_sd(const LLSD& sd)
+{
+ LLQuaternion quat;
+ quat.mQ[VX] = (F32)sd[0].asReal();
+ quat.mQ[VY] = (F32)sd[1].asReal();
+ quat.mQ[VZ] = (F32)sd[2].asReal();
+ quat.mQ[VW] = (F32)sd[3].asReal();
+ return quat;
+}
+
+// color4
+LLSD ll_sd_from_color4(const LLColor4& c)
+{
+ LLSD rv;
+ rv.append(c.mV[0]);
+ rv.append(c.mV[1]);
+ rv.append(c.mV[2]);
+ rv.append(c.mV[3]);
+ return rv;
+}
+
+LLColor4 ll_color4_from_sd(const LLSD& sd)
+{
+ LLColor4 c;
+ c.mV[0] = (F32)sd[0].asReal();
+ c.mV[1] = (F32)sd[1].asReal();
+ c.mV[2] = (F32)sd[2].asReal();
+ c.mV[3] = (F32)sd[3].asReal();
+ return c;
+}
+
+// U32
+LLSD ll_sd_from_U32(const U32 val)
+{
+ std::vector<U8> v;
+ U32 net_order = htonl(val);
+
+ v.resize(4);
+ memcpy(&(v[0]), &net_order, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U32 ll_U32_from_sd(const LLSD& sd)
+{
+ U32 ret;
+ std::vector<U8> v = sd.asBinary();
+ if (v.size() < 4)
+ {
+ return 0;
+ }
+ memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */
+ ret = ntohl(ret);
+ return ret;
+}
+
+//U64
+LLSD ll_sd_from_U64(const U64 val)
+{
+ std::vector<U8> v;
+ U32 high, low;
+
+ high = (U32)(val >> 32);
+ low = (U32)val;
+ high = htonl(high);
+ low = htonl(low);
+
+ v.resize(8);
+ memcpy(&(v[0]), &high, 4); /* Flawfinder: ignore */
+ memcpy(&(v[4]), &low, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U64 ll_U64_from_sd(const LLSD& sd)
+{
+ U32 high, low;
+ std::vector<U8> v = sd.asBinary();
+
+ if (v.size() < 8)
+ {
+ return 0;
+ }
+
+ memcpy(&high, &(v[0]), 4); /* Flawfinder: ignore */
+ memcpy(&low, &(v[4]), 4); /* Flawfinder: ignore */
+ high = ntohl(high);
+ low = ntohl(low);
+
+ return ((U64)high) << 32 | low;
+}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
new file mode 100644
index 0000000000..9369f1a39f
--- /dev/null
+++ b/indra/llcommon/llsdutil.h
@@ -0,0 +1,50 @@
+/**
+ * @file llsdutil.h
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Utility classes, functions, etc, for using structured data.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDUTIL_H
+#define LL_LLSDUTIL_H
+
+#include "llsd.h"
+#include "../llmath/v3math.h"
+#include "../llmath/v3dmath.h"
+#include "../llmath/v2math.h"
+#include "../llmath/llquaternion.h"
+#include "../llmath/v4color.h"
+#include "../llprimitive/lltextureanim.h"
+
+// vector3
+LLSD ll_sd_from_vector3(const LLVector3& vec);
+LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0);
+
+// vector3d (double)
+LLSD ll_sd_from_vector3d(const LLVector3d& vec);
+LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0);
+
+// vector2
+LLSD ll_sd_from_vector2(const LLVector2& vec);
+LLVector2 ll_vector2_from_sd(const LLSD& sd);
+
+// Quaternion
+LLSD ll_sd_from_quaternion(const LLQuaternion& quat);
+LLQuaternion ll_quaternion_from_sd(const LLSD& sd);
+
+// color4
+LLSD ll_sd_from_color4(const LLColor4& c);
+LLColor4 ll_color4_from_sd(const LLSD& sd);
+
+// U32
+LLSD ll_sd_from_U32(const U32);
+U32 ll_U32_from_sd(const LLSD& sd);
+
+// U64
+LLSD ll_sd_from_U64(const U64);
+U64 ll_U64_from_sd(const LLSD& sd);
+
+#endif // LL_LLSDUTIL_H
diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp
new file mode 100644
index 0000000000..9d5395ad07
--- /dev/null
+++ b/indra/llcommon/llsecondlifeurls.cpp
@@ -0,0 +1,63 @@
+/**
+ * @file llsecondlifeurls.cpp
+ * @brief Urls used in the product
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsecondlifeurls.h"
+
+const char CREATE_ACCOUNT_URL[] =
+ "http://secondlife.com/registration/";
+
+const char MANAGE_ACCOUNT[] =
+ "http://secondlife.com/account/";
+
+const char AUCTION_URL[] =
+ "http://secondlife.com/auctions/auction-detail.php?id=";
+
+const char EVENTS_URL[] =
+ "http://secondlife.com/events/";
+
+const char TIER_UP_URL[] =
+ "http://secondlife.com/app/landtier";
+
+const char LAND_URL[] =
+ "http://secondlife.com/app/landtier";
+
+const char UPGRADE_TO_PREMIUM_URL[] =
+ "http://secondlife.com/app/upgrade/";
+
+const char DIRECTX_9_URL[] =
+ "http://secondlife.com/support/";
+
+const char AMD_AGP_URL[] =
+ "http://secondlife.com/support/";
+
+const char VIA_URL[] =
+ "http://secondlife.com/support/";
+
+const char INTEL_CHIPSET_URL[] =
+ "http://secondlife.com/support/";
+
+const char SIS_CHIPSET_URL[] =
+ "http://secondlife.com/support/";
+
+const char BLOGS_URL[] =
+ "http://blog.secondlife.com/";
+
+const char BUY_CURRENCY_URL[] =
+ "http://secondlife.com/app/currency/";
+
+const char LSL_DOC_URL[] =
+ "http://secondlife.com/app/lsldoc/";
+
+const char SL_KB_URL[] =
+ "http://secondlife.com/knowledgebase/";
+
+const char ACCOUNT_TRANSACTIONS_URL[] =
+ "https://secondlife.com/account/transactions.php";
+
+const char RELEASE_NOTES[] = "releasenotes.txt";
diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h
new file mode 100644
index 0000000000..86d18d0eab
--- /dev/null
+++ b/indra/llcommon/llsecondlifeurls.h
@@ -0,0 +1,64 @@
+/**
+ * @file llsecondlifeurls.h
+ * @brief Global URLs to pages on our web site
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSECONDLIFEURLS_H
+#define LL_LLSECONDLIFEURLS_H
+
+// Account registration web page
+extern const char CREATE_ACCOUNT_URL[];
+
+// Manage Account
+extern const char MANAGE_ACCOUNT[];
+
+extern const char AUCTION_URL[];
+
+extern const char EVENTS_URL[];
+
+// Tier up to a new land level.
+extern const char TIER_UP_URL[];
+
+// Tier up to a new land level.
+extern const char LAND_URL[];
+
+// Upgrade from basic membership to premium membership
+extern const char UPGRADE_TO_PREMIUM_URL[];
+
+// How to get DirectX 9
+extern const char DIRECTX_9_URL[];
+
+// On AMD with bad AGP controller
+extern const char AMD_AGP_URL[];
+
+// Out of date VIA chipset
+extern const char VIA_URL[];
+
+// Out of date intel chipset driver
+extern const char INTEL_CHIPSET_URL[];
+
+// Out of date SiS chipset driver
+extern const char SIS_CHIPSET_URL[];
+
+// Linden Blogs page
+extern const char BLOGS_URL[];
+
+// Currency page
+extern const char BUY_CURRENCY_URL[];
+
+// LSL script wiki
+extern const char LSL_DOC_URL[];
+
+// SL KnowledgeBase page
+extern const char SL_KB_URL[];
+
+// Account transactions
+extern const char ACCOUNT_TRANSACTIONS_URL[];
+
+// Local Url Release Notes
+extern const char RELEASE_NOTES[];
+
+#endif
diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h
new file mode 100644
index 0000000000..0acac99c45
--- /dev/null
+++ b/indra/llcommon/llsimplehash.h
@@ -0,0 +1,137 @@
+/**
+ * @file llsimplehash.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSIMPLEHASH_H
+#define LL_LLSIMPLEHASH_H
+
+#include "llstl.h"
+
+template <typename HASH_KEY_TYPE>
+class LLSimpleHashEntry
+{
+protected:
+ HASH_KEY_TYPE mHashKey;
+ LLSimpleHashEntry<HASH_KEY_TYPE>* mNextEntry;
+public:
+ LLSimpleHashEntry(HASH_KEY_TYPE key) :
+ mHashKey(key),
+ mNextEntry(0)
+ {
+ }
+ virtual ~LLSimpleHashEntry()
+ {
+ }
+ HASH_KEY_TYPE getHashKey() const
+ {
+ return mHashKey;
+ }
+ LLSimpleHashEntry<HASH_KEY_TYPE>* getNextEntry() const
+ {
+ return mNextEntry;
+ }
+ void setNextEntry(LLSimpleHashEntry<HASH_KEY_TYPE>* next)
+ {
+ mNextEntry = next;
+ }
+};
+
+template <typename HASH_KEY_TYPE, int TABLE_SIZE>
+class LLSimpleHash
+{
+public:
+ LLSimpleHash()
+ {
+ llassert(TABLE_SIZE);
+ llassert((TABLE_SIZE ^ (TABLE_SIZE-1)) == (TABLE_SIZE | (TABLE_SIZE-1))); // power of 2
+ memset(mEntryTable, 0, sizeof(mEntryTable));
+ }
+ virtual ~LLSimpleHash()
+ {
+ }
+
+ virtual int getIndex(HASH_KEY_TYPE key)
+ {
+ return key & (TABLE_SIZE-1);
+ }
+
+ bool insert(LLSimpleHashEntry<HASH_KEY_TYPE>* entry)
+ {
+ llassert(entry->getNextEntry() == 0);
+ int index = getIndex(entry->getHashKey());
+ entry->setNextEntry(mEntryTable[index]);
+ mEntryTable[index] = entry;
+ return true;
+ }
+ LLSimpleHashEntry<HASH_KEY_TYPE>* find(HASH_KEY_TYPE key)
+ {
+ int index = getIndex(key);
+ LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index];
+ while(res && (res->getHashKey() != key))
+ {
+ res = res->getNextEntry();
+ }
+ return res;
+ }
+ bool erase(LLSimpleHashEntry<HASH_KEY_TYPE>* entry)
+ {
+ return erase(entry->getHashKey());
+ }
+ bool erase(HASH_KEY_TYPE key)
+ {
+ int index = getIndex(key);
+ LLSimpleHashEntry<HASH_KEY_TYPE>* prev = 0;
+ LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index];
+ while(res && (res->getHashKey() != key))
+ {
+ prev = res;
+ res = res->getNextEntry();
+ }
+ if (res)
+ {
+ LLSimpleHashEntry<HASH_KEY_TYPE>* next = res->getNextEntry();
+ if (prev)
+ {
+ prev->setNextEntry(next);
+ }
+ else
+ {
+ mEntryTable[index] = next;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // Removes and returns an arbitrary ("first") element from the table
+ // Used for deleting the entire table.
+ LLSimpleHashEntry<HASH_KEY_TYPE>* pop_element()
+ {
+ for (int i=0; i<TABLE_SIZE; i++)
+ {
+ LLSimpleHashEntry<HASH_KEY_TYPE>* entry = mEntryTable[i];
+ if (entry)
+ {
+ mEntryTable[i] = entry->getNextEntry();
+ return entry;
+ }
+ }
+ return 0;
+ }
+ // debugging
+ LLSimpleHashEntry<HASH_KEY_TYPE>* get_element_at_index(S32 index) const
+ {
+ return mEntryTable[index];
+ }
+
+
+private:
+ LLSimpleHashEntry<HASH_KEY_TYPE>* mEntryTable[TABLE_SIZE];
+};
+
+#endif // LL_LLSIMPLEHASH_H
diff --git a/indra/llcommon/llskiplist.h b/indra/llcommon/llskiplist.h
new file mode 100644
index 0000000000..a86eb05d46
--- /dev/null
+++ b/indra/llcommon/llskiplist.h
@@ -0,0 +1,502 @@
+/**
+ * @file llskiplist.h
+ * @brief skip list implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLSKIPLIST_H
+#define LL_LLSKIPLIST_H
+
+#include "llerror.h"
+//#include "vmath.h"
+
+// NOTA BENE: Insert first needs to be < NOT <=
+
+template <class DATA_TYPE, S32 BINARY_DEPTH = 10>
+class LLSkipList
+{
+public:
+ typedef BOOL (*compare)(const DATA_TYPE& first, const DATA_TYPE& second);
+ typedef compare insert_func;
+ typedef compare equals_func;
+
+ void init();
+
+ // basic constructor
+ LLSkipList();
+
+ // basic constructor including sorter
+ LLSkipList(insert_func insert_first, equals_func equals);
+ ~LLSkipList();
+
+ inline void setInsertFirst(insert_func insert_first);
+ inline void setEquals(equals_func equals);
+
+ inline BOOL addData(const DATA_TYPE& data);
+ inline BOOL checkData(const DATA_TYPE& data);
+
+ // returns number of items in the list
+ inline S32 getLength() const; // NOT a constant time operation, traverses entire list!
+
+ inline BOOL moveData(const DATA_TYPE& data, LLSkipList *newlist);
+
+ inline BOOL removeData(const DATA_TYPE& data);
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE getNextData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE getFirstData();
+
+ class LLSkipNode
+ {
+ public:
+ LLSkipNode()
+ : mData(0)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ LLSkipNode(DATA_TYPE data)
+ : mData(data)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ ~LLSkipNode()
+ {
+ }
+
+ DATA_TYPE mData;
+ LLSkipNode *mForward[BINARY_DEPTH];
+
+ private:
+ // Disallow copying of LLSkipNodes by not implementing these methods.
+ LLSkipNode(const LLSkipNode &);
+ LLSkipNode &operator=(const LLSkipNode &);
+ };
+
+ static BOOL defaultEquals(const DATA_TYPE& first, const DATA_TYPE& second)
+ {
+ return first == second;
+ }
+
+private:
+ LLSkipNode mHead;
+ LLSkipNode *mUpdate[BINARY_DEPTH];
+ LLSkipNode *mCurrentp;
+ LLSkipNode *mCurrentOperatingp;
+ S32 mLevel;
+ insert_func mInsertFirst;
+ equals_func mEquals;
+
+private:
+ // Disallow copying of LLSkipNodes by not implementing these methods.
+ LLSkipList(const LLSkipList &);
+ LLSkipList &operator=(const LLSkipList &);
+};
+
+
+///////////////////////
+//
+// Implementation
+//
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::init()
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// basic constructor
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList()
+: mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ init();
+}
+
+// basic constructor including sorter
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList(insert_func insert,
+ equals_func equals)
+: mInsertFirst(insert),
+ mEquals(equals)
+{
+ init();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::~LLSkipList()
+{
+ removeAllNodes();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
+{
+ mInsertFirst = insert_first;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(equals_func equals)
+{
+ mEquals = equals;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::addData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLSkipNode *snode = new LLSkipNode(data);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return TRUE;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+
+ if (current)
+ {
+ return mEquals(current->mData, data);
+ }
+
+ return FALSE;
+}
+
+// returns number of items in the list
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLSkipList<DATA_TYPE, BINARY_DEPTH>::getLength() const
+{
+ U32 length = 0;
+ for (LLSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+ return length;
+}
+
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE& data, LLSkipList *newlist)
+{
+ BOOL removed = removeData(data);
+ BOOL added = newlist->addData(data);
+ return removed && added;
+}
+
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = current->mForward[0];
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = current->mForward[0];
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
+{
+ LLSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mData);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+
+#endif
diff --git a/indra/llcommon/llskipmap.h b/indra/llcommon/llskipmap.h
new file mode 100644
index 0000000000..52270d89ba
--- /dev/null
+++ b/indra/llcommon/llskipmap.h
@@ -0,0 +1,1004 @@
+/**
+ * @file llskipmap.h
+ * @brief Associative container based on the skiplist algorithm.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSKIPMAP_H
+#define LL_LLSKIPMAP_H
+
+#include "llerror.h"
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH = 8>
+class LLSkipMap
+{
+public:
+ // basic constructor
+ LLSkipMap();
+
+ // basic constructor including sorter
+ LLSkipMap(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second),
+ BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+
+ ~LLSkipMap();
+
+ void setInsertFirst(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+ void setEquals(BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+
+ DATA_TYPE &addData(const INDEX_TYPE &index, DATA_TYPE datap);
+ DATA_TYPE &addData(const INDEX_TYPE &index);
+ DATA_TYPE &getData(const INDEX_TYPE &index);
+ DATA_TYPE &operator[](const INDEX_TYPE &index);
+
+ // If index present, returns data.
+ // If index not present, adds <index,NULL> and returns NULL.
+ DATA_TYPE &getData(const INDEX_TYPE &index, BOOL &b_new_entry);
+
+ // Returns TRUE if data present in map.
+ BOOL checkData(const INDEX_TYPE &index);
+
+ // Returns TRUE if key is present in map. This is useful if you
+ // are potentially storing NULL pointers in the map
+ BOOL checkKey(const INDEX_TYPE &index);
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ DATA_TYPE getIfThere(const INDEX_TYPE &index);
+
+ INDEX_TYPE reverseLookup(const DATA_TYPE datap);
+
+ // returns number of items in the list
+ S32 getLength(); // WARNING! getLength is O(n), not O(1)!
+
+ BOOL removeData(const INDEX_TYPE &index);
+
+ // remove all nodes from the list but do not delete data
+ void removeAllData();
+
+ // place mCurrentp on first node
+ void resetList();
+
+ // return the data currently pointed to
+ DATA_TYPE getCurrentDataWithoutIncrement();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_TYPE getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ DATA_TYPE getNextData();
+
+ INDEX_TYPE getNextKey();
+
+ // return the key currently pointed to
+ INDEX_TYPE getCurrentKeyWithoutIncrement();
+
+ // The internal iterator is at the end of the list.
+ BOOL notDone() const;
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void removeCurrentData();
+
+ void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_TYPE getFirstData();
+
+ INDEX_TYPE getFirstKey();
+
+ class LLSkipMapNode
+ {
+ public:
+ LLSkipMapNode()
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mIndex;
+
+ for (i = 0; i < (S32)sizeof(INDEX_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+
+ zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLSkipMapNode(const INDEX_TYPE &index)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLSkipMapNode(const INDEX_TYPE &index, DATA_TYPE datap)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ mData = datap;
+ }
+
+ ~LLSkipMapNode()
+ {
+ }
+
+
+ INDEX_TYPE mIndex;
+ DATA_TYPE mData;
+ LLSkipMapNode *mForward[BINARY_DEPTH];
+
+ private:
+ // Disallow copying of LLSkipMapNodes by not implementing these methods.
+ LLSkipMapNode(const LLSkipMapNode &);
+ LLSkipMapNode &operator=(const LLSkipMapNode &rhs);
+ };
+
+ static BOOL defaultEquals(const INDEX_TYPE &first, const INDEX_TYPE &second)
+ {
+ return first == second;
+ }
+
+private:
+ // don't generate implicit copy constructor or copy assignment
+ LLSkipMap(const LLSkipMap &);
+ LLSkipMap &operator=(const LLSkipMap &);
+
+private:
+ LLSkipMapNode mHead;
+ LLSkipMapNode *mUpdate[BINARY_DEPTH];
+ LLSkipMapNode *mCurrentp;
+ LLSkipMapNode *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const INDEX_TYPE &first, const INDEX_TYPE &second);
+ BOOL (*mEquals)(const INDEX_TYPE &first, const INDEX_TYPE &second);
+ S32 mNumberOfSteps;
+};
+
+//////////////////////////////////////////////////
+//
+// LLSkipMap implementation
+//
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::LLSkipMap()
+ : mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ // Skipmaps must have binary depth of at least 2
+ cassert(BINARY_DEPTH >= 2);
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::LLSkipMap(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second),
+ BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+ : mInsertFirst(insert_first),
+ mEquals(equals)
+{
+ // Skipmaps must have binary depth of at least 2
+ cassert(BINARY_DEPTH >= 2);
+
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::~LLSkipMap()
+{
+ removeAllData();
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+{
+ mInsertFirst = insert_first;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+{
+ mEquals = equals;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::addData(const INDEX_TYPE &index, DATA_TYPE datap)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // replace the existing data if a node is already there
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+ current->mData = datap;
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ {
+ break;
+ }
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index, datap);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::addData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ break;
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+ mNumberOfSteps++;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ break;
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::operator[](const INDEX_TYPE &index)
+{
+
+ return getData(index);
+}
+
+// If index present, returns data.
+// If index not present, adds <index,NULL> and returns NULL.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getData(const INDEX_TYPE &index, BOOL &b_new_entry)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+ b_new_entry = TRUE;
+ addData(index);
+
+ return current->mData;
+}
+
+// Returns TRUE if data present in map.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::checkData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ // Gets rid of some compiler ambiguity for the LLPointer<> templated class.
+ if (current->mData)
+ {
+ return mEquals(current->mIndex, index);
+ }
+ }
+
+ return FALSE;
+}
+
+// Returns TRUE if key is present in map. This is useful if you
+// are potentially storing NULL pointers in the map
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::checkKey(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mIndex, index);
+ }
+
+ return FALSE;
+}
+
+// If there, returns the data.
+// If not, returns NULL.
+// Never adds entries to the map.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getIfThere(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ if (mEquals(current->mIndex, index))
+ {
+ return current->mData;
+ }
+ }
+
+ // Avoid Linux compiler warning on returning NULL.
+ return DATA_TYPE();
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::reverseLookup(const DATA_TYPE datap)
+{
+ LLSkipMapNode *current = &mHead;
+
+ while (current)
+ {
+ if (datap == current->mData)
+ {
+
+ return current->mIndex;
+ }
+ current = *current->mForward;
+ }
+
+ // not found! return NULL
+ return INDEX_TYPE();
+}
+
+// returns number of items in the list
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ for (LLSkipMapNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+
+ return length;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+
+// remove all nodes from the list but do not delete data
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeAllData()
+{
+ LLSkipMapNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// place mCurrentp on first node
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// return the data currently pointed to
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentDataWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ return DATA_TYPE();
+ }
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // Basic types, like int, have default constructors that initialize
+ // them to zero. g++ 2.95 supports this. "int()" is zero.
+ // This also is nice for LLUUID()
+ return DATA_TYPE();
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // Basic types, like int, have default constructors that initialize
+ // them to zero. g++ 2.95 supports this. "int()" is zero.
+ // This also is nice for LLUUID()
+ return DATA_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getNextKey()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+// return the key currently pointed to
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentKeyWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ // See comment for getNextData()
+ return INDEX_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::notDone() const
+{
+ if (mCurrentOperatingp)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mIndex);
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mIndex);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // See comment for getNextData()
+ return DATA_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getFirstKey()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llstack.h b/indra/llcommon/llstack.h
new file mode 100644
index 0000000000..024acf149e
--- /dev/null
+++ b/indra/llcommon/llstack.h
@@ -0,0 +1,30 @@
+/**
+ * @file llstack.h
+ * @brief LLStack template class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTACK_H
+#define LL_LLSTACK_H
+
+#include "linked_lists.h"
+
+template <class DATA_TYPE> class LLStack
+{
+private:
+ LLLinkedList<DATA_TYPE> mStack;
+
+public:
+ LLStack() {}
+ ~LLStack() {}
+
+ void push(DATA_TYPE *data) { mStack.addData(data); }
+ DATA_TYPE *pop() { DATA_TYPE *tempp = mStack.getFirstData(); mStack.removeCurrentData(); return tempp; }
+ void deleteAllData() { mStack.deleteAllData(); }
+ void removeAllNodes() { mStack.removeAllNodes(); }
+};
+
+#endif
+
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
new file mode 100644
index 0000000000..ef707d3497
--- /dev/null
+++ b/indra/llcommon/llstat.cpp
@@ -0,0 +1,803 @@
+/**
+ * @file llstat.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstat.h"
+#include "llframetimer.h"
+#include "timing.h"
+
+class LLStatAccum::impl
+{
+public:
+ static const TimeScale IMPL_NUM_SCALES = (TimeScale)(SCALE_TWO_MINUTE + 1);
+ static U64 sScaleTimes[IMPL_NUM_SCALES];
+
+ BOOL mUseFrameTimer;
+
+ BOOL mRunning;
+ U64 mLastTime;
+
+ struct Bucket
+ {
+ F64 accum;
+ U64 endTime;
+
+ BOOL lastValid;
+ F64 lastAccum;
+ };
+
+ Bucket mBuckets[IMPL_NUM_SCALES];
+
+ BOOL mLastSampleValid;
+ F64 mLastSampleValue;
+
+
+ impl(bool useFrameTimer);
+
+ void reset(U64 when);
+
+ void sum(F64 value);
+ void sum(F64 value, U64 when);
+
+ F32 meanValue(TimeScale scale) const;
+
+ U64 getCurrentUsecs() const;
+ // Get current microseconds based on timer type
+};
+
+
+U64 LLStatAccum::impl::sScaleTimes[IMPL_NUM_SCALES] =
+{
+ USEC_PER_SEC * 1, // seconds
+ USEC_PER_SEC * 60, // minutes
+ USEC_PER_SEC * 60 * 2 // minutes
+#if 0
+ // enable these when more time scales are desired
+ USEC_PER_SEC * 60*60, // hours
+ USEC_PER_SEC * 24*60*60, // days
+ USEC_PER_SEC * 7*24*60*60, // weeks
+#endif
+};
+
+
+LLStatAccum::impl::impl(bool useFrameTimer)
+{
+ mUseFrameTimer = useFrameTimer;
+ mRunning = FALSE;
+ mLastSampleValid = FALSE;
+}
+
+void LLStatAccum::impl::reset(U64 when)
+{
+ mRunning = TRUE;
+ mLastTime = when;
+
+ for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ {
+ mBuckets[i].accum = 0.0;
+ mBuckets[i].endTime = when + sScaleTimes[i];
+ mBuckets[i].lastValid = FALSE;
+ }
+}
+
+void LLStatAccum::impl::sum(F64 value)
+{
+ sum(value, getCurrentUsecs());
+}
+
+void LLStatAccum::impl::sum(F64 value, U64 when)
+{
+ if (!mRunning)
+ {
+ reset(when);
+ return;
+ }
+ if (when < mLastTime)
+ {
+ llwarns << "LLStatAccum::sum clock has gone backwards from "
+ << mLastTime << " to " << when << ", resetting" << llendl;
+
+ reset(when);
+ return;
+ }
+
+ for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ {
+ Bucket& bucket = mBuckets[i];
+
+ if (when < bucket.endTime)
+ {
+ bucket.accum += value;
+ }
+ else
+ {
+ U64 timeScale = sScaleTimes[i];
+
+ U64 timeSpan = when - mLastTime;
+ // how long is this value for
+ U64 timeLeft = when - bucket.endTime;
+ // how much time is left after filling this bucket
+
+ if (timeLeft < timeScale)
+ {
+ F64 valueLeft = value * timeLeft / timeSpan;
+
+ bucket.lastValid = TRUE;
+ bucket.lastAccum = bucket.accum + (value - valueLeft);
+ bucket.accum = valueLeft;
+ bucket.endTime += timeScale;
+ }
+ else
+ {
+ U64 timeTail = timeLeft % timeScale;
+
+ bucket.lastValid = TRUE;
+ bucket.lastAccum = value * timeScale / timeSpan;
+ bucket.accum = value * timeTail / timeSpan;
+ bucket.endTime += (timeLeft - timeTail) + timeScale;
+ }
+ }
+ }
+
+ mLastTime = when;
+}
+
+
+F32 LLStatAccum::impl::meanValue(TimeScale scale) const
+{
+ if (!mRunning)
+ {
+ return 0.0;
+ }
+ if (scale < 0 || scale >= IMPL_NUM_SCALES)
+ {
+ llwarns << "llStatAccum::meanValue called for unsupported scale: "
+ << scale << llendl;
+ return 0.0;
+ }
+
+ const Bucket& bucket = mBuckets[scale];
+
+ F64 value = bucket.accum;
+ U64 timeLeft = bucket.endTime - mLastTime;
+ U64 scaleTime = sScaleTimes[scale];
+
+ if (bucket.lastValid)
+ {
+ value += bucket.lastAccum * timeLeft / scaleTime;
+ }
+ else if (timeLeft < scaleTime)
+ {
+ value *= scaleTime / (scaleTime - timeLeft);
+ }
+ else
+ {
+ value = 0.0;
+ }
+
+ return (F32)(value / scaleTime);
+}
+
+
+U64 LLStatAccum::impl::getCurrentUsecs() const
+{
+ if (mUseFrameTimer)
+ {
+ return LLFrameTimer::getTotalTime();
+ }
+ else
+ {
+ return totalTime();
+ }
+}
+
+
+
+
+
+LLStatAccum::LLStatAccum(bool useFrameTimer)
+ : m(* new impl(useFrameTimer))
+{
+}
+
+LLStatAccum::~LLStatAccum()
+{
+ delete &m;
+}
+
+F32 LLStatAccum::meanValue(TimeScale scale) const
+{
+ return m.meanValue(scale);
+}
+
+
+
+LLStatMeasure::LLStatMeasure(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatMeasure::sample(F64 value)
+{
+ U64 when = m.getCurrentUsecs();
+
+ if (m.mLastSampleValid)
+ {
+ F64 avgValue = (value + m.mLastSampleValue) / 2.0;
+ F64 interval = (F64)(when - m.mLastTime);
+
+ m.sum(avgValue * interval, when);
+ }
+ else
+ {
+ m.reset(when);
+ }
+
+ m.mLastSampleValid = TRUE;
+ m.mLastSampleValue = value;
+}
+
+
+LLStatRate::LLStatRate(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatRate::count(U32 value)
+{
+ m.sum((F64)value * impl::sScaleTimes[SCALE_SECOND]);
+}
+
+
+LLStatTime::LLStatTime(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatTime::start()
+{
+ m.sum(0.0);
+}
+
+void LLStatTime::stop()
+{
+ U64 endTime = m.getCurrentUsecs();
+ m.sum((F64)(endTime - m.mLastTime), endTime);
+}
+
+
+
+LLTimer LLStat::sTimer;
+LLFrameTimer LLStat::sFrameTimer;
+
+LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
+{
+ llassert(num_bins > 0);
+ U32 i;
+ mUseFrameTimer = use_frame_timer;
+ mNumValues = 0;
+ mLastValue = 0.f;
+ mLastTime = 0.f;
+ mNumBins = num_bins;
+ mCurBin = (mNumBins-1);
+ mNextBin = 0;
+ mBins = new F32[mNumBins];
+ mBeginTime = new F64[mNumBins];
+ mTime = new F64[mNumBins];
+ mDT = new F32[mNumBins];
+ for (i = 0; i < mNumBins; i++)
+ {
+ mBins[i] = 0.f;
+ mBeginTime[i] = 0.0;
+ mTime[i] = 0.0;
+ mDT[i] = 0.f;
+ }
+}
+
+LLStat::~LLStat()
+{
+ delete[] mBins;
+ delete[] mBeginTime;
+ delete[] mTime;
+ delete[] mDT;
+}
+
+void LLStat::reset()
+{
+ U32 i;
+
+ mNumValues = 0;
+ mLastValue = 0.f;
+ mCurBin = (mNumBins-1);
+ delete[] mBins;
+ delete[] mBeginTime;
+ delete[] mTime;
+ delete[] mDT;
+ mBins = new F32[mNumBins];
+ mBeginTime = new F64[mNumBins];
+ mTime = new F64[mNumBins];
+ mDT = new F32[mNumBins];
+ for (i = 0; i < mNumBins; i++)
+ {
+ mBins[i] = 0.f;
+ mBeginTime[i] = 0.0;
+ mTime[i] = 0.0;
+ mDT[i] = 0.f;
+ }
+}
+
+void LLStat::setBeginTime(const F64 time)
+{
+ mBeginTime[mNextBin] = time;
+}
+
+void LLStat::addValueTime(const F64 time, const F32 value)
+{
+ if (mNumValues < mNumBins)
+ {
+ mNumValues++;
+ }
+
+ // Increment the bin counters.
+ mCurBin++;
+ if ((U32)mCurBin == mNumBins)
+ {
+ mCurBin = 0;
+ }
+ mNextBin++;
+ if ((U32)mNextBin == mNumBins)
+ {
+ mNextBin = 0;
+ }
+
+ mBins[mCurBin] = value;
+ mTime[mCurBin] = time;
+ mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
+ //this value is used to prime the min/max calls
+ mLastTime = mTime[mCurBin];
+ mLastValue = value;
+
+ // Set the begin time for the next stat segment.
+ mBeginTime[mNextBin] = mTime[mCurBin];
+ mTime[mNextBin] = mTime[mCurBin];
+ mDT[mNextBin] = 0.f;
+}
+
+void LLStat::start()
+{
+ if (mUseFrameTimer)
+ {
+ mBeginTime[mNextBin] = sFrameTimer.getElapsedSeconds();
+ }
+ else
+ {
+ mBeginTime[mNextBin] = sTimer.getElapsedTimeF64();
+ }
+}
+
+void LLStat::addValue(const F32 value)
+{
+ if (mNumValues < mNumBins)
+ {
+ mNumValues++;
+ }
+
+ // Increment the bin counters.
+ mCurBin++;
+ if ((U32)mCurBin == mNumBins)
+ {
+ mCurBin = 0;
+ }
+ mNextBin++;
+ if ((U32)mNextBin == mNumBins)
+ {
+ mNextBin = 0;
+ }
+
+ mBins[mCurBin] = value;
+ if (mUseFrameTimer)
+ {
+ mTime[mCurBin] = sFrameTimer.getElapsedSeconds();
+ }
+ else
+ {
+ mTime[mCurBin] = sTimer.getElapsedTimeF64();
+ }
+ mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
+
+ //this value is used to prime the min/max calls
+ mLastTime = mTime[mCurBin];
+ mLastValue = value;
+
+ // Set the begin time for the next stat segment.
+ mBeginTime[mNextBin] = mTime[mCurBin];
+ mTime[mNextBin] = mTime[mCurBin];
+ mDT[mNextBin] = 0.f;
+}
+
+
+F32 LLStat::getMax() const
+{
+ U32 i;
+ F32 current_max = mLastValue;
+ if (mNumBins == 0)
+ {
+ current_max = 0.f;
+ }
+ else
+ {
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ if (mBins[i] > current_max)
+ {
+ current_max = mBins[i];
+ }
+ }
+ }
+ return current_max;
+}
+
+F32 LLStat::getMean() const
+{
+ U32 i;
+ F32 current_mean = 0.f;
+ U32 samples = 0;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ current_mean += mBins[i];
+ samples++;
+ }
+
+ // There will be a wrap error at 2^32. :)
+ if (samples != 0)
+ {
+ current_mean /= samples;
+ }
+ else
+ {
+ current_mean = 0.f;
+ }
+ return current_mean;
+}
+
+F32 LLStat::getMin() const
+{
+ U32 i;
+ F32 current_min = mLastValue;
+
+ if (mNumBins == 0)
+ {
+ current_min = 0.f;
+ }
+ else
+ {
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ if (mBins[i] < current_min)
+ {
+ current_min = mBins[i];
+ }
+ }
+ }
+ return current_min;
+}
+
+F32 LLStat::getSum() const
+{
+ U32 i;
+ F32 sum = 0.f;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ sum += mBins[i];
+ }
+
+ return sum;
+}
+
+F32 LLStat::getSumDuration() const
+{
+ U32 i;
+ F32 sum = 0.f;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ sum += mDT[i];
+ }
+
+ return sum;
+}
+
+F32 LLStat::getPrev(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+ return mBins[bin];
+}
+
+F32 LLStat::getPrevPerSec(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+ return mBins[bin] / mDT[bin];
+}
+
+F64 LLStat::getPrevBeginTime(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+
+ return mBeginTime[bin];
+}
+
+F64 LLStat::getPrevTime(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+
+ return mTime[bin];
+}
+
+F32 LLStat::getBin(S32 bin) const
+{
+ return mBins[bin];
+}
+
+F32 LLStat::getBinPerSec(S32 bin) const
+{
+ return mBins[bin] / mDT[bin];
+}
+
+F64 LLStat::getBinBeginTime(S32 bin) const
+{
+ return mBeginTime[bin];
+}
+
+F64 LLStat::getBinTime(S32 bin) const
+{
+ return mTime[bin];
+}
+
+F32 LLStat::getCurrent() const
+{
+ return mBins[mCurBin];
+}
+
+F32 LLStat::getCurrentPerSec() const
+{
+ return mBins[mCurBin] / mDT[mCurBin];
+}
+
+F64 LLStat::getCurrentBeginTime() const
+{
+ return mBeginTime[mCurBin];
+}
+
+F64 LLStat::getCurrentTime() const
+{
+ return mTime[mCurBin];
+}
+
+F32 LLStat::getCurrentDuration() const
+{
+ return mDT[mCurBin];
+}
+
+F32 LLStat::getMeanPerSec() const
+{
+ U32 i;
+ F32 value = 0.f;
+ F32 dt = 0.f;
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value += mBins[i];
+ dt += mDT[i];
+ }
+
+ if (dt > 0.f)
+ {
+ return value/dt;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLStat::getMeanDuration() const
+{
+ F32 dur = 0.0f;
+ U32 count = 0;
+ for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ dur += mDT[i];
+ count++;
+ }
+
+ if (count > 0)
+ {
+ dur /= F32(count);
+ return dur;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLStat::getMaxPerSec() const
+{
+ U32 i;
+ F32 value;
+
+ if (mNextBin != 0)
+ {
+ value = mBins[0]/mDT[0];
+ }
+ else if (mNumValues > 0)
+ {
+ value = mBins[1]/mDT[1];
+ }
+ else
+ {
+ value = 0.f;
+ }
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value = llmax(value, mBins[i]/mDT[i]);
+ }
+ return value;
+}
+
+F32 LLStat::getMinPerSec() const
+{
+ U32 i;
+ F32 value;
+
+ if (mNextBin != 0)
+ {
+ value = mBins[0]/mDT[0];
+ }
+ else if (mNumValues > 0)
+ {
+ value = mBins[1]/mDT[1];
+ }
+ else
+ {
+ value = 0.f;
+ }
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value = llmin(value, mBins[i]/mDT[i]);
+ }
+ return value;
+}
+
+F32 LLStat::getMinDuration() const
+{
+ F32 dur = 0.0f;
+ for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ dur = llmin(dur, mDT[i]);
+ }
+ return dur;
+}
+
+U32 LLStat::getNumValues() const
+{
+ return mNumValues;
+}
+
+S32 LLStat::getNumBins() const
+{
+ return mNumBins;
+}
+
+S32 LLStat::getCurBin() const
+{
+ return mCurBin;
+}
+
+S32 LLStat::getNextBin() const
+{
+ return mNextBin;
+}
+
+F64 LLStat::getLastTime() const
+{
+ return mLastTime;
+}
diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h
new file mode 100644
index 0000000000..4c86557365
--- /dev/null
+++ b/indra/llcommon/llstat.h
@@ -0,0 +1,182 @@
+/**
+ * @file llstat.h
+ * @brief Runtime statistics accumulation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTAT_H
+#define LL_LLSTAT_H
+
+#include <deque>
+
+#include "lltimer.h"
+#include "llframetimer.h"
+
+//
+// Accumulates statistics for an arbitrary length of time.
+// Does this by maintaining a chain of accumulators, each one
+// accumulation the results of the parent. Can scale to arbitrary
+// amounts of time with very low memory cost.
+//
+
+class LLStatAccum
+{
+protected:
+ LLStatAccum(bool use_frame_timer);
+ virtual ~LLStatAccum();
+
+public:
+ enum TimeScale {
+ SCALE_SECOND,
+ SCALE_MINUTE,
+ SCALE_TWO_MINUTE,
+ SCALE_HOUR,
+ SCALE_DAY,
+ SCALE_WEEK,
+
+ NUM_SCALES
+ };
+
+ F32 meanValue(TimeScale scale) const;
+ // see the subclasses for the specific meaning of value
+
+ F32 meanValueOverLastSecond() const { return meanValue(SCALE_SECOND); }
+ F32 meanValueOverLastMinute() const { return meanValue(SCALE_MINUTE); }
+
+protected:
+ class impl;
+ impl& m;
+};
+
+class LLStatMeasure : public LLStatAccum
+ // gathers statistics about things that are measured
+ // ex.: tempature, time dilation
+{
+public:
+ LLStatMeasure(bool use_frame_timer = true);
+
+ void sample(F64);
+ void sample(S32 v) { sample((F64)v); }
+ void sample(U32 v) { sample((F64)v); }
+ void sample(S64 v) { sample((F64)v); }
+ void sample(U64 v) { sample((F64)v); }
+};
+
+
+class LLStatRate : public LLStatAccum
+ // gathers statistics about things that can be counted over time
+ // ex.: LSL instructions executed, messages sent, simulator frames completed
+ // renders it in terms of rate of thing per second
+{
+public:
+ LLStatRate(bool use_frame_timer = true);
+
+ void count(U32);
+ // used to note that n items have occured
+
+ void mark() { count(1); }
+ // used for counting the rate thorugh a point in the code
+};
+
+
+class LLTimeBlock;
+
+class LLStatTime : public LLStatAccum
+ // gathers statistics about time spent in a block of code
+ // measure average duration per second in the block
+{
+public:
+ LLStatTime(bool use_frame_timer = false);
+
+private:
+ void start();
+ void stop();
+ friend class LLTimeBlock;
+};
+
+class LLTimeBlock
+{
+public:
+ LLTimeBlock(LLStatTime& stat) : mStat(stat) { mStat.start(); }
+ ~LLTimeBlock() { mStat.stop(); }
+private:
+ LLStatTime& mStat;
+};
+
+
+
+
+
+class LLStat
+{
+public:
+ LLStat(const U32 num_bins = 32, BOOL use_frame_timer = FALSE);
+ ~LLStat();
+
+ void reset();
+
+ void start(); // Start the timer for the current "frame", otherwise uses the time tracked from
+ // the last addValue
+ void addValue(const F32 value = 1.f); // Adds the current value being tracked, and tracks the DT.
+ void addValue(const S32 value) { addValue((F32)value); }
+ void addValue(const U32 value) { addValue((F32)value); }
+
+ void setBeginTime(const F64 time);
+ void addValueTime(const F64 time, const F32 value = 1.f);
+
+ S32 getCurBin() const;
+ S32 getNextBin() const;
+
+ F32 getCurrent() const;
+ F32 getCurrentPerSec() const;
+ F64 getCurrentBeginTime() const;
+ F64 getCurrentTime() const;
+ F32 getCurrentDuration() const;
+
+ F32 getPrev(S32 age) const; // Age is how many "addValues" previously - zero is current
+ F32 getPrevPerSec(S32 age) const; // Age is how many "addValues" previously - zero is current
+ F64 getPrevBeginTime(S32 age) const;
+ F64 getPrevTime(S32 age) const;
+
+ F32 getBin(S32 bin) const;
+ F32 getBinPerSec(S32 bin) const;
+ F64 getBinBeginTime(S32 bin) const;
+ F64 getBinTime(S32 bin) const;
+
+ F32 getMax() const;
+ F32 getMaxPerSec() const;
+
+ F32 getMean() const;
+ F32 getMeanPerSec() const;
+ F32 getMeanDuration() const;
+
+ F32 getMin() const;
+ F32 getMinPerSec() const;
+ F32 getMinDuration() const;
+
+ F32 getSum() const;
+ F32 getSumDuration() const;
+
+ U32 getNumValues() const;
+ S32 getNumBins() const;
+
+ F64 getLastTime() const;
+private:
+ BOOL mUseFrameTimer;
+ U32 mNumValues;
+ U32 mNumBins;
+ F32 mLastValue;
+ F64 mLastTime;
+ F32 *mBins;
+ F64 *mBeginTime;
+ F64 *mTime;
+ F32 *mDT;
+ S32 mCurBin;
+ S32 mNextBin;
+ static LLTimer sTimer;
+ static LLFrameTimer sFrameTimer;
+};
+
+#endif // LL_STAT_
diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h
new file mode 100644
index 0000000000..07c6e0bf52
--- /dev/null
+++ b/indra/llcommon/llstatenums.h
@@ -0,0 +1,40 @@
+/**
+ * @file llstatenums.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATENUMS_H
+#define LL_LLSTATENUMS_H
+
+enum
+{
+ LL_SIM_STAT_TIME_DILATION,
+ LL_SIM_STAT_FPS,
+ LL_SIM_STAT_PHYSFPS,
+ LL_SIM_STAT_AGENTUPS,
+ LL_SIM_STAT_FRAMEMS,
+ LL_SIM_STAT_NETMS,
+ LL_SIM_STAT_SIMOTHERMS,
+ LL_SIM_STAT_SIMPHYSICSMS,
+ LL_SIM_STAT_AGENTMS,
+ LL_SIM_STAT_IMAGESMS,
+ LL_SIM_STAT_SCRIPTMS,
+ LL_SIM_STAT_NUMTASKS,
+ LL_SIM_STAT_NUMTASKSACTIVE,
+ LL_SIM_STAT_NUMAGENTMAIN,
+ LL_SIM_STAT_NUMAGENTCHILD,
+ LL_SIM_STAT_NUMSCRIPTSACTIVE,
+ LL_SIM_STAT_LSLIPS,
+ LL_SIM_STAT_INPPS,
+ LL_SIM_STAT_OUTPPS,
+ LL_SIM_STAT_PENDING_DOWNLOADS,
+ LL_SIM_STAT_PENDING_UPLOADS,
+ LL_SIM_STAT_VIRTUAL_SIZE_KB,
+ LL_SIM_STAT_RESIDENT_SIZE_KB,
+ LL_SIM_STAT_PENDING_LOCAL_UPLOADS,
+ LL_SIM_STAT_TOTAL_UNACKED_BYTES
+};
+
+#endif
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
new file mode 100644
index 0000000000..61d83f7259
--- /dev/null
+++ b/indra/llcommon/llstl.h
@@ -0,0 +1,452 @@
+/**
+ * @file llstl.h
+ * @brief helper object & functions for use with the stl.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTL_H
+#define LL_LLSTL_H
+
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <vector>
+#include <set>
+#include <deque>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+// Use to compare the first element only of a pair
+// e.g. typedef std::set<std::pair<int, Data*>, compare_pair<int, Data*> > some_pair_set_t;
+template <typename T1, typename T2>
+struct compare_pair_first
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ return a.first < b.first;
+ }
+};
+
+template <typename T1, typename T2>
+struct compare_pair_greater
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ if (!(a.first < b.first))
+ return true;
+ else if (!(b.first < a.first))
+ return false;
+ else
+ return !(a.second < b.second);
+ }
+};
+
+// Use to compare the contents of two pointers (e.g. std::string*)
+template <typename T>
+struct compare_pointer_contents
+{
+ typedef const T* Tptr;
+ bool operator()(const Tptr& a, const Tptr& b) const
+ {
+ return *a < *b;
+ }
+};
+
+// DeletePointer is a simple helper for deleting all pointers in a container.
+// The general form is:
+//
+// std::for_each(cont.begin(), cont.end(), DeletePointer());
+
+struct DeletePointer
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete ptr;
+ }
+};
+struct DeletePointerArray
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete[] ptr;
+ }
+};
+
+// DeletePointer is a simple helper for deleting all pointers in a map.
+// The general form is:
+//
+// std::for_each(somemap.begin(), somemap.end(), DeletePairedPointer());
+
+struct DeletePairedPointer
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete ptr.second;
+ }
+};
+struct DeletePairedPointerArray
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete[] ptr.second;
+ }
+};
+
+
+// Alternate version of the above so that has a more cumbersome
+// syntax, but it can be used with compositional functors. *FIX: The
+// functor retuns a bool because msdev bombs during the composition if
+// you return void. Once we upgrade to a newer compiler, the second
+// unary_function template parameter can be set to void.
+//
+// Here's a snippit showing how you use this object:
+//
+// typedef std::map<int, widget*> map_type;
+// map_type widget_map;
+// ... // add elements
+// // delete them all
+// for_each(widget_map.begin(),
+// widget_map.end(),
+// llcompose1(DeletePointerFunctor<widget>(),
+// llselect2nd<map_type::value_type>()));
+
+template<typename T>
+struct DeletePointerFunctor : public std::unary_function<T*, bool>
+{
+ bool operator()(T* ptr) const
+ {
+ delete ptr;
+ return true;
+ }
+};
+
+// See notes about DeleteArray for why you should consider avoiding this.
+template<typename T>
+struct DeleteArrayFunctor : public std::unary_function<T*, bool>
+{
+ bool operator()(T* ptr) const
+ {
+ delete[] ptr;
+ return true;
+ }
+};
+
+// CopyNewPointer is a simple helper which accepts a pointer, and
+// returns a new pointer built with the copy constructor. Example:
+//
+// transform(in.begin(), in.end(), out.end(), CopyNewPointer());
+
+struct CopyNewPointer
+{
+ template<typename T> T* operator()(const T* ptr) const
+ {
+ return new T(*ptr);
+ }
+};
+
+// Simple function to help with finding pointers in maps.
+// For example:
+// typedef map_t;
+// std::map<int, const char*> foo;
+// foo[18] = "there";
+// foo[2] = "hello";
+// const char* bar = get_ptr_in_map(foo, 2); // bar -> "hello"
+// const char* baz = get_ptr_in_map(foo, 3); // baz == NULL
+template <typename K, typename T>
+inline T* get_ptr_in_map(const std::map<K,T*>& inmap, const K& key)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T*>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// helper function which returns true if key is in inmap.
+template <typename K, typename T>
+inline bool is_in_map(const std::map<K,T>& inmap, const K& key)
+{
+ typedef typename std::map<K,T>::const_iterator map_iter;
+ if(inmap.find(key) == inmap.end())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+// Similar to get_ptr_in_map, but for any type with a valid T(0) constructor.
+// To replace LLSkipMap getIfThere, use:
+// get_if_there(map, key, 0)
+// WARNING: Make sure default_value (generally 0) is not a valid map entry!
+template <typename K, typename T>
+inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return default_value;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// Useful for replacing the removeObj() functionality of LLDynamicArray
+// Example:
+// for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); )
+// {
+// if ((*iter)->isMarkedForRemoval())
+// iter = vector_replace_with_last(mList, iter);
+// else
+// ++iter;
+// }
+template <typename T, typename Iter>
+inline Iter vector_replace_with_last(std::vector<T>& invec, Iter iter)
+{
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ if (iter == invec.end())
+ {
+ return iter;
+ }
+ else if (iter == last)
+ {
+ invec.pop_back();
+ return invec.end();
+ }
+ else
+ {
+ *iter = *last;
+ invec.pop_back();
+ return iter;
+ }
+};
+
+// Useful for replacing the removeObj() functionality of LLDynamicArray
+// Example:
+// vector_replace_with_last(mList, x);
+template <typename T>
+inline bool vector_replace_with_last(std::vector<T>& invec, const T& val)
+{
+ typename std::vector<T>::iterator iter = std::find(invec.begin(), invec.end(), val);
+ if (iter != invec.end())
+ {
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ *iter = *last;
+ invec.pop_back();
+ return true;
+ }
+ return false;
+}
+
+// Append N elements to the vector and return a pointer to the first new element.
+template <typename T>
+inline T* vector_append(std::vector<T>& invec, S32 N)
+{
+ U32 sz = invec.size();
+ invec.resize(sz+N);
+ return &(invec[sz]);
+}
+
+// call function f to n members starting at first. similar to std::for_each
+template <class InputIter, class Size, class Function>
+Function ll_for_n(InputIter first, Size n, Function f)
+{
+ for ( ; n > 0; --n, ++first)
+ f(*first);
+ return f;
+}
+
+// copy first to result n times, incrementing each as we go
+template <class InputIter, class Size, class OutputIter>
+OutputIter ll_copy_n(InputIter first, Size n, OutputIter result)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = *first;
+ return result;
+}
+
+// set *result = op(*f) for n elements of f
+template <class InputIter, class OutputIter, class Size, class UnaryOp>
+OutputIter ll_transform_n(
+ InputIter first,
+ Size n,
+ OutputIter result,
+ UnaryOp op)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = op(*first);
+ return result;
+}
+
+
+
+/*
+ *
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+
+// helper to deal with the fact that MSDev does not package
+// select... with the stl. Look up usage on the sgi website.
+
+template <class _Pair>
+struct _LLSelect1st : public std::unary_function<_Pair, typename _Pair::first_type> {
+ const typename _Pair::first_type& operator()(const _Pair& __x) const {
+ return __x.first;
+ }
+};
+
+template <class _Pair>
+struct _LLSelect2nd : public std::unary_function<_Pair, typename _Pair::second_type>
+{
+ const typename _Pair::second_type& operator()(const _Pair& __x) const {
+ return __x.second;
+ }
+};
+
+template <class _Pair> struct llselect1st : public _LLSelect1st<_Pair> {};
+template <class _Pair> struct llselect2nd : public _LLSelect2nd<_Pair> {};
+
+// helper to deal with the fact that MSDev does not package
+// compose... with the stl. Look up usage on the sgi website.
+
+template <class _Operation1, class _Operation2>
+class ll_unary_compose :
+ public std::unary_function<typename _Operation2::argument_type,
+ typename _Operation1::result_type>
+{
+protected:
+ _Operation1 __op1;
+ _Operation2 __op2;
+public:
+ ll_unary_compose(const _Operation1& __x, const _Operation2& __y)
+ : __op1(__x), __op2(__y) {}
+ typename _Operation1::result_type
+ operator()(const typename _Operation2::argument_type& __x) const {
+ return __op1(__op2(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2>
+inline ll_unary_compose<_Operation1,_Operation2>
+llcompose1(const _Operation1& __op1, const _Operation2& __op2)
+{
+ return ll_unary_compose<_Operation1,_Operation2>(__op1, __op2);
+}
+
+template <class _Operation1, class _Operation2, class _Operation3>
+class ll_binary_compose
+ : public std::unary_function<typename _Operation2::argument_type,
+ typename _Operation1::result_type> {
+protected:
+ _Operation1 _M_op1;
+ _Operation2 _M_op2;
+ _Operation3 _M_op3;
+public:
+ ll_binary_compose(const _Operation1& __x, const _Operation2& __y,
+ const _Operation3& __z)
+ : _M_op1(__x), _M_op2(__y), _M_op3(__z) { }
+ typename _Operation1::result_type
+ operator()(const typename _Operation2::argument_type& __x) const {
+ return _M_op1(_M_op2(__x), _M_op3(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2, class _Operation3>
+inline ll_binary_compose<_Operation1, _Operation2, _Operation3>
+llcompose2(const _Operation1& __op1, const _Operation2& __op2,
+ const _Operation3& __op3)
+{
+ return ll_binary_compose<_Operation1,_Operation2,_Operation3>
+ (__op1, __op2, __op3);
+}
+
+// helpers to deal with the fact that MSDev does not package
+// bind... with the stl. Again, this is from sgi.
+template <class _Operation>
+class llbinder1st :
+ public std::unary_function<typename _Operation::second_argument_type,
+ typename _Operation::result_type> {
+protected:
+ _Operation op;
+ typename _Operation::first_argument_type value;
+public:
+ llbinder1st(const _Operation& __x,
+ const typename _Operation::first_argument_type& __y)
+ : op(__x), value(__y) {}
+ typename _Operation::result_type
+ operator()(const typename _Operation::second_argument_type& __x) const {
+ return op(value, __x);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline llbinder1st<_Operation>
+llbind1st(const _Operation& __oper, const _Tp& __x)
+{
+ typedef typename _Operation::first_argument_type _Arg1_type;
+ return llbinder1st<_Operation>(__oper, _Arg1_type(__x));
+}
+
+template <class _Operation>
+class llbinder2nd
+ : public std::unary_function<typename _Operation::first_argument_type,
+ typename _Operation::result_type> {
+protected:
+ _Operation op;
+ typename _Operation::second_argument_type value;
+public:
+ llbinder2nd(const _Operation& __x,
+ const typename _Operation::second_argument_type& __y)
+ : op(__x), value(__y) {}
+ typename _Operation::result_type
+ operator()(const typename _Operation::first_argument_type& __x) const {
+ return op(__x, value);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline llbinder2nd<_Operation>
+llbind2nd(const _Operation& __oper, const _Tp& __x)
+{
+ typedef typename _Operation::second_argument_type _Arg2_type;
+ return llbinder2nd<_Operation>(__oper, _Arg2_type(__x));
+}
+
+#endif // LL_LLSTL_H
diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp
new file mode 100644
index 0000000000..f9038afedc
--- /dev/null
+++ b/indra/llcommon/llstreamtools.cpp
@@ -0,0 +1,551 @@
+/**
+ * @file llstreamtools.cpp
+ * @brief some helper functions for parsing legacy simstate and asset files.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iostream>
+#include <string>
+
+#include "llstreamtools.h"
+
+
+// ----------------------------------------------------------------------------
+// some std::istream helper functions
+// ----------------------------------------------------------------------------
+
+// skips spaces and tabs
+bool skip_whitespace(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while (('\t' == c || ' ' == c) && input_stream.good())
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// skips whitespace, newlines, and carriage returns
+bool skip_emptyspace(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while ( input_stream.good()
+ && ('\t' == c || ' ' == c || '\n' == c || '\r' == c) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// skips emptyspace and lines that start with a #
+bool skip_comments_and_emptyspace(std::istream& input_stream)
+{
+ while (skip_emptyspace(input_stream))
+ {
+ char c = input_stream.peek();
+ if ('#' == c )
+ {
+ while ('\n' != c && input_stream.good())
+ {
+ c = input_stream.get();
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ return input_stream.good();
+}
+
+bool skip_line(std::istream& input_stream)
+{
+ char c;
+ do
+ {
+ c = input_stream.get();
+ } while ('\n' != c && input_stream.good());
+ return input_stream.good();
+}
+
+bool skip_to_next_word(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while ( input_stream.good()
+ && ( (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || '_' == c ) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ while ( input_stream.good()
+ && !( (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || '_' == c ) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream)
+{
+ int key_length = strlen(keyword); /*Flawfinder: ignore*/
+ if (0 == key_length)
+ {
+ return false;
+ }
+ while (input_stream.good())
+ {
+ skip_emptyspace(input_stream);
+ char c = input_stream.get();
+ if (keyword[0] != c)
+ {
+ skip_line(input_stream);
+ }
+ else
+ {
+ int key_index = 1;
+ while ( key_index < key_length
+ && keyword[key_index - 1] == c
+ && input_stream.good())
+ {
+ key_index++;
+ c = input_stream.get();
+ }
+
+ if (key_index == key_length
+ && keyword[key_index-1] == c)
+ {
+ c = input_stream.peek();
+ if (' ' == c || '\t' == c || '\r' == c || '\n' == c)
+ {
+ return true;
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ }
+ return false;
+}
+
+/* skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug in windows iostream
+bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stream)
+{
+ int key_length = strlen(keyword);
+ if (0 == key_length)
+ {
+ return false;
+ }
+ while (input_stream.good())
+ {
+ skip_emptyspace(input_stream);
+ char c = input_stream.get();
+ if (keyword[0] != c)
+ {
+ skip_line(input_stream);
+ }
+ else
+ {
+ int key_index = 1;
+ while ( key_index < key_length
+ && keyword[key_index - 1] == c
+ && input_stream.good())
+ {
+ key_index++;
+ c = input_stream.get();
+ }
+
+ if (key_index == key_length
+ && keyword[key_index-1] == c)
+ {
+ c = input_stream.peek();
+ if (' ' == c || '\t' == c || '\r' == c || '\n' == c)
+ {
+ // put the keyword back onto the stream
+ for (int index = key_length - 1; index >= 0; index--)
+ {
+ input_stream.putback(keyword[index]);
+ }
+ return true;
+ }
+ else
+ {
+ skip_line(input_stream);
+ break;
+ }
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ }
+ return false;
+}
+*/
+
+bool get_word(std::string& output_string, std::istream& input_stream)
+{
+ skip_emptyspace(input_stream);
+ char c = input_stream.peek();
+ while ( !isspace(c)
+ && '\n' != c
+ && '\r' != c
+ && input_stream.good() )
+ {
+ output_string += c;
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+bool get_word(std::string& output_string, std::istream& input_stream, int n)
+{
+ skip_emptyspace(input_stream);
+ int char_count = 0;
+ char c = input_stream.peek();
+ while (!isspace(c)
+ && '\n' != c
+ && '\r' != c
+ && input_stream.good()
+ && char_count < n)
+ {
+ char_count++;
+ output_string += c;
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// get everything up to and including the next newline
+bool get_line(std::string& output_string, std::istream& input_stream)
+{
+ char c = input_stream.get();
+ while (input_stream.good())
+ {
+ if ('\r' == c)
+ {
+ // skip carriage returns
+ }
+ else
+ {
+ output_string += c;
+ if ('\n' == c)
+ {
+ break;
+ }
+ }
+ c = input_stream.get();
+ }
+ return input_stream.good();
+}
+
+// get everything up to and including the next newline
+// up to the next n characters.
+// add a newline on the end if bail before actual line ending
+bool get_line(std::string& output_string, std::istream& input_stream, int n)
+{
+ int char_count = 0;
+ char c = input_stream.get();
+ while (input_stream.good() && char_count < n)
+ {
+ char_count++;
+ output_string += c;
+ if ('\r' == c)
+ {
+ // skip carriage returns
+ }
+ else
+ {
+ if ('\n' == c)
+ {
+ break;
+ }
+ if (char_count >= n)
+ {
+ output_string.append("\n");
+ break;
+ }
+ }
+ c = input_stream.get();
+ }
+ return input_stream.good();
+}
+
+/* disabled -- might tickle bug in windows iostream
+// backs up the input_stream by line_size + 1 characters
+bool unget_line(const std::string& line, std::istream& input_stream)
+{
+ input_stream.putback('\n'); // unget the newline
+ for (int line_index = line.size()-1; line_index >= 0; line_index--)
+ {
+ input_stream.putback(line[line_index]);
+ }
+ return input_stream.good();
+}
+*/
+
+// removes the last char in 'line' if it matches 'c'
+// returns true if removed last char
+bool remove_last_char(char c, std::string& line)
+{
+ int line_size = line.size();
+ if (line_size > 1
+ && c == line[line_size - 1])
+ {
+ line.replace(line_size - 1, 1, "");
+ return true;
+ }
+ return false;
+}
+
+// replaces escaped characters with the correct characters from left to right
+// "\\\\" ---> '\\' (two backslahes become one)
+// "\\n" ---> '\n' (backslash n becomes carriage return)
+void unescape_string(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size - 1)
+ {
+ if ('\\' == line[index])
+ {
+ if ('\\' == line[index + 1])
+ {
+ line.replace(index, 2, "\\");
+ line_size--;
+ }
+ else if ('n' == line[index + 1])
+ {
+ line.replace(index, 2, "\n");
+ line_size--;
+ }
+ }
+ index++;
+ }
+}
+
+// replaces unescaped characters with expanded equivalents from left to right
+// '\\' ---> "\\\\" (one backslash becomes two)
+// '\n' ---> "\\n" (carriage return becomes backslash n)
+void escape_string(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size)
+ {
+ if ('\\' == line[index])
+ {
+ line.replace(index, 1, "\\\\");
+ line_size++;
+ index++;
+ }
+ else if ('\n' == line[index])
+ {
+ line.replace(index, 1, "\\n");
+ line_size++;
+ index++;
+ }
+ index++;
+ }
+}
+
+// removes '\n' characters
+void replace_newlines_with_whitespace(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size)
+ {
+ if ('\n' == line[index])
+ {
+ line.replace(index, 1, " ");
+ }
+ index++;
+ }
+}
+
+// returns 1 for solitary "{"
+// returns -1 for solitary "}"
+// otherwise returns 0
+int get_brace_count(const std::string& line)
+{
+ int index = 0;
+ int line_size = line.size();
+ char c = 0;
+ while (index < line_size)
+ {
+ c = line[index];
+ index++;
+ if (!isspace(c))
+ {
+ break;
+ }
+ }
+ char brace = c;
+ // make sure the rest of the line is whitespace
+ while (index < line_size)
+ {
+ c = line[index];
+ if (!isspace(c))
+ {
+ break;
+ }
+ index++;
+ }
+ if ('\n' != c)
+ {
+ return 0;
+ }
+ if ('{' == brace)
+ {
+ return 1;
+ }
+ else if ('}' == brace)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+// erases any double-quote characters in 'line'
+void remove_double_quotes(std::string& line)
+{
+ int index = 0;
+ int line_size = line.size();
+ while (index < line_size)
+ {
+ if ('"' == line[index])
+ {
+ int count = 1;
+ while (index + count < line_size
+ && '"' == line[index + count])
+ {
+ count++;
+ }
+ line.replace(index, count, "");
+ line_size -= count;
+ }
+ else
+ {
+ index++;
+ }
+ }
+}
+
+// the 'keyword' is defined as the first word on a line
+// the 'value' is everything after the keyword on the same line
+// starting at the first non-whitespace and ending right before the newline
+void get_keyword_and_value(std::string& keyword,
+ std::string& value,
+ const std::string& line)
+{
+ // skip initial whitespace
+ int line_size = line.size();
+ int line_index = 0;
+ char c;
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if (!isspace(c))
+ {
+ break;
+ }
+ line_index++;
+ }
+
+ // get the keyword
+ keyword.assign("");
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if (isspace(c) || '\r' == c || '\n' == c)
+ {
+ break;
+ }
+ keyword += c;
+ line_index++;
+ }
+
+ if (keyword.size() > 0
+ && '\r' != line[line_index]
+ && '\n' != line[line_index])
+
+ {
+ // discard initial white spaces
+ while (line_index < line_size
+ && (' ' == line[line_index]
+ || '\t' == line[line_index]) )
+ {
+ line_index++;
+ }
+
+ // get the value
+ value.assign("");
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if ('\r' == c || '\n' == c)
+ {
+ break;
+ }
+ value += c;
+ line_index++;
+ }
+ }
+}
+
+std::istream& fullread(std::istream& str, char *buf, std::streamsize requested)
+{
+ std::streamsize got;
+ std::streamsize total = 0;
+
+ str.read(buf, requested); /*Flawfinder: ignore*/
+ got = str.gcount();
+ total += got;
+ while (got && total < requested)
+ {
+ if (str.fail())
+ str.clear();
+ str.read(buf + total, requested - total); /*Flawfinder: ignore*/
+ got = str.gcount();
+ total += got;
+ }
+ return str;
+}
+
+std::istream& operator>>(std::istream& str, const char *tocheck)
+{
+ char c;
+ const char *p;
+ p = tocheck;
+ while (*p && !str.bad())
+ {
+ str.get(c);
+ if (c != *p)
+ {
+ str.setstate(std::ios::failbit); /*Flawfinder: ignore*/
+ break;
+ }
+ p++;
+ }
+ return str;
+}
diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h
new file mode 100644
index 0000000000..e4099aac57
--- /dev/null
+++ b/indra/llcommon/llstreamtools.h
@@ -0,0 +1,98 @@
+/**
+ * @file llstreamtools.h
+ * @brief some helper functions for parsing legacy simstate and asset files.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STREAM_TOOLS_H
+#define LL_STREAM_TOOLS_H
+
+#include <iostream>
+#include <string>
+
+// unless specifed otherwise these all return input_stream.good()
+
+// skips spaces and tabs
+bool skip_whitespace(std::istream& input_stream);
+
+// skips whitespace and newlines
+bool skip_emptyspace(std::istream& input_stream);
+
+// skips emptyspace and lines that start with a #
+bool skip_comments_and_emptyspace(std::istream& input_stream);
+
+// skips to character after next newline
+bool skip_line(std::istream& input_stream);
+
+// skips to beginning of next non-emptyspace
+bool skip_to_next_word(std::istream& input_stream);
+
+// skips to character after the end of next keyword
+// a 'keyword' is defined as the first word on a line
+bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream);
+
+// skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug
+// in windows iostream
+// skips to beginning of next keyword
+// a 'keyword' is defined as the first word on a line
+//bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stream);
+
+// characters are pulled out of input_stream and appended to output_string
+bool get_word(std::string& output_string, std::istream& input_stream);
+bool get_line(std::string& output_string, std::istream& input_stream);
+
+// characters are pulled out of input_stream (up to a max of 'n')
+// and appended to output_string
+bool get_word(std::string& output_string, std::istream& input_stream, int n);
+bool get_line(std::string& output_string, std::istream& input_stream, int n);
+
+// unget_line() is disabled -- might tickle corruption bug in windows iostream
+//// backs up the input_stream by line_size + 1 characters
+//bool unget_line(const std::string& line, std::istream& input_stream);
+
+// TODO -- move these string manipulator functions to a different file
+
+// removes the last char in 'line' if it matches 'c'
+// returns true if removed last char
+bool remove_last_char(char c, std::string& line);
+
+// replaces escaped characters with the correct characters from left to right
+// "\\" ---> '\\'
+// "\n" ---> '\n'
+void unescape_string(std::string& line);
+
+// replaces unescaped characters with expanded equivalents from left to right
+// '\\' ---> "\\"
+// '\n' ---> "\n"
+void escape_string(std::string& line);
+
+// replaces each '\n' character with ' '
+void replace_newlines_with_whitespace(std::string& line);
+
+// returns 1 for solitary "{"
+// returns -1 for solitary "}"
+// otherwise returns 0
+int get_brace_count(const std::string& line);
+
+// erases any double-quote characters in line
+void remove_double_quotes(std::string& line);
+
+// the 'keyword' is defined as the first word on a line
+// the 'value' is everything after the keyword on the same line
+// starting at the first non-whitespace and ending right before the newline
+void get_keyword_and_value(std::string& keyword,
+ std::string& value,
+ const std::string& line);
+
+// continue to read from the stream until you really can't
+// read anymore or until we hit the count. Some istream
+// implimentations have a max that they will read.
+std::istream& fullread(std::istream& str, char *buf, std::streamsize requested);
+
+std::istream& operator>>(std::istream& str, const char *tocheck);
+
+#endif
+
+
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
new file mode 100644
index 0000000000..0688e43940
--- /dev/null
+++ b/indra/llcommon/llstrider.h
@@ -0,0 +1,38 @@
+/**
+ * @file llstrider.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTRIDER_H
+#define LL_LLSTRIDER_H
+
+#include "stdtypes.h"
+
+template <class Object> class LLStrider
+{
+ union
+ {
+ Object* mObjectp;
+ U8* mBytep;
+ };
+ U32 mSkip;
+public:
+
+ LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); }
+ ~LLStrider() { }
+
+ const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;}
+ void setStride (S32 skipBytes) { mSkip = (skipBytes ? skipBytes : sizeof(Object));}
+
+ void skip(const U32 index) { mBytep += mSkip*index;}
+
+ Object* get() { return mObjectp; }
+ Object* operator->() { return mObjectp; }
+ Object& operator *() { return *mObjectp; }
+ Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; }
+ Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); }
+};
+
+#endif // LL_LLSTRIDER_H
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
new file mode 100644
index 0000000000..50fd881ad7
--- /dev/null
+++ b/indra/llcommon/llstring.cpp
@@ -0,0 +1,835 @@
+/**
+ * @file llstring.cpp
+ * @brief String utility functions and the LLString class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstring.h"
+#include "llerror.h"
+
+std::string ll_safe_string(const char* in)
+{
+ if(in) return std::string(in);
+ return std::string();
+}
+
+U8 hex_as_nybble(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return (U8)(hex - '0');
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return (U8)(10 + hex - 'a');
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return (U8)(10 + hex - 'A');
+ }
+ return 0; // uh - oh, not hex any more...
+}
+
+
+// See http://www.unicode.org/Public/BETA/CVTUTF-1-2/ConvertUTF.c
+// for the Unicode implementation - this doesn't match because it was written before finding
+// it.
+
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr)
+{
+ std::string utf8_str = wstring_to_utf8str(wstr);
+ s << utf8_str;
+ return s;
+}
+
+std::string rawstr_to_utf8(const std::string& raw)
+{
+ LLWString wstr(utf8str_to_wstring(raw));
+ return wstring_to_utf8str(wstr);
+}
+
+S32 wchar_to_utf8chars(llwchar in_char, char* outchars)
+{
+ U32 cur_char = (U32)in_char;
+ char* base = outchars;
+ if (cur_char < 0x80)
+ {
+ *outchars++ = (U8)cur_char;
+ }
+ else if (cur_char < 0x800)
+ {
+ *outchars++ = 0xC0 | (cur_char >> 6);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x10000)
+ {
+ *outchars++ = 0xE0 | (cur_char >> 12);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x200000)
+ {
+ *outchars++ = 0xF0 | (cur_char >> 18);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else if (cur_char < 0x4000000)
+ {
+ *outchars++ = 0xF8 | (cur_char >> 24);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else if (cur_char < 0x80000000)
+ {
+ *outchars++ = 0xFC | (cur_char >> 30);
+ *outchars++ = 0x80 | ((cur_char >> 24) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else
+ {
+ llwarns << "Invalid Unicode character " << cur_char << "!" << llendl;
+ *outchars++ = LL_UNKNOWN_CHAR;
+ }
+ return outchars - base;
+}
+
+S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
+{
+ const U16* base = inchars;
+ U16 cur_char = *inchars++;
+ llwchar char32 = cur_char;
+ if ((cur_char >= 0xD800) && (cur_char <= 0xDFFF))
+ {
+ // Surrogates
+ char32 = ((llwchar)(cur_char - 0xD800)) << 10;
+ cur_char = *inchars++;
+ char32 += (llwchar)(cur_char - 0xDC00) + 0x0010000UL;
+ }
+ else
+ {
+ char32 = (llwchar)cur_char;
+ }
+ *outchar = char32;
+ return inchars - base;
+}
+
+S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8p)
+{
+ // Get 32 bit char32
+ llwchar char32;
+ S32 nchars16 = utf16chars_to_wchar(inchars, &char32);
+ // Convert to utf8
+ S32 nchars8 = wchar_to_utf8chars(char32, outchars);
+ if (nchars8p)
+ {
+ *nchars8p = nchars8;
+ }
+ return nchars16;
+}
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len)
+{
+ llutf16string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ U32 cur_char = utf32str[i];
+ if (cur_char > 0xFFFF)
+ {
+ out += (0xD7C0 + (cur_char >> 10));
+ out += (0xDC00 | (cur_char & 0x3FF));
+ }
+ else
+ {
+ out += cur_char;
+ }
+ i++;
+ }
+ return out;
+}
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str)
+{
+ const S32 len = (S32)utf32str.length();
+ return wstring_to_utf16str(utf32str, len);
+}
+
+llutf16string utf8str_to_utf16str ( const LLString& utf8str )
+{
+ LLWString wstr = utf8str_to_wstring ( utf8str );
+ return wstring_to_utf16str ( wstr );
+}
+
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len)
+{
+ LLWString wout;
+
+ S32 i = 0;
+ // craziness to make gcc happy (llutf16string.c_str() is tweaked on linux):
+ const U16* chars16 = &(*(utf16str.begin()));
+ while (i < len)
+ {
+ llwchar cur_char;
+ i += utf16chars_to_wchar(chars16+i, &cur_char);
+ wout += cur_char;
+ }
+ return wout;
+}
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str)
+{
+ const S32 len = (S32)utf16str.length();
+ return utf16str_to_wstring(utf16str, len);
+}
+
+S32 wchar_utf8_length(const llwchar wc)
+{
+ if (wc < 0x80)
+ {
+ // This case will also catch negative values which are
+ // technically invalid.
+ return 1;
+ }
+ else if (wc < 0x800)
+ {
+ return 2;
+ }
+ else if (wc < 0x10000)
+ {
+ return 3;
+ }
+ else if (wc < 0x200000)
+ {
+ return 4;
+ }
+ else if (wc < 0x4000000)
+ {
+ return 5;
+ }
+ else
+ {
+ return 6;
+ }
+}
+
+
+S32 wstring_utf8_length(const LLWString& wstr)
+{
+ S32 len = 0;
+ for (S32 i = 0; i < (S32)wstr.length(); i++)
+ {
+ len += wchar_utf8_length(wstr[i]);
+ }
+ return len;
+}
+
+
+LLWString utf8str_to_wstring(const std::string& utf8str, S32 len)
+{
+ LLWString wout;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ llwchar unichar;
+ U8 cur_char = utf8str[i];
+
+ if (cur_char < 0x80)
+ {
+ // Ascii character, just add it
+ unichar = cur_char;
+ }
+ else
+ {
+ S32 cont_bytes = 0;
+ if ((cur_char >> 5) == 0x6) // Two byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x1F&cur_char);
+ cont_bytes = 1;
+ }
+ else if ((cur_char >> 4) == 0xe) // Three byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x0F&cur_char);
+ cont_bytes = 2;
+ }
+ else if ((cur_char >> 3) == 0x1e) // Four byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x07&cur_char);
+ cont_bytes = 3;
+ }
+ else if ((cur_char >> 2) == 0x3e) // Five byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x03&cur_char);
+ cont_bytes = 4;
+ }
+ else if ((cur_char >> 1) == 0x7e) // Six byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x01&cur_char);
+ cont_bytes = 5;
+ }
+ else
+ {
+ wout += LL_UNKNOWN_CHAR;
+ ++i;
+ continue;
+ }
+
+ // Check that this character doesn't go past the end of the string
+ S32 end = (len < (i + cont_bytes)) ? len : (i + cont_bytes);
+ do
+ {
+ ++i;
+
+ cur_char = utf8str[i];
+ if ( (cur_char >> 6) == 0x2 )
+ {
+ unichar <<= 6;
+ unichar += (0x3F&cur_char);
+ }
+ else
+ {
+ // Malformed sequence - roll back to look at this as a new char
+ unichar = LL_UNKNOWN_CHAR;
+ --i;
+ break;
+ }
+ } while(i < end);
+
+ // Handle overlong characters and NULL characters
+ if ( ((cont_bytes == 1) && (unichar < 0x80))
+ || ((cont_bytes == 2) && (unichar < 0x800))
+ || ((cont_bytes == 3) && (unichar < 0x10000))
+ || ((cont_bytes == 4) && (unichar < 0x200000))
+ || ((cont_bytes == 5) && (unichar < 0x4000000)) )
+ {
+ unichar = LL_UNKNOWN_CHAR;
+ }
+ }
+
+ wout += unichar;
+ ++i;
+ }
+ return wout;
+}
+
+LLWString utf8str_to_wstring(const std::string& utf8str)
+{
+ const S32 len = (S32)utf8str.length();
+ return utf8str_to_wstring(utf8str, len);
+}
+
+std::string wstring_to_utf8str(const LLWString& utf32str, S32 len)
+{
+ std::string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ char tchars[8]; /* Flawfinder: ignore */
+ S32 n = wchar_to_utf8chars(utf32str[i], tchars);
+ tchars[n] = 0;
+ out += tchars;
+ i++;
+ }
+ return out;
+}
+
+std::string wstring_to_utf8str(const LLWString& utf32str)
+{
+ const S32 len = (S32)utf32str.length();
+ return wstring_to_utf8str(utf32str, len);
+}
+
+std::string utf16str_to_utf8str(const llutf16string& utf16str)
+{
+ return wstring_to_utf8str(utf16str_to_wstring(utf16str));
+}
+
+std::string utf16str_to_utf8str(const llutf16string& utf16str, S32 len)
+{
+ return wstring_to_utf8str(utf16str_to_wstring(utf16str, len), len);
+}
+
+
+//LLWString wstring_truncate(const LLWString &wstr, const S32 max_len)
+//{
+// return wstr.substr(0, llmin((S32)wstr.length(), max_len));
+//}
+//
+//
+//LLWString wstring_trim(const LLWString &wstr)
+//{
+// LLWString outstr;
+// outstr = wstring_trimhead(wstr);
+// outstr = wstring_trimtail(outstr);
+// return outstr;
+//}
+//
+//
+//LLWString wstring_trimhead(const LLWString &wstr)
+//{
+// if(wstr.empty())
+// {
+// return wstr;
+// }
+//
+// S32 i = 0;
+// while((i < (S32)wstr.length()) && iswspace(wstr[i]))
+// {
+// i++;
+// }
+// return wstr.substr(i, wstr.length() - i);
+//}
+//
+//
+//LLWString wstring_trimtail(const LLWString &wstr)
+//{
+// if(wstr.empty())
+// {
+// return wstr;
+// }
+//
+// S32 len = (S32)wstr.length();
+//
+// S32 i = len - 1;
+// while (i >= 0 && iswspace(wstr[i]))
+// {
+// i--;
+// }
+//
+// if (i >= 0)
+// {
+// return wstr.substr(0, i + 1);
+// }
+// return wstr;
+//}
+//
+//
+//LLWString wstring_copyinto(const LLWString &dest, const LLWString &src, const S32 insert_offset)
+//{
+// llassert( insert_offset <= (S32)dest.length() );
+//
+// LLWString out_str = dest.substr(0, insert_offset);
+// out_str += src;
+// LLWString tail = dest.substr(insert_offset);
+// out_str += tail;
+//
+// return out_str;
+//}
+
+
+//LLWString wstring_detabify(const LLWString &wstr, const S32 num_spaces)
+//{
+// LLWString out_str;
+// // Replace tabs with spaces
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] == '\t')
+// {
+// for (S32 j = 0; j < num_spaces; j++)
+// out_str += ' ';
+// }
+// else
+// {
+// out_str += wstr[i];
+// }
+// }
+// return out_str;
+//}
+
+
+//LLWString wstring_makeASCII(const LLWString &wstr)
+//{
+// // Replace non-ASCII chars with replace_char
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// if (out_str[i] > 0x7f)
+// {
+// out_str[i] = LL_UNKNOWN_CHAR;
+// }
+// }
+// return out_str;
+//}
+
+
+//LLWString wstring_substChar(const LLWString &wstr, const llwchar target_char, const llwchar replace_char)
+//{
+// // Replace all occurences of target_char with replace_char
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// if (out_str[i] == target_char)
+// {
+// out_str[i] = replace_char;
+// }
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_tolower(const LLWString &wstr)
+//{
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// out_str[i] = towlower(out_str[i]);
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_convert_to_lf(const LLWString &wstr)
+//{
+// const llwchar CR = 13;
+// // Remove carriage returns from string with CRLF
+// LLWString out_str;
+//
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] != CR)
+// {
+// out_str += wstr[i];
+// }
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_convert_to_crlf(const LLWString &wstr)
+//{
+// const llwchar LF = 10;
+// const llwchar CR = 13;
+// // Remove carriage returns from string with CRLF
+// LLWString out_str;
+//
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] == LF)
+// {
+// out_str += CR;
+// }
+// out_str += wstr[i];
+// }
+// return out_str;
+//}
+
+
+//S32 wstring_compare_insensitive(const LLWString &lhs, const LLWString &rhs)
+//{
+//
+// if (lhs == rhs)
+// {
+// return 0;
+// }
+//
+// if (lhs.empty())
+// {
+// return rhs.empty() ? 0 : 1;
+// }
+//
+// if (rhs.empty())
+// {
+// return -1;
+// }
+//
+//#ifdef LL_LINUX
+// // doesn't work because gcc 2.95 doesn't correctly implement c_str(). Sigh...
+// llerrs << "wstring_compare_insensitive doesn't work on Linux!" << llendl;
+// return 0;
+//#else
+// LLWString lhs_lower = lhs;
+// LLWString::toLower(lhs_lower);
+// std::string lhs_lower = wstring_to_utf8str(lhs_lower);
+// LLWString rhs_lower = lhs;
+// LLWString::toLower(rhs_lower);
+// std::string rhs_lower = wstring_to_utf8str(rhs_lower);
+//
+// return strcmp(lhs_lower.c_str(), rhs_lower.c_str());
+//#endif
+//}
+
+
+std::string utf8str_trim(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::trim(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+
+std::string utf8str_tolower(const std::string& utf8str)
+{
+ LLWString out_str = utf8str_to_wstring(utf8str);
+ LLWString::toLower(out_str);
+ return wstring_to_utf8str(out_str);
+}
+
+
+S32 utf8str_compare_insensitive(const std::string& lhs, const std::string& rhs)
+{
+ LLWString wlhs = utf8str_to_wstring(lhs);
+ LLWString wrhs = utf8str_to_wstring(rhs);
+ return LLWString::compareInsensitive(wlhs.c_str(), wrhs.c_str());
+}
+
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len)
+{
+ if (0 == max_len)
+ {
+ return std::string();
+ }
+ if ((S32)utf8str.length() <= max_len)
+ {
+ return utf8str;
+ }
+ else
+ {
+ S32 cur_char = max_len;
+
+ // If we're ASCII, we don't need to do anything
+ if ((U8)utf8str[cur_char] > 0x7f)
+ {
+ // If first two bits are (10), it's the tail end of a multibyte char. We need to shift back
+ // to the first character
+ while (0x80 == (0xc0 & utf8str[cur_char]))
+ {
+ cur_char--;
+ // Keep moving forward until we hit the first char;
+ if (cur_char == 0)
+ {
+ // Make sure we don't trash memory if we've got a bogus string.
+ break;
+ }
+ }
+ }
+ // The byte index we're on is one we want to get rid of, so we only want to copy up to (cur_char-1) chars
+ return utf8str.substr(0, cur_char);
+ }
+}
+
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::replaceChar(wstr, target_char, replace_char);
+ //wstr = wstring_substChar(wstr, target_char, replace_char);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string utf8str_makeASCII(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::_makeASCII(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string mbcsstring_makeASCII(const std::string& wstr)
+{
+ // Replace non-ASCII chars with replace_char
+ std::string out_str = wstr;
+ for (S32 i = 0; i < (S32)out_str.length(); i++)
+ {
+ if ((U8)out_str[i] > 0x7f)
+ {
+ out_str[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+ return out_str;
+}
+
+S32 LLStringOps::collate(const llwchar* a, const llwchar* b)
+{
+ #if LL_WINDOWS
+ // in Windows, wide string functions operator on 16-bit strings,
+ // not the proper 32 bit wide string
+ return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str());
+ #else
+ return wcscoll(a, b);
+ #endif
+}
+
+namespace LLStringFn
+{
+ void replace_nonprintable(std::basic_string<char>& string, char replacement)
+ {
+ const char MIN = 0x20;
+ std::basic_string<char>::size_type len = string.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if(string[ii] < MIN)
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable(
+ std::basic_string<llwchar>& string,
+ llwchar replacement)
+ {
+ const llwchar MIN = 0x20;
+ const llwchar MAX = 0x7f;
+ std::basic_string<llwchar>::size_type len = string.size();
+ for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii)
+ {
+ if((string[ii] < MIN) || (string[ii] > MAX))
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable_and_pipe(std::basic_string<char>& str,
+ char replacement)
+ {
+ const char MIN = 0x20;
+ const char PIPE = 0x7c;
+ std::basic_string<char>::size_type len = str.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if( (str[ii] < MIN) || (str[ii] == PIPE) )
+ {
+ str[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str,
+ llwchar replacement)
+ {
+ const llwchar MIN = 0x20;
+ const llwchar MAX = 0x7f;
+ const llwchar PIPE = 0x7c;
+ std::basic_string<llwchar>::size_type len = str.size();
+ for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii)
+ {
+ if( (str[ii] < MIN) || (str[ii] > MAX) || (str[ii] == PIPE) )
+ {
+ str[ii] = replacement;
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+// Testing
+
+#ifdef _DEBUG
+
+template<class T>
+void LLStringBase<T>::testHarness()
+{
+ LLString s1;
+
+ llassert( s1.c_str() == NULL );
+ llassert( s1.size() == 0 );
+ llassert( s1.empty() );
+
+ LLString s2( "hello");
+ llassert( !strcmp( s2.c_str(), "hello" ) );
+ llassert( s2.size() == 5 );
+ llassert( !s2.empty() );
+ LLString s3( s2 );
+
+ llassert( "hello" == s2 );
+ llassert( s2 == "hello" );
+ llassert( s2 > "gello" );
+ llassert( "gello" < s2 );
+ llassert( "gello" != s2 );
+ llassert( s2 != "gello" );
+
+ LLString s4 = s2;
+ llassert( !s4.empty() );
+ s4.empty();
+ llassert( s4.empty() );
+
+ LLString s5("");
+ llassert( s5.empty() );
+
+ llassert( isValidIndex(s5, 0) );
+ llassert( !isValidIndex(s5, 1) );
+
+ s3 = s2;
+ s4 = "hello again";
+
+ s4 += "!";
+ s4 += s4;
+ llassert( s4 == "hello again!hello again!" );
+
+
+ LLString s6 = s2 + " " + s2;
+ LLString s7 = s6;
+ llassert( s6 == s7 );
+ llassert( !( s6 != s7) );
+ llassert( !(s6 < s7) );
+ llassert( !(s6 > s7) );
+
+ llassert( !(s6 == "hi"));
+ llassert( s6 == "hello hello");
+ llassert( s6 < "hi");
+
+ llassert( s6[1] == 'e' );
+ s6[1] = 'f';
+ llassert( s6[1] == 'f' );
+
+ s2.erase( 4, 1 );
+ llassert( s2 == "hell");
+ s2.insert( 0, 'y' );
+ llassert( s2 == "yhell");
+ s2.erase( 1, 3 );
+ llassert( s2 == "yl");
+ s2.insert( 1, "awn, don't yel");
+ llassert( s2 == "yawn, don't yell");
+
+ LLString s8 = s2.substr( 6, 5 );
+ llassert( s8 == "don't" );
+
+ LLString s9 = " \t\ntest \t\t\n ";
+ trim(s9);
+ llassert( s9 == "test" );
+
+ s8 = "abc123&*(ABC";
+
+ s9 = s8;
+ toUpper(s9);
+ llassert( s9 == "ABC123&*(ABC" );
+
+ s9 = s8;
+ toLower(s9);
+ llassert( s9 == "abc123&*(abc" );
+
+
+ LLString s10( 10, 'x' );
+ llassert( s10 == "xxxxxxxxxx" );
+
+ LLString s11( "monkey in the middle", 7, 2 );
+ llassert( s11 == "in" );
+
+ LLString s12; //empty
+ s12 += "foo";
+ llassert( s12 == "foo" );
+
+ LLString s13; //empty
+ s13 += 'f';
+ llassert( s13 == "f" );
+}
+
+
+#endif // _DEBUG
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
new file mode 100644
index 0000000000..dca8ce4f3e
--- /dev/null
+++ b/indra/llcommon/llstring.h
@@ -0,0 +1,1281 @@
+/**
+ * @file llstring.h
+ * @brief String utility functions and LLString class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+#include <algorithm>
+#include <map>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#if LL_LINUX
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+const char LL_UNKNOWN_CHAR = '?';
+
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+class LLUUID;
+class LLColor4;
+class LLColor4U;
+
+#if (LL_DARWIN || (LL_LINUX && __GNUC__ > 2))
+// Template specialization of char_traits for U16s. Only necessary on Mac for now (exists on Windows, unused/broken on Linux/gcc2.95)
+namespace std
+{
+template<>
+struct char_traits<U16>
+{
+ typedef U16 char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
+
+ static size_t
+ length(const char_type* __s)
+ {
+ const char_type *cur_char = __s;
+ while (*cur_char != 0)
+ {
+ ++cur_char;
+ }
+ return cur_char - __s;
+ }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ {
+ // This isn't right.
+ //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
+
+ // I don't think there's a standard 'memset' for 16-bit values.
+ // Do this the old-fashioned way.
+
+ size_t __i;
+ for(__i = 0; __i < __n; __i++)
+ {
+ __s[__i] = __a;
+ }
+ return __s;
+ }
+
+ static char_type
+ to_char_type(const int_type& __c)
+ { return static_cast<char_type>(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c)
+ { return static_cast<int_type>(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(EOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return (__c == eof()) ? 0 : __c; }
+ };
+};
+#endif
+
+class LLStringOps
+{
+public:
+ static char toUpper(char elem) { return toupper(elem); }
+ static llwchar toUpper(llwchar elem) { return towupper(elem); }
+
+ static char toLower(char elem) { return tolower(elem); }
+ static llwchar toLower(llwchar elem) { return towlower(elem); }
+
+ static BOOL isSpace(char elem) { return isspace(elem) != 0; }
+ static BOOL isSpace(llwchar elem) { return iswspace(elem) != 0; }
+
+ static BOOL isUpper(char elem) { return isupper(elem) != 0; }
+ static BOOL isUpper(llwchar elem) { return iswupper(elem) != 0; }
+
+ static BOOL isLower(char elem) { return islower(elem) != 0; }
+ static BOOL isLower(llwchar elem) { return iswlower(elem) != 0; }
+
+ static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
+ static S32 collate(const llwchar* a, const llwchar* b);
+
+ static BOOL isDigit(char a) { return isdigit(a) != 0; }
+ static BOOL isDigit(llwchar a) { return iswdigit(a) != 0; }
+};
+
+//RN: I used a templated base class instead of a pure interface class to minimize code duplication
+// but it might be worthwhile to just go with two implementations (LLString and LLWString) of
+// an interface class, unless we can think of a good reason to have a std::basic_string polymorphic base
+
+//****************************************************************
+// NOTA BENE: do *NOT* dynamically allocate memory inside of LLStringBase as the {*()^#%*)#%W^*)#%*)STL implentation
+// of basic_string doesn't provide a virtual destructor. If we need to allocate resources specific to LLString
+// then we should either customize std::basic_string to linden::basic_string or change LLString to be a wrapper
+// that contains an instance of std::basic_string. Similarly, overriding methods defined in std::basic_string will *not*
+// be called in a polymorphic manner (passing an instance of basic_string to a particular function)
+//****************************************************************
+
+template <class T>
+class LLStringBase : public std::basic_string<T>
+{
+public:
+ typedef typename std::basic_string<T>::size_type size_type;
+
+ // naming convention follows those set for LLUUID
+// static LLStringBase null; // deprecated for std::string compliance
+// static LLStringBase zero_length; // deprecated for std::string compliance
+
+
+ // standard constructors
+ LLStringBase() : std::basic_string<T>() {}
+ LLStringBase(const LLStringBase& s): std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s) : std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s, size_type pos, size_type n = std::basic_string<T>::npos)
+ : std::basic_string<T>(s, pos, n) {}
+ LLStringBase(size_type count, const T& c) : std::basic_string<T>() { assign(count, c);}
+ // custom constructors
+ LLStringBase(const T* s);
+ LLStringBase(const T* s, size_type n);
+ LLStringBase(const T* s, size_type pos, size_type n );
+
+#if LL_LINUX
+ void clear() { assign(null); }
+
+ LLStringBase<T>& assign(const T* s);
+ LLStringBase<T>& assign(const T* s, size_type n);
+ LLStringBase<T>& assign(const LLStringBase& s);
+ LLStringBase<T>& assign(size_type n, const T& c);
+ LLStringBase<T>& assign(const T* a, const T* b);
+ LLStringBase<T>& assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2);
+ LLStringBase<T>& assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2);
+
+ // workaround for bug in gcc2 STL headers.
+ #if ((__GNUC__ <= 2) && (!defined _STLPORT_VERSION))
+ const T* c_str () const
+ {
+ if (length () == 0)
+ {
+ static const T zero = 0;
+ return &zero;
+ }
+
+ //terminate ();
+ { string_char_traits<T>::assign(const_cast<T*>(data())[length()], string_char_traits<T>::eos()); }
+
+ return data ();
+ }
+ #endif
+#endif
+
+ bool operator==(const T* _Right) const { return _Right ? (std::basic_string<T>::compare(_Right) == 0) : this->empty(); }
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static LLStringBase null;
+
+ typedef std::map<std::string, std::string> format_map_t;
+ static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
+
+ static BOOL isValidIndex(const std::basic_string<T>& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static void trimHead(std::basic_string<T>& string);
+ static void trimTail(std::basic_string<T>& string);
+ static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
+ static void truncate(std::basic_string<T>& string, size_type count);
+
+ static void toUpper(std::basic_string<T>& string);
+ static void toLower(std::basic_string<T>& string);
+
+ // True if this is the head of s.
+ static BOOL isHead( const std::basic_string<T>& string, const T* s );
+
+ static void addCRLF(std::basic_string<T>& string);
+ static void removeCRLF(std::basic_string<T>& string);
+
+ static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
+ static void replaceChar( std::basic_string<T>& string, T target, T replacement );
+
+ static BOOL containsNonprintable(const std::basic_string<T>& string);
+ static void stripNonprintable(std::basic_string<T>& string);
+
+ /**
+ * @brief Unsafe way to make ascii characters. You should probably
+ * only call this when interacting with the host operating system.
+ * The 1 byte LLString does not work correctly.
+ * The 2 and 4 byte LLString probably work, so LLWString::_makeASCII
+ * should work.
+ */
+ static void _makeASCII(std::basic_string<T>& string);
+
+ static BOOL read(std::basic_string<T>& string, const char* filename); /*Flawfinder: ignore*/
+ static BOOL write(std::basic_string<T>& string, const char* filename);
+
+ // Conversion to other data types
+ static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
+ static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
+ static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
+ static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
+ static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
+ static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
+ static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
+ static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
+ static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Utility functions for working with char*'s and strings
+
+ // Like strcmp but also handles empty strings. Uses
+ // current locale.
+ static S32 compareStrings(const T* lhs, const T* rhs);
+
+ // case insensitive version of above. Uses current locale on
+ // Win32, and falls back to a non-locale aware comparison on
+ // Linux.
+ static S32 compareInsensitive(const T* lhs, const T* rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+
+ // A replacement for strncpy.
+ // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
+ // up to dst_size-1 characters of src.
+ static void copy(T* dst, const T* src, size_type dst_size);
+
+ // Copies src into dst at a given offset.
+ static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+
+#ifdef _DEBUG
+ static void testHarness();
+#endif
+
+};
+
+template<class T> LLStringBase<T> LLStringBase<T>::null;
+
+typedef LLStringBase<char> LLString;
+typedef LLStringBase<llwchar> LLWString;
+
+struct LLDictionaryLess
+{
+public:
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ return (LLString::precedesDict(a, b) ? true : false);
+ }
+};
+
+
+/**
+ * Simple support functions
+ */
+
+/**
+ * @breif chop off the trailing characters in a string.
+ *
+ * This function works on bytes rather than glyphs, so this will
+ * incorrectly truncate non-single byte strings.
+ * Use utf8str_truncate() for utf8 strings
+ * @return a copy of in string minus the trailing count characters.
+ */
+inline std::string chop_tail_copy(
+ const std::string& in,
+ std::string::size_type count)
+{
+ return std::string(in, 0, in.length() - count);
+}
+
+/**
+ * @brief Return a string constructed from in without crashing if the
+ * pointer is NULL.
+ */
+std::string ll_safe_string(const char* in);
+
+/**
+ * @brief This translates a nybble stored as a hex value from 0-f back
+ * to a nybble in the low order bits of the return byte.
+ */
+U8 hex_as_nybble(char hex);
+
+
+/**
+ * Unicode support
+ */
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// of the data may not be recovered.
+std::string rawstr_to_utf8(const std::string& raw);
+
+//
+// We should never use UTF16 except when communicating with Win32!
+//
+typedef std::basic_string<U16> llutf16string;
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
+LLWString utf16str_to_wstring(const llutf16string &utf16str);
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
+llutf16string wstring_to_utf16str(const LLWString &utf32str);
+
+llutf16string utf8str_to_utf16str ( const LLString& utf8str, S32 len);
+llutf16string utf8str_to_utf16str ( const LLString& utf8str );
+
+LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
+LLWString utf8str_to_wstring(const std::string &utf8str);
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+// Special hack for llfilepicker.cpp:
+S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8 = 0);
+S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar);
+S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+//
+std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
+std::string wstring_to_utf8str(const LLWString &utf32str);
+
+std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
+std::string utf16str_to_utf8str(const llutf16string &utf16str);
+
+// Length of this UTF32 string in bytes when transformed to UTF8
+S32 wstring_utf8_length(const LLWString& wstr);
+
+// Length in bytes of this wide char in a UTF8 string
+S32 wchar_utf8_length(const llwchar wc);
+
+std::string utf8str_tolower(const std::string& utf8str);
+
+/**
+ * @brief Properly truncate a utf8 string to a maximum byte count.
+ *
+ * The returned string may be less than max_len if the truncation
+ * happens in the middle of a glyph. If max_len is longer than the
+ * string passed in, the return value == utf8str.
+ * @param utf8str A valid utf8 string to truncate.
+ * @param max_len The maximum number of bytes in the returne
+ * @return Returns a valid utf8 string with byte count <= max_len.
+ */
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
+
+std::string utf8str_trim(const std::string& utf8str);
+
+S32 utf8str_compare_insensitive(
+ const std::string& lhs,
+ const std::string& rhs);
+
+/**
+ * @brief Replace all occurences of target_char with replace_char
+ *
+ * @param utf8str A utf8 string to process.
+ * @param target_char The wchar to be replaced
+ * @param replace_char The wchar which is written on replace
+ */
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char);
+
+std::string utf8str_makeASCII(const std::string& utf8str);
+
+// Hack - used for evil notecards.
+std::string mbcsstring_makeASCII(const std::string& str);
+
+template <class T>
+std::ostream& operator<<(std::ostream &s, const LLStringBase<T> &str)
+{
+ s << ((std::basic_string<T>)str);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr);
+
+
+/**
+ * Many of the 'strip' and 'replace' methods of LLStringBase need
+ * specialization to work with the signed char type.
+ * Sadly, it is not possible (AFAIK) to specialize a single method of
+ * a template class.
+ * That stuff should go here.
+ */
+namespace LLStringFn
+{
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<char>& string,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * a wide string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<llwchar>& string,
+ llwchar replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<char>& str,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a wide string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement wide character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str,
+ llwchar replacement);
+}
+
+////////////////////////////////////////////////////////////
+
+// static
+template<class T>
+S32 LLStringBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map)
+{
+ typedef typename std::basic_string<T>::size_type string_size_type_t;
+ S32 res = 0;
+ for (format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ U32 fmtlen = iter->first.size();
+ string_size_type_t n = 0;
+ while (1)
+ {
+ n = s.find(iter->first, n);
+ if (n == std::basic_string<T>::npos)
+ {
+ break;
+ }
+ s.erase(n, fmtlen);
+ s.insert(n, iter->second);
+ n += fmtlen;
+ ++res;
+ }
+ }
+ return res;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareStrings(const T* lhs, const T* rhs)
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0])
+ {
+ result = -1;
+ }
+ else
+ {
+ result = LLStringOps::collate(lhs, rhs);
+ }
+ return result;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareInsensitive(const T* lhs, const T* rhs )
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0] )
+ {
+ result = -1;
+ }
+ else
+ {
+ LLStringBase<T> lhs_string(lhs);
+ LLStringBase<T> rhs_string(rhs);
+ LLStringBase<T>::toUpper(lhs_string);
+ LLStringBase<T>::toUpper(rhs_string);
+ result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+ }
+ return result;
+}
+
+
+// Case sensitive comparison with good handling of numbers. Does not use current locale.
+// a.k.a. strdictcmp()
+
+//static
+template<class T>
+S32 LLStringBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+ S32 bias = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( bias==0 ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
+ }else{
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ if( ca==cb ) ca += bias;
+ return ca-cb;
+}
+
+// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+// static
+template<class T>
+BOOL LLStringBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+{
+ if( a.size() && b.size() )
+ {
+ return (LLStringBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
+ }
+ else
+ {
+ return (!b.empty());
+ }
+}
+
+// Constructors
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s ) : std::basic_string<T>()
+{
+ if (s) assign(s);
+}
+
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type n ) : std::basic_string<T>()
+{
+ if (s) assign(s, n);
+}
+
+// Init from a substring
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type pos, size_type n ) : std::basic_string<T>()
+{
+ if( s )
+ {
+ assign(s + pos, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+}
+
+#if LL_LINUX
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s, size_type n)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const LLStringBase<T>& s)
+{
+ std::basic_string<T>::assign(s);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(size_type n, const T& c)
+{
+ std::basic_string<T>::assign(n, c);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* a, const T* b)
+{
+ if (a > b)
+ assign(LLStringBase<T>::null);
+ else
+ assign(a, (size_type) (b-a));
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+#endif
+
+//static
+template<class T>
+void LLStringBase<T>::toUpper(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::toLower(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimHead(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ size_type i = 0;
+ while( i < string.length() && LLStringOps::isSpace( string[i] ) )
+ {
+ i++;
+ }
+ string.erase(0, i);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimTail(std::basic_string<T>& string)
+{
+ if( string.size() )
+ {
+ size_type len = string.length();
+ size_type i = len;
+ while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
+ {
+ i--;
+ }
+
+ string.erase( i, len - i );
+ }
+}
+
+
+// Replace line feeds with carriage return-line feed pairs.
+//static
+template<class T>
+void LLStringBase<T>::addCRLF(std::basic_string<T>& string)
+{
+ const T LF = 10;
+ const T CR = 13;
+
+ // Count the number of line feeds
+ size_type count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len; i++ )
+ {
+ if( string[i] == LF )
+ {
+ count++;
+ }
+ }
+
+ // Insert a carriage return before each line feed
+ if( count )
+ {
+ size_type size = len + count;
+ T *t = new T[size];
+ size_type j = 0;
+ for( i = 0; i < len; ++i )
+ {
+ if( string[i] == LF )
+ {
+ t[j] = CR;
+ ++j;
+ }
+ t[j] = string[i];
+ ++j;
+ }
+
+ string.assign(t, size);
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringBase<T>::removeCRLF(std::basic_string<T>& string)
+{
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count; i++ )
+ {
+ if( string[i+cr_count] == CR )
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ for (found_pos = string.find(target, found_pos);
+ found_pos != std::basic_string<T>::npos;
+ found_pos = string.find(target, found_pos))
+ {
+ string[found_pos] = replacement;
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+{
+ const char LF = 10;
+ const S8 MIN = 32;
+// const S8 MAX = 127;
+
+ size_type len = string.size();
+ for( size_type i = 0; i < len; i++ )
+ {
+ // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
+ // which has a max value of 127.
+ if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
+ {
+ string[i] = replacement;
+ }
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab )
+{
+ llassert( spaces_per_tab >= 0 );
+
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ LLStringBase<T> out_str;
+ // Replace tabs with spaces
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] == TAB)
+ {
+ for (size_type j = 0; j < spaces_per_tab; j++)
+ out_str += SPACE;
+ }
+ else
+ {
+ out_str += string[i];
+ }
+ }
+ string = out_str;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::containsNonprintable(const std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ BOOL rv = FALSE;
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ if(string[i] < MIN)
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+template<class T>
+void LLStringBase<T>::stripNonprintable(std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ size_type j = 0;
+ if (string.empty())
+ {
+ return;
+ }
+ char* c_string = new char[string.size() + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ char* read_head = &string[i];
+ write_head = &c_string[j];
+ if(!(*read_head < MIN))
+ {
+ *write_head = *read_head;
+ ++j;
+ }
+ }
+ c_string[j]= '\0';
+ string = c_string;
+ delete []c_string;
+}
+
+template<class T>
+void LLStringBase<T>::_makeASCII(std::basic_string<T>& string)
+{
+ // Replace non-ASCII chars with LL_UNKNOWN_CHAR
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] > 0x7f)
+ {
+ string[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copy( T* dst, const T* src, size_type dst_size )
+{
+ if( dst_size > 0 )
+ {
+ size_type min_len = 0;
+ if( src )
+ {
+ min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
+ memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
+ }
+ dst[min_len] = '\0';
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+{
+ llassert( offset <= dst.length() );
+
+ // special case - append to end of string and avoid expensive (when strings are large) string manipulations
+ if ( offset == dst.length() )
+ {
+ dst += src;
+ }
+ else
+ {
+ LLWString tail = dst.substr(offset);
+
+ dst = dst.substr(0, offset);
+ dst += src;
+ dst += tail;
+ };
+}
+
+// True if this is the head of s.
+//static
+template<class T>
+BOOL LLStringBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+{
+ if( string.empty() )
+ {
+ // Early exit
+ return FALSE;
+ }
+ else
+ {
+ return (strncmp( s, string.c_str(), string.size() ) == 0);
+ }
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::read(std::basic_string<T>& string, const char* filename) /*Flawfinder: ignore*/
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::read at %s:%d\n", __FILE__, __LINE__);
+#else
+ llifstream ifs(filename, llifstream::binary);
+ if (!ifs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ std::basic_ostringstream<T> oss;
+
+ oss << ifs.rdbuf();
+
+ string = oss.str();
+
+ ifs.close();
+#endif
+ return TRUE;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::write(std::basic_string<T>& string, const char* filename)
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::write at %s:%d\n", __FILE__, __LINE__);
+#else
+ llofstream ofs(filename, llofstream::binary);
+ if (!ofs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ ofs << string;
+
+ ofs.close();
+#endif
+ return TRUE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ if(
+ (temp == "1") ||
+ (temp == "T") ||
+ (temp == "t") ||
+ (temp == "TRUE") ||
+ (temp == "true") ||
+ (temp == "True") )
+ {
+ value = TRUE;
+ return TRUE;
+ }
+ else
+ if(
+ (temp == "0") ||
+ (temp == "F") ||
+ (temp == "f") ||
+ (temp == "FALSE") ||
+ (temp == "false") ||
+ (temp == "False") )
+ {
+ value = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
+ {
+ value = (U8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
+ {
+ value = (S8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
+ {
+ value = (S16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
+ {
+ value = (U16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow reporting here
+ //if( ULONG_MAX == v )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if((LONG_MAX == v) || (LONG_MIN == v))
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+{
+ F64 value64 = 0.0;
+ BOOL success = convertToF64(string, value64);
+ if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
+ {
+ value = (F32) value64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+void LLStringBase<T>::truncate(std::basic_string<T>& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+#endif // LL_STRING_H
diff --git a/indra/llcommon/llstringtable.cpp b/indra/llcommon/llstringtable.cpp
new file mode 100644
index 0000000000..483c4fe502
--- /dev/null
+++ b/indra/llcommon/llstringtable.cpp
@@ -0,0 +1,323 @@
+/**
+ * @file llstringtable.cpp
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstringtable.h"
+#include "llstl.h"
+
+LLStringTable gStringTable(32768);
+
+LLStringTable::LLStringTable(int tablesize)
+: mUniqueEntries(0)
+{
+ S32 i;
+ if (!tablesize)
+ tablesize = 4096; // some arbitrary default
+ // Make sure tablesize is power of 2
+ for (i = 31; i>0; i--)
+ {
+ if (tablesize & (1<<i))
+ {
+ if (tablesize >= (3<<(i-1)))
+ tablesize = (1<<(i+1));
+ else
+ tablesize = (1<<i);
+ break;
+ }
+ }
+ mMaxEntries = tablesize;
+
+#if !STRING_TABLE_HASH_MAP
+ // ALlocate strings
+ mStringList = new string_list_ptr_t[mMaxEntries];
+ // Clear strings
+ for (i = 0; i < mMaxEntries; i++)
+ {
+ mStringList[i] = NULL;
+ }
+#endif
+}
+
+LLStringTable::~LLStringTable()
+{
+#if !STRING_TABLE_HASH_MAP
+ if (mStringList)
+ {
+ for (S32 i = 0; i < mMaxEntries; i++)
+ {
+ if (mStringList[i])
+ {
+ string_list_t::iterator iter;
+ for (iter = mStringList[i]->begin(); iter != mStringList[i]->end(); iter++)
+ delete *iter; // *iter = (LLStringTableEntry*)
+ }
+ delete mStringList[i];
+ }
+ delete [] mStringList;
+ mStringList = NULL;
+ }
+#else
+ // Need to clean up the string hash
+ for_each(mStringHash.begin(), mStringHash.end(), DeletePairedPointer());
+ mStringHash.clear();
+#endif
+}
+
+
+static U32 hash_my_string(const char *str, int max_entries)
+{
+ U32 retval = 0;
+#if 0
+ while (*str)
+ {
+ retval <<= 1;
+ retval += *str++;
+ }
+#else
+ while (*str)
+ {
+ retval = (retval<<4) + *str;
+ U32 x = (retval & 0xf0000000);
+ if (x) retval = retval ^ (x>>24);
+ retval = retval & (~x);
+ str++;
+ }
+#endif
+ return (retval & (max_entries-1)); // max_entries is gauranteed to be power of 2
+}
+
+char* LLStringTable::checkString(const std::string& str)
+{
+ return checkString(str.c_str());
+}
+
+char* LLStringTable::checkString(const char *str)
+{
+ LLStringTableEntry* entry = checkStringEntry(str);
+ if (entry)
+ {
+ return entry->mString;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+LLStringTableEntry* LLStringTable::checkStringEntry(const std::string& str)
+{
+ return checkStringEntry(str.c_str());
+}
+
+LLStringTableEntry* LLStringTable::checkStringEntry(const char *str)
+{
+ if (str)
+ {
+ char *ret_val;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ return entry;
+ }
+ }
+#else
+ string_list_t *strlist = mStringList[hash_value];
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ return entry;
+ }
+ }
+ }
+#endif
+ }
+ return NULL;
+}
+
+char* LLStringTable::addString(const std::string& str)
+{
+ //RN: safe to use temporary c_str since string is copied
+ return addString(str.c_str());
+}
+
+char* LLStringTable::addString(const char *str)
+{
+
+ LLStringTableEntry* entry = addStringEntry(str);
+ if (entry)
+ {
+ return entry->mString;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+LLStringTableEntry* LLStringTable::addStringEntry(const std::string& str)
+{
+ return addStringEntry(str.c_str());
+}
+
+LLStringTableEntry* LLStringTable::addStringEntry(const char *str)
+{
+ if (str)
+ {
+ char *ret_val = NULL;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ entry->incCount();
+ return entry;
+ }
+ }
+
+ // not found, so add!
+ LLStringTableEntry* newentry = new LLStringTableEntry(str);
+ ret_val = newentry->mString;
+ mStringHash.insert(string_hash_t::value_type(hash_value, newentry));
+#else
+ string_list_t *strlist = mStringList[hash_value];
+
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ entry->incCount();
+ return entry;
+ }
+ }
+ }
+ else
+ {
+ mStringList[hash_value] = new string_list_t;
+ strlist = mStringList[hash_value];
+ }
+
+ // not found, so add!
+ LLStringTableEntry *newentry = new LLStringTableEntry(str);
+ //ret_val = newentry->mString;
+ strlist->push_front(newentry);
+#endif
+ mUniqueEntries++;
+ return newentry;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLStringTable::removeString(const char *str)
+{
+ if (str)
+ {
+ char *ret_val;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+ {
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ if (!entry->decCount())
+ {
+ mUniqueEntries--;
+ if (mUniqueEntries < 0)
+ {
+ llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ }
+ delete iter->second;
+ mStringHash.erase(iter);
+ }
+ return;
+ }
+ }
+ }
+#else
+ string_list_t *strlist = mStringList[hash_value];
+
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ if (!entry->decCount())
+ {
+ mUniqueEntries--;
+ if (mUniqueEntries < 0)
+ {
+ llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ }
+ strlist->remove(entry);
+ delete entry;
+ }
+ return;
+ }
+ }
+ }
+#endif
+ }
+}
+
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h
new file mode 100644
index 0000000000..ad428ce565
--- /dev/null
+++ b/indra/llcommon/llstringtable.h
@@ -0,0 +1,208 @@
+/**
+ * @file llstringtable.h
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STRING_TABLE_H
+#define LL_STRING_TABLE_H
+
+#include "llstl.h"
+#include <list>
+#include <set>
+
+#if LL_WINDOWS
+# if (_MSC_VER >= 1300)
+# define STRING_TABLE_HASH_MAP 1
+# pragma warning(disable : 4996)
+# endif
+#else
+//# define STRING_TABLE_HASH_MAP 1
+#endif
+
+#if STRING_TABLE_HASH_MAP
+#include <hash_map>
+#endif
+
+// string_table.h
+// LLStringTable class header file
+// Provides a _fast_ method for finding unique copies of strings
+//
+// Copyright 2001-2002, Linden Research, Inc.
+
+const U32 MAX_STRINGS_LENGTH = 256;
+
+class LLStringTableEntry
+{
+public:
+ LLStringTableEntry(const char *str)
+ : mString(NULL), mCount(1)
+ {
+ // Copy string
+ U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/
+ length = llmin(length, MAX_STRINGS_LENGTH);
+ mString = new char[length];
+ strncpy(mString, str, length); /*Flawfinder: ignore*/
+ mString[length - 1] = 0;
+ }
+ ~LLStringTableEntry()
+ {
+ delete [] mString;
+ mCount = 0;
+ }
+ void incCount() { mCount++; }
+ BOOL decCount() { return --mCount; }
+
+ char *mString;
+ S32 mCount;
+};
+
+class LLStringTable
+{
+public:
+ LLStringTable(int tablesize);
+ ~LLStringTable();
+
+ char *checkString(const char *str);
+ char *checkString(const std::string& str);
+ LLStringTableEntry *checkStringEntry(const char *str);
+ LLStringTableEntry *checkStringEntry(const std::string& str);
+
+ char *addString(const char *str);
+ char *addString(const std::string& str);
+ LLStringTableEntry *addStringEntry(const char *str);
+ LLStringTableEntry *addStringEntry(const std::string& str);
+ void removeString(const char *str);
+
+ S32 mMaxEntries;
+ S32 mUniqueEntries;
+
+#if STRING_TABLE_HASH_MAP
+ typedef std::hash_multimap<U32, LLStringTableEntry *> string_hash_t;
+ string_hash_t mStringHash;
+#else
+ typedef std::list<LLStringTableEntry *> string_list_t;
+ typedef string_list_t * string_list_ptr_t;
+ string_list_ptr_t *mStringList;
+#endif
+};
+
+extern LLStringTable gStringTable;
+
+//============================================================================
+
+// This class is designed to be used locally,
+// e.g. as a member of an LLXmlTree
+// Strings can be inserted only, then quickly looked up
+
+typedef const std::string* LLStdStringHandle;
+
+class LLStdStringTable
+{
+public:
+ LLStdStringTable(S32 tablesize = 0)
+ {
+ if (tablesize == 0)
+ {
+ tablesize = 256; // default
+ }
+ // Make sure tablesize is power of 2
+ for (S32 i = 31; i>0; i--)
+ {
+ if (tablesize & (1<<i))
+ {
+ if (tablesize >= (3<<(i-1)))
+ tablesize = (1<<(i+1));
+ else
+ tablesize = (1<<i);
+ break;
+ }
+ }
+ mTableSize = tablesize;
+ mStringList = new string_set_t[tablesize];
+ }
+ ~LLStdStringTable()
+ {
+ cleanup();
+ delete[] mStringList;
+ }
+ void cleanup()
+ {
+ // remove strings
+ for (S32 i = 0; i<mTableSize; i++)
+ {
+ string_set_t& stringset = mStringList[i];
+ for (string_set_t::iterator iter = stringset.begin(); iter != stringset.end(); iter++)
+ {
+ delete *iter;
+ }
+ stringset.clear();
+ }
+ }
+
+ LLStdStringHandle lookup(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle checkString(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle insert(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ LLStdStringHandle result = lookup(hashval, s);
+ if (result == NULL)
+ {
+ result = new std::string(s);
+ mStringList[hashval].insert(result);
+ }
+ return result;
+ }
+ LLStdStringHandle addString(const std::string& s)
+ {
+ return insert(s);
+ }
+
+private:
+ U32 makehash(const std::string& s)
+ {
+ S32 len = (S32)s.size();
+ const char* c = s.c_str();
+ U32 hashval = 0;
+ for (S32 i=0; i<len; i++)
+ {
+ hashval = ((hashval<<5) + hashval) + *c++;
+ }
+ return hashval & (mTableSize-1);
+ }
+ LLStdStringHandle lookup(U32 hashval, const std::string& s)
+ {
+ string_set_t& stringset = mStringList[hashval];
+ LLStdStringHandle handle = &s;
+ string_set_t::iterator iter = stringset.find(handle); // compares actual strings
+ if (iter != stringset.end())
+ {
+ return *iter;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+private:
+ S32 mTableSize;
+ typedef std::set<LLStdStringHandle, compare_pointer_contents<std::string> > string_set_t;
+ string_set_t* mStringList; // [mTableSize]
+};
+
+
+#endif
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
new file mode 100644
index 0000000000..65fa4a5c9c
--- /dev/null
+++ b/indra/llcommon/llsys.cpp
@@ -0,0 +1,534 @@
+/**
+ * @file llsys.cpp
+ * @brief Impelementation of the basic system query functions.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <zlib/zlib.h>
+#include "processor.h"
+
+#if LL_DARWIN
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+#elif LL_LINUX
+#include <sys/utsname.h>
+const char MEMINFO_FILE[] = "/proc/meminfo";
+const char CPUINFO_FILE[] = "/proc/cpuinfo";
+#endif
+
+
+static const S32 CPUINFO_BUFFER_SIZE = 16383;
+LLCPUInfo gSysCPU;
+
+LLOSInfo::LLOSInfo() :
+ mMajorVer(0), mMinorVer(0), mBuild(0),
+ mOSString("")
+{
+
+#if LL_WINDOWS
+ OSVERSIONINFOEX osvi;
+ BOOL bOsVersionInfoEx;
+
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
+ {
+ // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
+ osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ if(!GetVersionEx( (OSVERSIONINFO *) &osvi))
+ return;
+ }
+ mMajorVer = osvi.dwMajorVersion;
+ mMinorVer = osvi.dwMinorVersion;
+ mBuild = osvi.dwBuildNumber;
+
+ switch(osvi.dwPlatformId)
+ {
+ case VER_PLATFORM_WIN32_NT:
+ {
+ // Test for the product.
+ if(osvi.dwMajorVersion <= 4)
+ {
+ mOSString = "Microsoft Windows NT ";
+ }
+ else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ {
+ mOSString = "Microsoft Windows 2000 ";
+ }
+ else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1)
+ {
+ mOSString = "Microsoft Windows XP ";
+ }
+ else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSString = "Microsoft Windows XP x64 Edition ";
+ else mOSString = "Microsoft Windows Server 2003 ";
+ }
+ else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSString = "Microsoft Windows Vista ";
+ else mOSString = "Microsoft Windows Vista Server ";
+ }
+ else // Use the registry on early versions of Windows NT.
+ {
+ HKEY hKey;
+ WCHAR szProductType[80];
+ DWORD dwBufLen;
+ RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
+ 0, KEY_QUERY_VALUE, &hKey );
+ RegQueryValueEx( hKey, L"ProductType", NULL, NULL,
+ (LPBYTE) szProductType, &dwBufLen);
+ RegCloseKey( hKey );
+ if ( lstrcmpi( L"WINNT", szProductType) == 0 )
+ {
+ mOSString += "Professional ";
+ }
+ else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 )
+ {
+ mOSString += "Server ";
+ }
+ else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 )
+ {
+ mOSString += "Advanced Server ";
+ }
+ }
+
+ std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion);
+ // Display version, service pack (if any), and build number.
+ char tmp[MAX_STRING]; /* Flawfinder: ignore */
+ if(osvi.dwMajorVersion <= 4)
+ {
+ snprintf(
+ tmp,
+ sizeof(tmp),
+ "version %d.%d %s (Build %d)",
+ osvi.dwMajorVersion,
+ osvi.dwMinorVersion,
+ csdversion.c_str(),
+ (osvi.dwBuildNumber & 0xffff)); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(
+ tmp,
+ sizeof(tmp),
+ "%s (Build %d)",
+ csdversion.c_str(),
+ (osvi.dwBuildNumber & 0xffff)); /*Flawfinder: ignore*/
+ }
+ mOSString += tmp;
+ }
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ // Test for the Windows 95 product family.
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+ {
+ mOSString = "Microsoft Windows 95 ";
+ if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
+ {
+ mOSString += "OSR2 ";
+ }
+ }
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
+ {
+ mOSString = "Microsoft Windows 98 ";
+ if ( osvi.szCSDVersion[1] == 'A' )
+ {
+ mOSString += "SE ";
+ }
+ }
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
+ {
+ mOSString = "Microsoft Windows Millennium Edition ";
+ }
+ break;
+ }
+#else
+ struct utsname un;
+ if(0==uname(&un))
+ {
+ mOSString.append(un.sysname);
+ mOSString.append(" ");
+ mOSString.append(un.release);
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+ }
+ else
+ {
+ mOSString.append("Unable to collect OS info");
+ }
+#endif
+
+}
+
+#ifndef LL_WINDOWS
+// static
+S32 LLOSInfo::getMaxOpenFiles()
+{
+ const S32 OPEN_MAX_GUESS = 256;
+
+#ifdef OPEN_MAX
+ static S32 open_max = OPEN_MAX;
+#else
+ static S32 open_max = 0;
+#endif
+
+ if (0 == open_max)
+ {
+ // First time through.
+ errno = 0;
+ if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0)
+ {
+ if (0 == errno)
+ {
+ // Indeterminate.
+ open_max = OPEN_MAX_GUESS;
+ }
+ else
+ {
+ llerrs << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << llendl;
+ }
+ }
+ }
+ return open_max;
+}
+#endif
+
+void LLOSInfo::stream(std::ostream& s) const
+{
+ s << mOSString;
+}
+
+const std::string& LLOSInfo::getOSString() const
+{
+ return mOSString;
+}
+
+const S32 STATUS_SIZE = 8192;
+
+//static
+U32 LLOSInfo::getProcessVirtualSizeKB()
+{
+ U32 virtual_size = 0;
+#if LL_WINDOWS
+#endif
+#if LL_LINUX
+ FILE *status_filep = LLFile::fopen("/proc/self/status", "r");
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+ bzero(buff, STATUS_SIZE);
+
+ rewind(status_filep);
+ fread(buff, 1, STATUS_SIZE-2, status_filep);
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmSize:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &virtual_size);
+ }
+ fclose(status_filep);
+#endif
+ return virtual_size;
+}
+
+//static
+U32 LLOSInfo::getProcessResidentSizeKB()
+{
+ U32 resident_size = 0;
+#if LL_WINDOWS
+#endif
+#if LL_LINUX
+ FILE *status_filep = LLFile::fopen("/proc/self/status", "r");
+ if (status_filep != NULL)
+ {
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+ bzero(buff, STATUS_SIZE);
+
+ rewind(status_filep);
+ fread(buff, 1, STATUS_SIZE-2, status_filep);
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmRSS:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &resident_size);
+ }
+ fclose(status_filep);
+ }
+#endif
+ return resident_size;
+}
+
+LLCPUInfo::LLCPUInfo()
+{
+ CProcessor proc;
+ const ProcessorInfo* info = proc.GetCPUInfo();
+ mHasSSE = (info->_Ext.SSE_StreamingSIMD_Extensions != 0);
+ mHasSSE2 = (info->_Ext.SSE2_StreamingSIMD2_Extensions != 0);
+ mCPUMhz = (S32)(proc.GetCPUFrequency(50)/1000000.0);
+ mFamily.assign( info->strFamily );
+}
+
+std::string LLCPUInfo::getCPUString() const
+{
+ std::string cpu_string;
+
+#if LL_WINDOWS || LL_DARWIN
+ // gather machine information.
+ char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */
+ CProcessor proc;
+ if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE))
+ {
+ cpu_string.append(proc_buf);
+ }
+#else
+ cpu_string.append("Can't get CPU information");
+#endif
+
+ return cpu_string;
+}
+
+std::string LLCPUInfo::getCPUStringTerse() const
+{
+ std::string cpu_string;
+
+#if LL_WINDOWS || LL_DARWIN
+ CProcessor proc;
+ const ProcessorInfo *info = proc.GetCPUInfo();
+
+ cpu_string.append(info->strBrandID);
+
+ F64 freq = (F64)(S64)proc.GetCPUFrequency(50) / 1000000.f;
+
+ // cpu speed is often way wrong, do a sanity check
+ if (freq < 10000.f && freq > 200.f )
+ {
+ char tmp[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(tmp, sizeof(tmp), " (%.0f Mhz)", freq); /* Flawfinder: ignore */
+
+ cpu_string.append(tmp);
+ }
+#else
+ cpu_string.append("Can't get terse CPU information");
+#endif
+
+ return cpu_string;
+}
+
+void LLCPUInfo::stream(std::ostream& s) const
+{
+#if LL_WINDOWS || LL_DARWIN
+ // gather machine information.
+ char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */
+ CProcessor proc;
+ if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE))
+ {
+ s << proc_buf;
+ }
+ else
+ {
+ s << "Unable to collect processor info";
+ }
+#else
+ // *FIX: This works on linux. What will it do on other systems?
+ FILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "r"); /* Flawfinder: ignore */
+ if(cpuinfo)
+ {
+ char line[MAX_STRING]; /* Flawfinder: ignore */
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, cpuinfo))
+ {
+ line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
+ s << line;
+ }
+ fclose(cpuinfo);
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+#endif
+}
+
+LLMemoryInfo::LLMemoryInfo()
+{
+}
+
+#if LL_LINUX
+#include <unistd.h>
+#include <sys/sysinfo.h>
+#endif
+
+U32 LLMemoryInfo::getPhysicalMemory() const
+{
+#if LL_WINDOWS
+ MEMORYSTATUS state;
+ state.dwLength = sizeof(state);
+ GlobalMemoryStatus(&state);
+
+ return (U32)state.dwTotalPhys;
+
+#elif LL_DARWIN
+ // This might work on Linux as well. Someone check...
+ unsigned int phys = 0;
+ int mib[2] = { CTL_HW, HW_PHYSMEM };
+
+ size_t len = sizeof(phys);
+ sysctl(mib, 2, &phys, &len, NULL, 0);
+
+ return phys;
+#elif LL_LINUX
+
+ return getpagesize() * get_phys_pages();
+
+#else
+ return 0;
+
+#endif
+}
+
+void LLMemoryInfo::stream(std::ostream& s) const
+{
+#if LL_WINDOWS
+ MEMORYSTATUS state;
+ state.dwLength = sizeof(state);
+ GlobalMemoryStatus(&state);
+
+ s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl;
+ s << "Total Physical Kb: " << (U32)state.dwTotalPhys/1024 << std::endl;
+ s << "Avail Physical Kb: " << (U32)state.dwAvailPhys/1024 << std::endl;
+ s << "Total page Kb: " << (U32)state.dwTotalPageFile/1024 << std::endl;
+ s << "Avail page Kb: " << (U32)state.dwAvailPageFile/1024 << std::endl;
+ s << "Total Virtual Kb: " << (U32)state.dwTotalVirtual/1024 << std::endl;
+ s << "Avail Virtual Kb: " << (U32)state.dwAvailVirtual/1024 << std::endl;
+#elif LL_DARWIN
+ U64 phys = 0;
+
+ size_t len = sizeof(phys);
+
+ if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
+ {
+ s << "Total Physical Kb: " << phys/1024 << std::endl;
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+
+#else
+ // *FIX: This works on linux. What will it do on other systems?
+ FILE* meminfo = LLFile::fopen(MEMINFO_FILE,"r"); /* Flawfinder: ignore */
+ if(meminfo)
+ {
+ char line[MAX_STRING]; /* Flawfinder: ignore */
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, meminfo))
+ {
+ line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
+ s << line;
+ }
+ fclose(meminfo);
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+#endif
+}
+
+std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+BOOL gunzip_file(const char *srcfile, const char *dstfile)
+{
+ char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */
+ const S32 UNCOMPRESS_BUFFER_SIZE = 32768;
+ BOOL retval = FALSE;
+ gzFile src = NULL;
+ U8 buffer[UNCOMPRESS_BUFFER_SIZE];
+ FILE *dst = NULL;
+ S32 bytes = 0;
+ (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */
+ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */
+ src = gzopen(srcfile, "rb");
+ if (! src) goto err;
+ dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ if (! dst) goto err;
+ do
+ {
+ bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE);
+ fwrite(buffer, sizeof(U8), bytes, dst);
+ } while(gzeof(src) == 0);
+ fclose(dst);
+ dst = NULL;
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = TRUE;
+err:
+ if (src != NULL) gzclose(src);
+ if (dst != NULL) fclose(dst);
+ return retval;
+}
+
+BOOL gzip_file(const char *srcfile, const char *dstfile)
+{
+ const S32 COMPRESS_BUFFER_SIZE = 32768;
+ char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */
+ BOOL retval = FALSE;
+ U8 buffer[COMPRESS_BUFFER_SIZE];
+ gzFile dst = NULL;
+ FILE *src = NULL;
+ S32 bytes = 0;
+ (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */
+ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */
+ dst = gzopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ if (! dst) goto err;
+ src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
+ if (! src) goto err;
+
+ do
+ {
+ bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE,src);
+ gzwrite(dst, buffer, bytes);
+ } while(feof(src) == 0);
+ gzclose(dst);
+ dst = NULL;
+#if LL_WINDOWS
+ // Rename in windows needs the dstfile to not exist.
+ LLFile::remove(dstfile);
+#endif
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = TRUE;
+ err:
+ if (src != NULL) fclose(src);
+ if (dst != NULL) gzclose(dst);
+ return retval;
+}
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
new file mode 100644
index 0000000000..05c975a5fa
--- /dev/null
+++ b/indra/llcommon/llsys.h
@@ -0,0 +1,91 @@
+/**
+ * @file llsys.h
+ * @brief System information debugging classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SYS_H
+#define LL_SYS_H
+
+//
+// The LLOSInfo, LLCPUInfo, and LLMemoryInfo classes are essentially
+// the same, but query different machine subsystems. Here's how you
+// use an LLCPUInfo object:
+//
+// LLCPUInfo info;
+// llinfos << info << llendl;
+//
+
+#include <iosfwd>
+#include <string>
+
+class LLOSInfo
+{
+public:
+ LLOSInfo();
+ void stream(std::ostream& s) const;
+
+ const std::string& getOSString() const;
+
+ S32 mMajorVer;
+ S32 mMinorVer;
+ S32 mBuild;
+
+#ifndef LL_WINDOWS
+ static S32 getMaxOpenFiles();
+#endif
+
+ static U32 getProcessVirtualSizeKB();
+ static U32 getProcessResidentSizeKB();
+private:
+ std::string mOSString;
+};
+
+
+class LLCPUInfo
+{
+public:
+ LLCPUInfo();
+ void stream(std::ostream& s) const;
+
+ std::string getCPUString() const;
+ std::string getCPUStringTerse() const;
+
+ BOOL hasSSE() const { return mHasSSE; }
+ BOOL hasSSE2() const { return mHasSSE2; }
+ S32 getMhz() const { return mCPUMhz; }
+
+ // Family is "AMD Duron" or "Intel Pentium Pro"
+ const std::string& getFamily() const { return mFamily; }
+
+private:
+ BOOL mHasSSE;
+ BOOL mHasSSE2;
+ S32 mCPUMhz;
+ std::string mFamily;
+};
+
+class LLMemoryInfo
+{
+public:
+ LLMemoryInfo();
+ void stream(std::ostream& s) const;
+
+ U32 getPhysicalMemory() const;
+};
+
+
+std::ostream& operator<<(std::ostream& s, const LLOSInfo& info);
+std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info);
+std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info);
+
+// gunzip srcfile into dstfile. Returns FALSE on error.
+BOOL gunzip_file(const char *srcfile, const char *dstfile);
+// gzip srcfile into dstfile. Returns FALSE on error.
+BOOL gzip_file(const char *srcfile, const char *dstfile);
+
+extern LLCPUInfo gSysCPU;
+
+#endif // LL_LLSYS_H
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
new file mode 100644
index 0000000000..bd2dd7c8f5
--- /dev/null
+++ b/indra/llcommon/llthread.cpp
@@ -0,0 +1,330 @@
+/**
+ * @file llthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+#include "llthread.h"
+
+#include "lltimer.h"
+
+#if LL_LINUX
+#include <sched.h>
+#endif
+
+//----------------------------------------------------------------------------
+// Usage:
+// void run_func(LLThread* thread)
+// {
+// }
+// LLThread* thread = new LLThread();
+// thread->run(run_func);
+// ...
+// thread->setQuitting();
+// while(!timeout)
+// {
+// if (thread->isStopped())
+// {
+// delete thread;
+// break;
+// }
+// }
+//
+//----------------------------------------------------------------------------
+
+//
+// Handed to the APR thread creation function
+//
+void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap)
+{
+ LLThread *threadp = (LLThread *)datap;
+
+ // Set thread state to running
+ threadp->mStatus = RUNNING;
+
+ // Run the user supplied function
+ threadp->run();
+
+ llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl;
+
+ // We're done with the run function, this thread is done executing now.
+ threadp->mStatus = STOPPED;
+
+ return NULL;
+}
+
+
+LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
+ mPaused(FALSE),
+ mName(name),
+ mAPRThreadp(NULL),
+ mStatus(STOPPED)
+{
+ // Thread creation probably CAN be paranoid about APR being initialized, if necessary
+ if (poolp)
+ {
+ mIsLocalPool = FALSE;
+ mAPRPoolp = poolp;
+ }
+ else
+ {
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+ mRunCondition = new LLCondition(mAPRPoolp);
+}
+
+
+LLThread::~LLThread()
+{
+ // Warning! If you somehow call the thread destructor from itself,
+ // the thread will die in an unclean fashion!
+ if (mAPRThreadp)
+ {
+ if (!isStopped())
+ {
+ // The thread isn't already stopped
+ // First, set the flag that indicates that we're ready to die
+ setQuitting();
+
+ llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
+ // Now wait a bit for the thread to exit
+ // It's unclear whether I should even bother doing this - this destructor
+ // should netver get called unless we're already stopped, really...
+ S32 counter = 0;
+ const S32 MAX_WAIT = 600;
+ while (counter < MAX_WAIT)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ // Sleep for a tenth of a second
+ ms_sleep(100);
+ yield();
+ counter++;
+ }
+ }
+
+ if (!isStopped())
+ {
+ // This thread just wouldn't stop, even though we gave it time
+ llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+ return;
+ }
+ mAPRThreadp = NULL;
+ }
+
+ delete mRunCondition;
+
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
+}
+
+
+void LLThread::start()
+{
+ apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+
+ // We won't bother joining
+ apr_thread_detach(mAPRThreadp);
+}
+
+//============================================================================
+// Called from MAIN THREAD.
+
+// Request that the thread pause/resume.
+// The thread will pause when (and if) it calls checkPause()
+void LLThread::pause()
+{
+ if (!mPaused)
+ {
+ // this will cause the thread to stop execution as soon as checkPause() is called
+ mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread
+ }
+}
+
+void LLThread::unpause()
+{
+ if (mPaused)
+ {
+ mPaused = 0;
+ }
+
+ wake(); // wake up the thread if necessary
+}
+
+// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+bool LLThread::runCondition(void)
+{
+ // by default, always run. Handling of pause/unpause is done regardless of this function's result.
+ return true;
+}
+
+//============================================================================
+// Called from run() (CHILD THREAD).
+// Stop thread execution if requested until unpaused.
+void LLThread::checkPause()
+{
+ mRunCondition->lock();
+
+ // This is in a while loop because the pthread API allows for spurious wakeups.
+ while(shouldSleep())
+ {
+ mRunCondition->wait(); // unlocks mRunCondition
+ // mRunCondition is locked when the thread wakes up
+ }
+
+ mRunCondition->unlock();
+}
+
+//============================================================================
+
+bool LLThread::isQuitting() const
+{
+ return (QUITTING == mStatus);
+}
+
+
+bool LLThread::isStopped() const
+{
+ return (STOPPED == mStatus);
+}
+
+
+void LLThread::setQuitting()
+{
+ mRunCondition->lock();
+ if (mStatus == RUNNING)
+ {
+ mStatus = QUITTING;
+ }
+ mRunCondition->unlock();
+ wake();
+}
+
+
+// static
+void LLThread::yield()
+{
+#if LL_LINUX
+ sched_yield(); // annoyingly, apr_thread_yield is a noop on linux...
+#else
+ apr_thread_yield();
+#endif
+}
+
+void LLThread::wake()
+{
+ mRunCondition->lock();
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+ mRunCondition->unlock();
+}
+
+void LLThread::wakeLocked()
+{
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+}
+
+//============================================================================
+
+LLMutex::LLMutex(apr_pool_t *poolp) :
+ mAPRMutexp(NULL)
+{
+ if (poolp)
+ {
+ mIsLocalPool = FALSE;
+ mAPRPoolp = poolp;
+ }
+ else
+ {
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_DEFAULT, mAPRPoolp);
+}
+
+
+LLMutex::~LLMutex()
+{
+#if _DEBUG
+ llassert(!isLocked()); // better not be locked!
+#endif
+ apr_thread_mutex_destroy(mAPRMutexp);
+ mAPRMutexp = NULL;
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
+}
+
+
+void LLMutex::lock()
+{
+ apr_thread_mutex_lock(mAPRMutexp);
+}
+
+void LLMutex::unlock()
+{
+ apr_thread_mutex_unlock(mAPRMutexp);
+}
+
+bool LLMutex::isLocked()
+{
+ apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
+ if (APR_STATUS_IS_EBUSY(status))
+ {
+ return true;
+ }
+ else
+ {
+ apr_thread_mutex_unlock(mAPRMutexp);
+ return false;
+ }
+}
+
+//============================================================================
+
+LLCondition::LLCondition(apr_pool_t *poolp) :
+ LLMutex(poolp)
+{
+ // base class (LLMutex) has already ensured that mAPRPoolp is set up.
+
+ apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
+}
+
+
+LLCondition::~LLCondition()
+{
+ apr_thread_cond_destroy(mAPRCondp);
+ mAPRCondp = NULL;
+}
+
+
+void LLCondition::wait()
+{
+ apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
+}
+
+void LLCondition::signal()
+{
+ apr_thread_cond_signal(mAPRCondp);
+}
+
+void LLCondition::broadcast()
+{
+ apr_thread_cond_broadcast(mAPRCondp);
+}
+
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
new file mode 100644
index 0000000000..f6f6bd210a
--- /dev/null
+++ b/indra/llcommon/llthread.h
@@ -0,0 +1,164 @@
+/**
+ * @file llthread.h
+ * @brief Base classes for thread, mutex and condition handling.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTHREAD_H
+#define LL_LLTHREAD_H
+
+#include "llapr.h"
+#include "llapp.h"
+
+#include "apr-1/apr_thread_cond.h"
+
+class LLThread;
+class LLMutex;
+class LLCondition;
+
+class LLThread
+{
+public:
+ typedef enum e_thread_status
+ {
+ STOPPED = 0, // The thread is not running. Not started, or has exited its run function
+ RUNNING = 1, // The thread is currently running
+ QUITTING= 2 // Someone wants this thread to quit
+ } EThreadStatus;
+
+ LLThread(const std::string& name, apr_pool_t *poolp = NULL);
+ virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state.
+
+ static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
+
+
+ bool isQuitting() const;
+ bool isStopped() const;
+
+ // PAUSE / RESUME functionality. See source code for important usage notes.
+public:
+ // Called from MAIN THREAD.
+ void pause();
+ void unpause();
+ bool isPaused() { return mPaused ? true : false; }
+
+ // Cause the thread to wake up and check its condition
+ void wake();
+
+ // Same as above, but to be used when the condition is already locked.
+ void wakeLocked();
+
+ // Called from run() (CHILD THREAD). Pause the thread if requested until unpaused.
+ void checkPause();
+
+ // this kicks off the apr thread
+ void start(void);
+
+ apr_pool_t *getAPRPool() { return mAPRPoolp; }
+
+private:
+ BOOL mPaused;
+
+ // static function passed to APR thread creation routine
+ static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
+
+protected:
+ std::string mName;
+ LLCondition* mRunCondition;
+
+ apr_thread_t *mAPRThreadp;
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+ EThreadStatus mStatus;
+
+ void setQuitting();
+
+ // virtual function overridden by subclass -- this will be called when the thread runs
+ virtual void run(void) = 0;
+
+ // virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+ virtual bool runCondition(void);
+
+ // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition()
+ inline void lockData();
+ inline void unlockData();
+
+ // This is the predicate that decides whether the thread should sleep.
+ // It should only be called with mRunCondition locked, since the virtual runCondition() function may need to access
+ // data structures that are thread-unsafe.
+ bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); }
+
+ // To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following:
+ // mRunCondition->lock();
+ // if(!shouldSleep())
+ // mRunCondition->signal();
+ // mRunCondition->unlock();
+};
+
+//============================================================================
+
+class LLMutex
+{
+public:
+ LLMutex(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+ ~LLMutex();
+
+ void lock(); // blocks
+ void unlock();
+ bool isLocked(); // non-blocking, but does do a lock/unlock so not free
+
+protected:
+ apr_thread_mutex_t *mAPRMutexp;
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+};
+
+// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
+class LLCondition : public LLMutex
+{
+public:
+ LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+ ~LLCondition();
+
+ void wait(); // blocks
+ void signal();
+ void broadcast();
+
+protected:
+ apr_thread_cond_t *mAPRCondp;
+};
+
+class LLMutexLock
+{
+public:
+ LLMutexLock(LLMutex* mutex)
+ {
+ mMutex = mutex;
+ mMutex->lock();
+ }
+ ~LLMutexLock()
+ {
+ mMutex->unlock();
+ }
+private:
+ LLMutex* mMutex;
+};
+
+//============================================================================
+
+void LLThread::lockData()
+{
+ mRunCondition->lock();
+}
+
+void LLThread::unlockData()
+{
+ mRunCondition->unlock();
+}
+
+
+//============================================================================
+
+#endif // LL_LLTHREAD_H
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
new file mode 100644
index 0000000000..bd054f02d8
--- /dev/null
+++ b/indra/llcommon/lltimer.cpp
@@ -0,0 +1,517 @@
+/**
+ * @file lltimer.cpp
+ * @brief Cross-platform objects for doing timing
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+
+#if LL_WINDOWS
+#include <time.h>
+
+#elif LL_LINUX
+#include <time.h>
+#include <sys/time.h>
+#include <sched.h>
+
+#elif LL_DARWIN
+# include <time.h>
+# include <sys/time.h>
+#else
+# error "architecture not supported"
+#endif
+
+
+#include "lltimer.h"
+#include "u64.h"
+
+//
+// Locally used constants
+//
+const U32 SEC_PER_DAY = 86400;
+const F64 SEC_TO_MICROSEC = 1000000.f;
+const U64 SEC_TO_MICROSEC_U64 = 1000000;
+const F64 USEC_TO_SEC_F64 = 0.000001;
+
+
+//---------------------------------------------------------------------------
+// Globals and statics
+//---------------------------------------------------------------------------
+
+S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
+LLTimer* LLTimer::sTimer = NULL;
+
+F64 gClockFrequency = 0.0;
+F64 gClockFrequencyInv = 0.0;
+F64 gClocksToMicroseconds = 0.0;
+U64 gTotalTimeClockCount = 0;
+U64 gLastTotalTimeClockCount = 0;
+
+//
+// Forward declarations
+//
+
+
+//---------------------------------------------------------------------------
+// Implementation
+//---------------------------------------------------------------------------
+
+#if LL_WINDOWS
+void ms_sleep(long ms)
+{
+ Sleep((U32)ms);
+}
+
+void llyield()
+{
+ SleepEx(0, TRUE); // Relinquishes time slice to any thread of equal priority, can be woken up by extended IO functions
+}
+#elif LL_LINUX
+void ms_sleep(long ms)
+{
+ struct timespec t;
+ t.tv_sec = ms / 1000;
+ t.tv_nsec = (ms % 1000) * 1000000l;
+ nanosleep(&t, NULL);
+}
+
+void llyield()
+{
+ sched_yield();
+}
+#elif LL_DARWIN
+void ms_sleep(long ms)
+{
+ struct timespec t;
+ t.tv_sec = ms / 1000;
+ t.tv_nsec = (ms % 1000) * 1000000l;
+ nanosleep(&t, NULL);
+}
+
+void llyield()
+{
+// sched_yield();
+}
+#else
+# error "architecture not supported"
+#endif
+
+//
+// CPU clock/other clock frequency and count functions
+//
+
+#if LL_WINDOWS
+U64 get_clock_count()
+{
+ static bool firstTime = true;
+ static U64 offset;
+ // ensures that callers to this function never have to deal with wrap
+
+ // QueryPerformanceCounter implementation
+ LARGE_INTEGER clock_count;
+ QueryPerformanceCounter(&clock_count);
+ if (firstTime) {
+ offset = clock_count.QuadPart;
+ firstTime = false;
+ }
+ return clock_count.QuadPart - offset;
+}
+
+F64 calc_clock_frequency(U32 uiMeasureMSecs)
+{
+ __int64 freq;
+ QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
+ return (F64)freq;
+}
+#endif // LL_WINDOWS
+
+
+#if LL_LINUX || LL_DARWIN
+// Both Linux and Mac use gettimeofday for accurate time
+F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
+{
+ return 1000000.0; // microseconds, so 1 Mhz.
+}
+
+U64 get_clock_count()
+{
+ // Linux clocks are in microseconds
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
+}
+#endif
+
+
+void update_clock_frequencies()
+{
+ gClockFrequency = calc_clock_frequency(50);
+ gClockFrequencyInv = 1.0/gClockFrequency;
+ gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// returns a U64 number that represents the number of
+// microseconds since the unix epoch - Jan 1, 1970
+U64 totalTime()
+{
+ U64 current_clock_count = get_clock_count();
+ if (!gTotalTimeClockCount)
+ {
+ update_clock_frequencies();
+ gTotalTimeClockCount = current_clock_count;
+
+#if LL_WINDOWS
+ // Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
+ // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
+ // make in the future.
+
+ gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
+#endif
+
+ // Update the last clock count
+ gLastTotalTimeClockCount = current_clock_count;
+ }
+ else
+ {
+ if (current_clock_count >= gLastTotalTimeClockCount)
+ {
+ // No wrapping, we're all okay.
+ gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
+ }
+ else
+ {
+ // We've wrapped. Compensate correctly
+ gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
+ }
+
+ // Update the last clock count
+ gLastTotalTimeClockCount = current_clock_count;
+ }
+
+ // Return the total clock tick count in microseconds.
+ return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+LLTimer::LLTimer()
+{
+ if (!gClockFrequency)
+ {
+ update_clock_frequencies();
+ }
+
+ mStarted = TRUE;
+ reset();
+}
+
+LLTimer::~LLTimer()
+{
+}
+
+// static
+U64 LLTimer::getTotalTime()
+{
+ // simply call into the implementation function.
+ return totalTime();
+}
+
+// static
+F64 LLTimer::getTotalSeconds()
+{
+ return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
+}
+
+void LLTimer::reset()
+{
+ mLastClockCount = get_clock_count();
+ mExpirationTicks = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+U64 LLTimer::getCurrentClockCount()
+{
+ return get_clock_count();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setLastClockCount(U64 current_count)
+{
+ mLastClockCount = current_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static
+U64 getElapsedTimeAndUpdate(U64& lastClockCount)
+{
+ U64 current_clock_count = get_clock_count();
+ U64 result;
+
+ if (current_clock_count >= lastClockCount)
+ {
+ result = current_clock_count - lastClockCount;
+ }
+ else
+ {
+ // time has gone backward
+ result = 0;
+ }
+
+ lastClockCount = current_clock_count;
+
+ return result;
+}
+
+
+F64 LLTimer::getElapsedTimeF64() const
+{
+ U64 last = mLastClockCount;
+ return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
+}
+
+F32 LLTimer::getElapsedTimeF32() const
+{
+ return (F32)getElapsedTimeF64();
+}
+
+F64 LLTimer::getElapsedTimeAndResetF64()
+{
+ return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
+}
+
+F32 LLTimer::getElapsedTimeAndResetF32()
+{
+ return (F32)getElapsedTimeAndResetF64();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setTimerExpirySec(F32 expiration)
+{
+ mExpirationTicks = get_clock_count()
+ + (U64)((F32)(expiration * gClockFrequency));
+}
+
+F32 LLTimer::getRemainingTimeF32()
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks > mExpirationTicks)
+ {
+ return 0.0f;
+ }
+ return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
+}
+
+
+BOOL LLTimer::checkExpirationAndReset(F32 expiration)
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks < mExpirationTicks)
+ {
+ return FALSE;
+ }
+
+ mExpirationTicks = cur_ticks
+ + (U64)((F32)(expiration * gClockFrequency));
+ return TRUE;
+}
+
+
+BOOL LLTimer::hasExpired()
+{
+ return (get_clock_count() >= mExpirationTicks)
+ ? TRUE : FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+BOOL LLTimer::knownBadTimer()
+{
+ BOOL failed = FALSE;
+
+#if LL_WINDOWS
+ WCHAR bad_pci_list[][10] = {L"1039:0530",
+ L"1039:0620",
+ L"10B9:0533",
+ L"10B9:1533",
+ L"1106:0596",
+ L"1106:0686",
+ L"1166:004F",
+ L"1166:0050",
+ L"8086:7110",
+ L"\0"
+ };
+
+ HKEY hKey = NULL;
+ LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
+ KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
+
+ WCHAR name[1024];
+ DWORD name_len = 1024;
+ FILETIME scrap;
+
+ S32 key_num = 0;
+ WCHAR pci_id[10];
+
+ wcscpy(pci_id, L"0000:0000"); /*Flawfinder: ignore*/
+
+ while (nResult == ERROR_SUCCESS)
+ {
+ nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
+
+ if (nResult == ERROR_SUCCESS)
+ {
+ memcpy(&pci_id[0],&name[4],4); /* Flawfinder: ignore */
+ memcpy(&pci_id[5],&name[13],4); /* Flawfinder: ignore */
+
+ for (S32 check = 0; bad_pci_list[check][0]; check++)
+ {
+ if (!wcscmp(pci_id, bad_pci_list[check]))
+ {
+// llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
+ failed = TRUE;
+ break;
+ }
+ }
+// llinfo << "PCI chipset found: " << pci_id << endl;
+ name_len = 1024;
+ }
+ }
+#endif
+ return(failed);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// NON-MEMBER FUNCTIONS
+//
+///////////////////////////////////////////////////////////////////////////////
+
+U32 time_corrected()
+{
+ U32 corrected_time = (U32)time(NULL) + gUTCOffset;
+ return corrected_time;
+}
+
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+BOOL is_daylight_savings()
+{
+ time_t now = time(NULL);
+
+ // Internal buffer to local server time
+ struct tm* internal_time = localtime(&now);
+
+ // tm_isdst > 0 => daylight savings
+ // tm_isdst = 0 => not daylight savings
+ // tm_isdst < 0 => can't tell
+ return (internal_time->tm_isdst > 0);
+}
+
+
+struct tm* utc_to_pacific_time(S32 utc_time, BOOL pacific_daylight_time)
+{
+ time_t unix_time = (time_t)utc_time;
+
+ S32 pacific_offset_hours;
+ if (pacific_daylight_time)
+ {
+ pacific_offset_hours = -7;
+ }
+ else
+ {
+ pacific_offset_hours = -8;
+ }
+
+ // We subtract off the PST/PDT offset _before_ getting
+ // "UTC" time, because this will handle wrapping around
+ // for 5 AM UTC -> 10 PM PDT of the previous day.
+ unix_time += pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
+
+ // Internal buffer to PST/PDT (see above)
+ struct tm* internal_time = gmtime(&unix_time);
+
+ /*
+ // Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
+ if (pacific_daylight_time)
+ {
+ internal_time->tm_isdst = 1;
+ }
+ */
+
+ return internal_time;
+}
+
+
+void microsecondsToTimecodeString(U64 current_time, char *tcstring)
+{
+ U64 hours;
+ U64 minutes;
+ U64 seconds;
+ U64 frames;
+ U64 subframes;
+
+ hours = current_time / (U64)3600000000ul;
+ minutes = current_time / (U64)60000000;
+ minutes %= 60;
+ seconds = current_time / (U64)1000000;
+ seconds %= 60;
+ frames = current_time / (U64)41667;
+ frames %= 24;
+ subframes = current_time / (U64)42;
+ subframes %= 100;
+
+ sprintf(tcstring,"%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes); /* Flawfinder: ignore */
+}
+
+
+void secondsToTimecodeString(F32 current_time, char *tcstring)
+{
+ microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// LLEventTimer Implementation
+//
+//////////////////////////////////////////////////////////////////////////////
+
+std::list<LLEventTimer*> LLEventTimer::sActiveList;
+
+LLEventTimer::LLEventTimer(F32 period)
+: mTimer()
+{
+ mPeriod = period;
+ sActiveList.push_back(this);
+}
+
+LLEventTimer::~LLEventTimer()
+{
+ sActiveList.remove(this);
+}
+
+void LLEventTimer::updateClass()
+{
+ for (std::list<LLEventTimer*>::iterator iter = sActiveList.begin(); iter != sActiveList.end(); )
+ {
+ LLEventTimer* timer = *iter++;
+ F32 et = timer->mTimer.getElapsedTimeF32();
+ if (et > timer->mPeriod) {
+ timer->mTimer.reset();
+ timer->tick();
+ }
+ }
+}
+
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
new file mode 100644
index 0000000000..69e786c50a
--- /dev/null
+++ b/indra/llcommon/lltimer.h
@@ -0,0 +1,142 @@
+/**
+ * @file lltimer.h
+ * @brief Cross-platform objects for doing timing
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TIMER_H
+#define LL_TIMER_H
+
+#if LL_LINUX || LL_DARWIN
+# include <time.h>
+# include <sys/time.h>
+#endif
+
+#include <list>
+
+// units conversions
+#ifndef USEC_PER_SEC
+ const U32 USEC_PER_SEC = 1000000;
+#endif
+const U32 SEC_PER_MIN = 60;
+const U32 MIN_PER_HOUR = 60;
+const U32 USEC_PER_MIN = USEC_PER_SEC * SEC_PER_MIN;
+const U32 USEC_PER_HOUR = USEC_PER_MIN * MIN_PER_HOUR;
+const U32 SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR;
+const F64 SEC_PER_USEC = 1.0 / (F64) USEC_PER_SEC;
+
+class LLTimer
+{
+public:
+ static LLTimer *sTimer; // global timer
+
+protected:
+ U64 mLastClockCount;
+ U64 mExpirationTicks;
+ BOOL mStarted;
+
+public:
+ LLTimer();
+ ~LLTimer();
+
+ static void initClass() { if (!sTimer) sTimer = new LLTimer; }
+ static void cleanupClass() { delete sTimer; sTimer = NULL; }
+
+ // Return a high precision number of seconds since the start of
+ // this application instance.
+ static F64 getElapsedSeconds()
+ {
+ return sTimer->getElapsedTimeF64();
+ }
+
+ // Return a high precision usec since epoch
+ static U64 getTotalTime();
+
+ // Return a high precision seconds since epoch
+ static F64 getTotalSeconds();
+
+
+ // MANIPULATORS
+ void start() { reset(); mStarted = TRUE; }
+ void stop() { mStarted = FALSE; }
+ void reset(); // Resets the timer
+ void setLastClockCount(U64 current_count); // Sets the timer so that the next elapsed call will be relative to this time
+ void setTimerExpirySec(F32 expiration);
+ BOOL checkExpirationAndReset(F32 expiration);
+ BOOL hasExpired();
+ F32 getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
+ F64 getElapsedTimeAndResetF64();
+
+ F32 getRemainingTimeF32();
+
+ static BOOL knownBadTimer();
+
+ // ACCESSORS
+ F32 getElapsedTimeF32() const; // Returns elapsed time in seconds
+ F64 getElapsedTimeF64() const; // Returns elapsed time in seconds
+
+ BOOL getStarted() const { return mStarted; }
+
+
+ static U64 getCurrentClockCount(); // Returns the raw clockticks
+};
+
+//
+// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
+//
+U64 get_clock_count();
+F64 calc_clock_frequency(U32 msecs);
+void update_clock_frequencies();
+
+
+// Sleep for milliseconds
+void ms_sleep(long ms);
+
+// Yield
+//void llyield(); // Yield your timeslice - not implemented yet for Mac, so commented out.
+
+// Returns the correct UTC time in seconds, like time(NULL).
+// Useful on the viewer, which may have its local clock set wrong.
+U32 time_corrected();
+
+// Correction factor used by time_corrected() above.
+extern S32 gUTCOffset;
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+BOOL is_daylight_savings();
+
+// Converts internal "struct tm" time buffer to Pacific Standard/Daylight Time
+// Usage:
+// S32 utc_time;
+// utc_time = time_corrected();
+// struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight);
+struct tm* utc_to_pacific_time(S32 utc_time, BOOL pacific_daylight_time);
+
+void microsecondsToTimecodeString(U64 current_time, char *tcstring);
+void secondsToTimecodeString(F32 current_time, char *tcstring);
+
+// class for scheduling a function to be called at a given frequency (approximate, inprecise)
+class LLEventTimer
+{
+public:
+ LLEventTimer(F32 period); // period is the amount of time between each call to tick()
+ virtual ~LLEventTimer();
+
+ //function to be called at the supplied frequency
+ virtual void tick() = 0;
+
+ static void updateClass();
+
+protected:
+ LLTimer mTimer;
+ F32 mPeriod;
+
+private:
+ //list of active timers
+ static std::list<LLEventTimer*> sActiveList;
+};
+
+#endif
diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
new file mode 100644
index 0000000000..1b66173399
--- /dev/null
+++ b/indra/llcommon/lluri.cpp
@@ -0,0 +1,579 @@
+/**
+ * @file lluri.cpp
+ * @author Phoenix
+ * @date 2006-02-08
+ * @brief Implementation of the LLURI class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lluri.h"
+#include "llsd.h"
+
+// uric = reserved | unreserved | escaped
+// reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
+// unreserved = alphanum | mark
+// mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+// escaped = "%" hex hex
+static const char* ESCAPED_CHARACTERS[256] =
+{
+ "%00", // 0
+ "%01", // 1
+ "%02", // 2
+ "%03", // 3
+ "%04", // 4
+ "%05", // 5
+ "%06", // 6
+ "%07", // 7
+ "%08", // 8
+ "%09", // 9
+ "%0a", // 10
+ "%0b", // 11
+ "%0c", // 12
+ "%0d", // 13
+ "%0e", // 14
+ "%0f", // 15
+ "%10", // 16
+ "%11", // 17
+ "%12", // 18
+ "%13", // 19
+ "%14", // 20
+ "%15", // 21
+ "%16", // 22
+ "%17", // 23
+ "%18", // 24
+ "%19", // 25
+ "%1a", // 26
+ "%1b", // 27
+ "%1c", // 28
+ "%1d", // 29
+ "%1e", // 30
+ "%1f", // 31
+ "%20", // 32
+ "!", // 33
+ "%22", // 34
+ "%23", // 35
+ "$", // 36
+ "%25", // 37
+ "&", // 38
+ "'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "%3c", // 60
+ "=", // 61
+ "%3e", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "%5b", // 91
+ "%5c", // 92
+ "%5d", // 93
+ "%5e", // 94
+ "_", // 95
+ "%60", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "%7b", // 123
+ "%7c", // 124
+ "%7d", // 125
+ "~", // 126
+ "%7f", // 127
+ "%80", // 128
+ "%81", // 129
+ "%82", // 130
+ "%83", // 131
+ "%84", // 132
+ "%85", // 133
+ "%86", // 134
+ "%87", // 135
+ "%88", // 136
+ "%89", // 137
+ "%8a", // 138
+ "%8b", // 139
+ "%8c", // 140
+ "%8d", // 141
+ "%8e", // 142
+ "%8f", // 143
+ "%90", // 144
+ "%91", // 145
+ "%92", // 146
+ "%93", // 147
+ "%94", // 148
+ "%95", // 149
+ "%96", // 150
+ "%97", // 151
+ "%98", // 152
+ "%99", // 153
+ "%9a", // 154
+ "%9b", // 155
+ "%9c", // 156
+ "%9d", // 157
+ "%9e", // 158
+ "%9f", // 159
+ "%a0", // 160
+ "%a1", // 161
+ "%a2", // 162
+ "%a3", // 163
+ "%a4", // 164
+ "%a5", // 165
+ "%a6", // 166
+ "%a7", // 167
+ "%a8", // 168
+ "%a9", // 169
+ "%aa", // 170
+ "%ab", // 171
+ "%ac", // 172
+ "%ad", // 173
+ "%ae", // 174
+ "%af", // 175
+ "%b0", // 176
+ "%b1", // 177
+ "%b2", // 178
+ "%b3", // 179
+ "%b4", // 180
+ "%b5", // 181
+ "%b6", // 182
+ "%b7", // 183
+ "%b8", // 184
+ "%b9", // 185
+ "%ba", // 186
+ "%bb", // 187
+ "%bc", // 188
+ "%bd", // 189
+ "%be", // 190
+ "%bf", // 191
+ "%c0", // 192
+ "%c1", // 193
+ "%c2", // 194
+ "%c3", // 195
+ "%c4", // 196
+ "%c5", // 197
+ "%c6", // 198
+ "%c7", // 199
+ "%c8", // 200
+ "%c9", // 201
+ "%ca", // 202
+ "%cb", // 203
+ "%cc", // 204
+ "%cd", // 205
+ "%ce", // 206
+ "%cf", // 207
+ "%d0", // 208
+ "%d1", // 209
+ "%d2", // 210
+ "%d3", // 211
+ "%d4", // 212
+ "%d5", // 213
+ "%d6", // 214
+ "%d7", // 215
+ "%d8", // 216
+ "%d9", // 217
+ "%da", // 218
+ "%db", // 219
+ "%dc", // 220
+ "%dd", // 221
+ "%de", // 222
+ "%df", // 223
+ "%e0", // 224
+ "%e1", // 225
+ "%e2", // 226
+ "%e3", // 227
+ "%e4", // 228
+ "%e5", // 229
+ "%e6", // 230
+ "%e7", // 231
+ "%e8", // 232
+ "%e9", // 233
+ "%ea", // 234
+ "%eb", // 235
+ "%ec", // 236
+ "%ed", // 237
+ "%ee", // 238
+ "%ef", // 239
+ "%f0", // 240
+ "%f1", // 241
+ "%f2", // 242
+ "%f3", // 243
+ "%f4", // 244
+ "%f5", // 245
+ "%f6", // 246
+ "%f7", // 247
+ "%f8", // 248
+ "%f9", // 249
+ "%fa", // 250
+ "%fb", // 251
+ "%fc", // 252
+ "%fd", // 253
+ "%fe", // 254
+ "%ff" // 255
+};
+
+LLURI::LLURI()
+{
+}
+
+LLURI::LLURI(const std::string& escaped_str)
+{
+ std::string::size_type delim_pos, delim_pos2;
+ delim_pos = escaped_str.find(':');
+ std::string temp;
+ if (delim_pos == std::string::npos)
+ {
+ mScheme = "";
+ mEscapedOpaque = escaped_str;
+ }
+ else
+ {
+ mScheme = escaped_str.substr(0, delim_pos);
+ mEscapedOpaque = escaped_str.substr(delim_pos+1);
+ }
+
+ if (mScheme == "http" || mScheme == "https" || mScheme == "ftp")
+ {
+ if (mEscapedOpaque.substr(0,2) != "//")
+ {
+ return;
+ }
+
+ delim_pos = mEscapedOpaque.find('/', 2);
+ delim_pos2 = mEscapedOpaque.find('?', 2);
+ // no path, no query
+ if (delim_pos == std::string::npos &&
+ delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2);
+ mEscapedPath = "";
+ }
+ // path exist, no query
+ else if (delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ // no path, only query
+ else if (delim_pos == std::string::npos ||
+ delim_pos2 < delim_pos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos2);
+ }
+ // path and query
+ else
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ }
+
+ delim_pos = mEscapedPath.find('?');
+ if (delim_pos != std::string::npos)
+ {
+ mEscapedQuery = mEscapedPath.substr(delim_pos+1);
+ mEscapedPath = mEscapedPath.substr(0,delim_pos);
+ }
+}
+
+LLURI::~LLURI()
+{
+}
+
+
+LLURI LLURI::buildHTTP(const std::string& host_port,
+ const LLSD& path)
+{
+ LLURI result;
+ result.mScheme = "HTTP";
+ // TODO: deal with '/' '?' '#' in host_port
+ result.mEscapedAuthority = "//" + escape(host_port);
+ if (path.isArray())
+ {
+ // break out and escape each path component
+ for (LLSD::array_const_iterator it = path.beginArray();
+ it != path.endArray();
+ ++it)
+ {
+ lldebugs << "PATH: inserting " << it->asString() << llendl;
+ result.mEscapedPath += "/" + escape(it->asString());
+ }
+ }
+ result.mEscapedOpaque = result.mEscapedAuthority +
+ result.mEscapedPath;
+ return result;
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& host_port,
+ const LLSD& path,
+ const LLSD& query)
+{
+ LLURI result = buildHTTP(host_port, path);
+ // break out and escape each query component
+ if (query.isMap())
+ {
+ for (LLSD::map_const_iterator it = query.beginMap();
+ it != query.endMap();
+ it++)
+ {
+ result.mEscapedQuery += escape(it->first) +
+ (it->second.isUndefined() ? "" : "=" + it->second.asString()) +
+ "&";
+ }
+ if (query.size() > 0)
+ {
+ result.mEscapedOpaque += "?" + result.mEscapedQuery;
+ }
+ }
+ return result;
+}
+
+std::string LLURI::asString() const
+{
+ if (mScheme.empty())
+ {
+ return mEscapedOpaque;
+ }
+ else
+ {
+ return mScheme + ":" + mEscapedOpaque;
+ }
+}
+
+std::string LLURI::scheme() const
+{
+ return mScheme;
+}
+
+std::string LLURI::opaque() const
+{
+ return unescape(mEscapedOpaque);
+}
+
+std::string LLURI::authority() const
+{
+ return unescape(mEscapedAuthority);
+}
+
+
+namespace {
+ void findAuthorityParts(const std::string& authority,
+ std::string& user,
+ std::string& host,
+ std::string& port)
+ {
+ std::string::size_type start_pos = authority.find('@');
+ if (start_pos == std::string::npos)
+ {
+ user = "";
+ start_pos = 0;
+ }
+ else
+ {
+ user = authority.substr(0, start_pos);
+ start_pos += 1;
+ }
+
+ std::string::size_type end_pos = authority.find(':', start_pos);
+ if (end_pos == std::string::npos)
+ {
+ host = authority.substr(start_pos);
+ port = "";
+ }
+ else
+ {
+ host = authority.substr(start_pos, end_pos - start_pos);
+ port = authority.substr(end_pos + 1);
+ }
+ }
+}
+
+std::string LLURI::hostName() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ return unescape(host);
+}
+
+U16 LLURI::hostPort() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ if (port.empty())
+ {
+ if (mScheme == "http")
+ return 80;
+ if (mScheme == "https")
+ return 443;
+ if (mScheme == "ftp")
+ return 21;
+ return 0;
+ }
+ return atoi(port.c_str());
+}
+
+std::string LLURI::path() const
+{
+ return unescape(mEscapedPath);
+}
+
+std::string LLURI::query() const
+{
+ return unescape(mEscapedQuery);
+}
+
+LLSD LLURI::queryMap() const
+{
+ return queryMap(mEscapedQuery);
+}
+
+// static
+LLSD LLURI::queryMap(std::string escaped_query_string)
+{
+ lldebugs << "LLURI::queryMap query params: " << escaped_query_string << llendl;
+
+ LLSD result = LLSD::emptyArray();
+ while(!escaped_query_string.empty())
+ {
+ // get tuple first
+ std::string tuple;
+ std::string::size_type tuple_begin = escaped_query_string.find('&');
+ if (tuple_begin != std::string::npos)
+ {
+ tuple = escaped_query_string.substr(0, tuple_begin);
+ escaped_query_string = escaped_query_string.substr(tuple_begin+1);
+ }
+ else
+ {
+ tuple = escaped_query_string;
+ escaped_query_string = "";
+ }
+ if (tuple.empty()) continue;
+
+ // parse tuple
+ std::string::size_type key_end = tuple.find('=');
+ if (key_end != std::string::npos)
+ {
+ std::string key = unescape(tuple.substr(0,key_end));
+ std::string value = unescape(tuple.substr(key_end+1));
+ lldebugs << "inserting key " << key << " value " << value << llendl;
+ result[key] = value;
+ }
+ else
+ {
+ lldebugs << "inserting key " << unescape(tuple) << " value true" << llendl;
+ result[unescape(tuple)] = true;
+ }
+ }
+ return result;
+}
+
+// static
+std::string LLURI::escape(const std::string& str)
+{
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ S32 c;
+ for(; it != end; ++it)
+ {
+ c = (S32)(*it);
+ ostr << ESCAPED_CHARACTERS[c];
+ }
+ return ostr.str();
+}
+
+// static
+std::string LLURI::unescape(const std::string& str)
+{
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ for(; it != end; ++it)
+ {
+ if((*it) == '%')
+ {
+ ++it;
+ if(it == end) break;
+ U8 c = hex_as_nybble(*it++);
+ c = c << 4;
+ if (it == end) break;
+ c |= hex_as_nybble(*it);
+ ostr.put((char)c);
+ }
+ else
+ {
+ ostr.put(*it);
+ }
+ }
+ return ostr.str();
+}
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
new file mode 100644
index 0000000000..3fc62aeb98
--- /dev/null
+++ b/indra/llcommon/lluri.h
@@ -0,0 +1,72 @@
+/**
+ * @file lluri.h
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Declaration of the URI class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURI_H
+#define LL_LLURI_H
+
+#include <string>
+
+class LLSD;
+
+/**
+ *
+ * LLURI instances are immutable
+ * See: http://www.ietf.org/rfc/rfc3986.txt
+ *
+ */
+class LLURI
+{
+public:
+ LLURI();
+ LLURI(const std::string& escaped_str);
+ // construct from escaped string, as would be transmitted on the net
+
+ ~LLURI();
+
+ static LLURI buildHTTP(const std::string& host_port,
+ const LLSD& path);
+ static LLURI buildHTTP(const std::string& host_port,
+ const LLSD& path,
+ const LLSD& query);
+
+ std::string asString() const;
+ // the whole URI, escaped as needed
+
+ // Parts of a URI
+ // These functions return parts of the decoded URI. The returned
+ // strings are un-escaped as needed
+
+ // for all schemes
+ std::string scheme() const; // ex.: "http", note lack of colon
+ std::string opaque() const; // everything after the colon
+
+ // for schemes that follow path like syntax (http, https, ftp)
+ std::string authority() const; // ex.: "bob@host.com:80"
+ std::string hostName() const; // ex.: "host.com"
+ U16 hostPort() const; // ex.: 80, will include implicit port
+ std::string path() const; // ex.: "/abc/def", includes leading slash
+// LLSD pathArray() const; // above decoded into an array of strings
+ std::string query() const; // ex.: "x=34", section after "?"
+ LLSD queryMap() const; // above decoded into a map
+ static LLSD queryMap(std::string escaped_query_string);
+
+ // Escaping Utilities
+ static std::string escape(const std::string& str);
+ static std::string unescape(const std::string& str);
+
+private:
+ std::string mScheme;
+ std::string mEscapedOpaque;
+ std::string mEscapedAuthority;
+ std::string mEscapedPath;
+ std::string mEscapedQuery;
+};
+
+#endif // LL_LLURI_H
diff --git a/indra/llcommon/lluuidhashmap.h b/indra/llcommon/lluuidhashmap.h
new file mode 100644
index 0000000000..f7d32b1fe0
--- /dev/null
+++ b/indra/llcommon/lluuidhashmap.h
@@ -0,0 +1,557 @@
+/**
+ * @file lluuidhashmap.h
+ * @brief A uuid based hash map.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUUIDHASHMAP_H
+#define LL_LLUUIDHASHMAP_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+#include "lluuid.h"
+
+// UUID hash map
+
+ /*
+ LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
+ LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
+
+ LLDynamicArray<LLUUID> source_ids;
+ const S32 COUNT = 100000;
+ S32 q;
+ for (q = 0; q < COUNT; q++)
+ {
+ llinfos << "Creating" << llendl;
+ LLUUID id;
+ id.generate();
+ //llinfos << q << ":" << id << llendl;
+ uuid_pair pair;
+ pair.mUUID = id;
+ pair.mValue = q;
+ foo.set(id, pair);
+ source_ids.put(id);
+ //ms_sleep(1);
+ }
+
+ uuid_pair cur;
+ llinfos << "Iterating" << llendl;
+ for (cur = bar.first(); !bar.done(); cur = bar.next())
+ {
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value iterated!" << llendl;
+ }
+ //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
+ //ms_sleep(1);
+ }
+
+ llinfos << "Finding" << llendl;
+ for (q = 0; q < COUNT; q++)
+ {
+ cur = foo.get(source_ids[q]);
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value found!" << llendl;
+ }
+ //llinfos << res.mValue << ":" << res.mUUID << llendl;
+ //ms_sleep(1);
+ }
+
+ llinfos << "Removing" << llendl;
+ for (q = 0; q < COUNT/2; q++)
+ {
+ if (!foo.remove(source_ids[q]))
+ {
+ llerrs << "Remove failed!" << llendl;
+ }
+ //ms_sleep(1);
+ }
+
+ llinfos << "Iterating" << llendl;
+ for (cur = bar.first(); !bar.done(); cur = bar.next())
+ {
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value found!" << llendl;
+ }
+ //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
+ //ms_sleep(1);
+ }
+ llinfos << "Done with UUID map test" << llendl;
+
+ return 0;
+ */
+
+
+//
+// LLUUIDHashNode
+//
+
+template <class DATA, int SIZE>
+class LLUUIDHashNode
+{
+public:
+ LLUUIDHashNode();
+
+public:
+ S32 mCount;
+ U8 mKey[SIZE];
+ DATA mData[SIZE];
+ LLUUIDHashNode<DATA, SIZE> *mNextNodep;
+};
+
+
+//
+// LLUUIDHashNode implementation
+//
+template <class DATA, int SIZE>
+LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
+{
+ mCount = 0;
+ mNextNodep = NULL;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+class LLUUIDHashMap
+{
+public:
+ // basic constructor including sorter
+ LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
+ const DATA_TYPE &null_data);
+ ~LLUUIDHashMap();
+
+ inline DATA_TYPE &get(const LLUUID &uuid);
+ inline BOOL check(const LLUUID &uuid) const;
+ inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
+ inline BOOL remove(const LLUUID &uuid);
+ void removeAll();
+
+ inline S32 getLength() const; // Warning, NOT O(1!)
+public:
+ BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
+ LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
+
+ S32 mIterCount;
+protected:
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLUUIDHashMap implementation
+//
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
+ const DATA_TYPE &null_data)
+: mEquals(equals),
+ mIterCount(0),
+ mNull(null_data)
+{ }
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
+{
+ removeAll();
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
+{
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+
+ BOOL first = TRUE;
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ nodep->mData[i] = mNull;
+ }
+
+ nodep->mCount = 0;
+ // Done with all objects in this node, go to the next.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
+ nodep = nodep->mNextNodep;
+
+ // Delete the node if it's not the first node
+ if (first)
+ {
+ first = FALSE;
+ curp->mNextNodep = NULL;
+ }
+ else
+ {
+ delete curp;
+ }
+ }
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
+{
+ S32 count = 0;
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ while (nodep)
+ {
+ count += nodep->mCount;
+ nodep = nodep->mNextNodep;
+ }
+ }
+ return count;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
+{
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ // Grab the second byte of the UUID, which is the key for the node data
+ const S32 second_byte = uuid.mData[1];
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // The second byte matched, and our equality test passed.
+ // We found it.
+ return nodep->mData[i];
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ }
+ return mNull;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
+{
+ const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ // Grab the second byte of the UUID, which is the key for the node data
+ const S32 second_byte = uuid.mData[1];
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // The second byte matched, and our equality test passed.
+ // We found it.
+ return TRUE;
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ }
+
+ // Didn't find anything
+ return FALSE;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
+{
+ // Set is just like a normal find, except that if we find a match
+ // we replace it with the input value.
+ // If we don't find a match, we append to the end of the list.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ const S32 second_byte = uuid.mData[1];
+ while (1)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // We found a match for this key, replace the data with
+ // the incoming data.
+ nodep->mData[i] = data;
+ return nodep->mData[i];
+ }
+ }
+ if (!nodep->mNextNodep)
+ {
+ // We've iterated through all of the keys without finding a match
+ if (i < SIZE)
+ {
+ // There's still some space on this node, append
+ // the key and data to it.
+ nodep->mKey[i] = second_byte;
+ nodep->mData[i] = data;
+ nodep->mCount++;
+
+ return nodep->mData[i];
+ }
+ else
+ {
+ // This node is full, append a new node to the end.
+ nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
+ nodep->mNextNodep->mKey[0] = second_byte;
+ nodep->mNextNodep->mData[0] = data;
+ nodep->mNextNodep->mCount = 1;
+
+ return nodep->mNextNodep->mData[0];
+ }
+ }
+
+ // No match on this node, go to the next
+ nodep = nodep->mNextNodep;
+ }
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
+{
+ if (mIterCount)
+ {
+ // We don't allow remove when we're iterating, it's bad karma!
+ llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
+ }
+ // Remove is the trickiest operation.
+ // What we want to do is swap the last element of the last
+ // node if we find the one that we want to remove, but we have
+ // to deal with deleting the node from the tail if it's empty, but
+ // NOT if it's the only node left.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
+
+ // Not empty, we need to search through the nodes
+ const S32 second_byte = uuid.mData[1];
+
+ // A modification of the standard search algorithm.
+ while (nodep)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // We found the node that we want to remove.
+ // Find the last (and next-to-last) node, and the index of the last
+ // element. We could conceviably start from the node we're on,
+ // but that makes it more complicated, this is easier.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
+ LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
+
+ // Find the last and next-to-last
+ while (lastp->mNextNodep)
+ {
+ prevp = lastp;
+ lastp = lastp->mNextNodep;
+ }
+
+ // First, swap in the last to the current location.
+ nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
+ nodep->mData[i] = lastp->mData[lastp->mCount - 1];
+
+ // Now, we delete the entry
+ lastp->mCount--;
+ lastp->mData[lastp->mCount] = mNull;
+
+ if (!lastp->mCount)
+ {
+ // We deleted the last element!
+ if (lastp != &mNodes[uuid.mData[0]])
+ {
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete lastp;
+ }
+ }
+ return TRUE;
+ }
+ }
+
+ // Iterate to the next node, we've scanned all the entries in this one.
+ nodep = nodep->mNextNodep;
+ }
+ return FALSE;
+}
+
+
+//
+// LLUUIDHashMapIter
+//
+
+template <class DATA_TYPE, int SIZE>
+class LLUUIDHashMapIter
+{
+public:
+ LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ ~LLUUIDHashMapIter();
+
+
+ inline void first();
+ inline void next();
+ inline BOOL done() const;
+
+ DATA_TYPE& operator*() const
+ {
+ return mCurHashNodep->mData[mCurHashNodeKey];
+ }
+ DATA_TYPE* operator->() const
+ {
+ return &(operator*());
+ }
+
+protected:
+ LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
+ LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
+
+ S32 mCurHashMapNodeNum;
+ S32 mCurHashNodeKey;
+
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLUUIDHashMapIter Implementation
+//
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ mHashMapp = hash_mapp;
+ mCurHashNodep = NULL;
+ mCurHashMapNodeNum = 0;
+ mCurHashNodeKey = 0;
+}
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
+{
+ if (mCurHashNodep)
+ {
+ // We're partway through an iteration, we can clean up now
+ mHashMapp->mIterCount--;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ for (i = 0; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ if (!mCurHashNodep)
+ {
+ // Increment, since it's no longer safe for us to do a remove
+ mHashMapp->mIterCount++;
+ }
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // Completely empty!
+ mCurHashNodep = NULL;
+ //return mNull;
+ return;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
+{
+ return mCurHashNodep ? FALSE : TRUE;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
+{
+ // No current entry, this iterator is done
+ if (!mCurHashNodep)
+ {
+ //return mNull;
+ return;
+ }
+
+ // Go to the next element
+ mCurHashNodeKey++;
+ if (mCurHashNodeKey < mCurHashNodep->mCount)
+ {
+ // We're not done with this node, return the current element
+ //return mCurHashNodep->mData[mCurHashNodeKey];
+ return;
+ }
+
+ // Done with this node, move to the next
+ mCurHashNodep = mCurHashNodep->mNextNodep;
+ if (mCurHashNodep)
+ {
+ // Return the first element
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+
+ // Find the next non-empty node (keyed on the first byte)
+ mCurHashMapNodeNum++;
+
+ S32 i;
+ for (i = mCurHashMapNodeNum; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ // We found one that wasn't empty
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // OK, we're done, nothing else to iterate
+ mCurHashNodep = NULL;
+ mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
+ //return mNull;
+}
+
+#endif // LL_LLUUIDHASHMAP_H
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
new file mode 100644
index 0000000000..a9370c8f6d
--- /dev/null
+++ b/indra/llcommon/llworkerthread.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file llworkerthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llworkerthread.h"
+#include "llstl.h"
+
+#if USE_FRAME_CALLBACK_MANAGER
+#include "llframecallbackmanager.h"
+#endif
+
+//============================================================================
+
+/*static*/ LLWorkerThread* LLWorkerThread::sLocal = NULL;
+/*static*/ std::set<LLWorkerThread*> LLWorkerThread::sThreadList;
+
+//============================================================================
+// Run on MAIN thread
+
+//static
+void LLWorkerThread::initClass(bool local_is_threaded, bool local_run_always)
+{
+ if (!sLocal)
+ {
+ sLocal = new LLWorkerThread(local_is_threaded, local_run_always);
+ }
+}
+
+//static
+void LLWorkerThread::cleanupClass()
+{
+ if (sLocal)
+ {
+ while (sLocal->getPending())
+ {
+ sLocal->update(0);
+ }
+ delete sLocal;
+ sLocal = NULL;
+ llassert(sThreadList.size() == 0);
+ }
+}
+
+//static
+S32 LLWorkerThread::updateClass(U32 ms_elapsed)
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->update(ms_elapsed);
+ }
+ return getAllPending();
+}
+
+//static
+S32 LLWorkerThread::getAllPending()
+{
+ S32 res = 0;
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ res += (*iter)->getPending();
+ }
+ return res;
+}
+
+//static
+void LLWorkerThread::pauseAll()
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->pause();
+ }
+}
+
+//static
+void LLWorkerThread::waitOnAllPending()
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->waitOnPending();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+LLWorkerThread::LLWorkerThread(bool threaded, bool runalways) :
+ LLQueuedThread("Worker", threaded, runalways)
+{
+ sThreadList.insert(this);
+}
+
+LLWorkerThread::~LLWorkerThread()
+{
+ llverify(sThreadList.erase(this) == 1);
+ // ~LLQueuedThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+
+LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 param, U32 priority)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, priority, workerclass, param);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "add called after LLWorkerThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+bool LLWorkerThread::processRequest(QueuedRequest* qreq)
+{
+ Request *req = (Request*)qreq;
+
+ req->getWorkerClass()->setWorking(true);
+
+ bool complete = req->getWorkerClass()->doWork(req->getParam());
+
+ req->getWorkerClass()->setWorking(false);
+
+ LLThread::yield(); // worker thread should yield after each request
+
+ return complete;
+}
+
+//============================================================================
+
+LLWorkerThread::Request::Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
+ LLQueuedThread::QueuedRequest(handle, priority),
+ mWorkerClass(workerclass),
+ mParam(param)
+{
+}
+
+void LLWorkerThread::Request::deleteRequest()
+{
+ LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+//============================================================================
+// LLWorkerClass:: operates in main thread
+
+LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name)
+ : mWorkerThread(workerthread),
+ mWorkerClassName(name),
+ mWorkHandle(LLWorkerThread::nullHandle()),
+ mWorkFlags(0)
+{
+ if (!mWorkerThread)
+ {
+ mWorkerThread = LLWorkerThread::sLocal;
+ }
+}
+LLWorkerClass::~LLWorkerClass()
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
+ if (!workreq)
+ {
+ llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
+ }
+ if (workreq->getStatus() != LLWorkerThread::STATUS_ABORT &&
+ workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
+ workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
+ {
+ llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
+ }
+ }
+}
+
+void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl;
+ }
+ mWorkerThread = workerthread;
+}
+
+//----------------------------------------------------------------------------
+
+bool LLWorkerClass::yield()
+{
+ llassert(mWorkFlags & WCF_WORKING);
+ LLThread::yield();
+ mWorkerThread->checkPause();
+ return (getFlags() & WCF_ABORT_REQUESTED) ? true : false;
+}
+
+//----------------------------------------------------------------------------
+
+// calls startWork, adds doWork() to queue
+void LLWorkerClass::addWork(S32 param, U32 priority)
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl;
+ }
+#if _DEBUG
+// llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl;
+#endif
+ startWork(param);
+ mWorkHandle = mWorkerThread->add(this, param, priority);
+}
+
+void LLWorkerClass::abortWork()
+{
+#if _DEBUG
+// LLWorkerThread::Request* workreq = mWorkerThread->getRequest(mWorkHandle);
+// if (workreq)
+// llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
+#endif
+ mWorkerThread->abortRequest(mWorkHandle);
+ setFlags(WCF_ABORT_REQUESTED);
+}
+
+// if doWork is complete or aborted, call endWork() and return true
+bool LLWorkerClass::checkWork()
+{
+ bool complete = false, abort = false;
+ LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
+ llassert(workreq);
+ if (getFlags(WCF_ABORT_REQUESTED) || workreq->getStatus() == LLWorkerThread::STATUS_ABORTED)
+ {
+ complete = true;
+ abort = true;
+ }
+ else if (workreq->getStatus() == LLWorkerThread::STATUS_COMPLETE)
+ {
+ complete = true;
+ }
+ if (complete)
+ {
+#if _DEBUG
+// llinfos << "endWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
+#endif
+ endWork(workreq->getParam(), abort);
+ mWorkerThread->completeRequest(mWorkHandle);
+ mWorkHandle = LLWorkerThread::nullHandle();
+ }
+ return complete;
+}
+
+void LLWorkerClass::killWork()
+{
+ if (haveWork())
+ {
+ abortWork();
+ bool paused = mWorkerThread->isPaused();
+ while (!checkWork())
+ {
+ mWorkerThread->updateQueue(0);
+ }
+ if (paused)
+ {
+ mWorkerThread->pause();
+ }
+ }
+}
+
+void LLWorkerClass::setPriority(U32 priority)
+{
+ if (haveWork())
+ {
+ mWorkerThread->setPriority(mWorkHandle, priority);
+ }
+}
+
+//============================================================================
+
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
new file mode 100644
index 0000000000..bf5887e797
--- /dev/null
+++ b/indra/llcommon/llworkerthread.h
@@ -0,0 +1,168 @@
+/**
+ * @file llworkerthread.h
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORKERTHREAD_H
+#define LL_LLWORKERTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llqueuedthread.h"
+
+#define USE_FRAME_CALLBACK_MANAGER 0
+
+//============================================================================
+
+class LLWorkerClass;
+
+//============================================================================
+// Note: ~LLWorkerThread is O(N) N=# of worker threads, assumed to be small
+// It is assumed that LLWorkerThreads are rarely created/destroyed.
+
+class LLWorkerThread : public LLQueuedThread
+{
+public:
+ class Request : public LLQueuedThread::QueuedRequest
+ {
+ protected:
+ ~Request() {}; // use deleteRequest()
+
+ public:
+ Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
+
+ S32 getParam()
+ {
+ return mParam;
+ }
+ LLWorkerClass* getWorkerClass()
+ {
+ return mWorkerClass;
+ }
+
+ /*virtual*/ void deleteRequest();
+
+ private:
+ LLWorkerClass* mWorkerClass;
+ S32 mParam;
+ };
+
+public:
+ LLWorkerThread(bool threaded = true, bool runalways = true);
+ ~LLWorkerThread();
+
+protected:
+ /*virtual*/ bool processRequest(QueuedRequest* req);
+
+public:
+ handle_t add(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
+
+ static void initClass(bool local_is_threaded = true, bool local_run_always = true); // Setup sLocal
+ static S32 updateClass(U32 ms_elapsed);
+ static S32 getAllPending();
+ static void pauseAll();
+ static void waitOnAllPending();
+ static void cleanupClass(); // Delete sLocal
+
+public:
+ static LLWorkerThread* sLocal; // Default worker thread
+ static std::set<LLWorkerThread*> sThreadList; // array of threads (includes sLocal)
+};
+
+//============================================================================
+
+// This is a base class which any class with worker functions should derive from.
+// Example Usage:
+// LLMyWorkerClass* foo = new LLMyWorkerClass();
+// foo->fetchData(); // calls addWork()
+// while(1) // main loop
+// {
+// if (foo->hasData()) // calls checkWork()
+// foo->processData();
+// }
+//
+// WorkerClasses only have one set of work functions. If they need to do multiple
+// background tasks, use 'param' to switch amnong them.
+// Only one background task can be active at a time (per instance).
+// i.e. don't call addWork() if haveWork() returns true
+
+class LLWorkerClass
+{
+public:
+ typedef LLWorkerThread::handle_t handle_t;
+ enum FLAGS
+ {
+ WCF_WORKING = 0x01,
+ WCF_ABORT_REQUESTED = 0x80
+ };
+
+public:
+ LLWorkerClass(LLWorkerThread* workerthread, const std::string& name);
+ virtual ~LLWorkerClass();
+
+ // pure virtual, called from WORKER THREAD, returns TRUE if done
+ virtual bool doWork(S32 param)=0; // Called from LLWorkerThread::processRequest()
+
+ // called from WORKER THREAD
+ void setWorking(bool working) { working ? setFlags(WCF_WORKING) : clearFlags(WCF_WORKING); }
+
+ bool isWorking() { return getFlags(WCF_WORKING); }
+ bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
+
+ const std::string& getName() const { return mWorkerClassName; }
+
+protected:
+ // Call from doWork only to avoid eating up cpu time.
+ // Returns true if work has been aborted
+ // yields the current thread and calls mWorkerThread->checkPause()
+ bool yield();
+
+ void setWorkerThread(LLWorkerThread* workerthread);
+
+ // addWork(): calls startWork, adds doWork() to queue
+ void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL);
+
+ // abortWork(): requests that work be aborted
+ void abortWork();
+
+ // checkWork(): if doWork is complete or aborted, call endWork() and return true
+ bool checkWork();
+
+ // haveWork(): return true if mWorkHandle != null
+ bool haveWork() { return mWorkHandle != LLWorkerThread::nullHandle(); }
+
+ // killWork(): aborts work and waits for the abort to process
+ void killWork();
+
+ // setPriority(): changes the priority of a request
+ void setPriority(U32 priority);
+
+private:
+ void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; }
+ void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; }
+ U32 getFlags() { return mWorkFlags; }
+ bool getFlags(U32 flags) { return mWorkFlags & flags ? true : false; }
+
+private:
+ // pure virtuals
+ virtual void startWork(S32 param)=0; // called from addWork() (MAIN THREAD)
+ virtual void endWork(S32 param, bool aborted)=0; // called from doWork() (MAIN THREAD)
+
+protected:
+ LLWorkerThread* mWorkerThread;
+ std::string mWorkerClassName;
+ handle_t mWorkHandle;
+
+private:
+ LLAtomicU32 mWorkFlags;
+};
+
+//============================================================================
+
+
+#endif // LL_LLWORKERTHREAD_H
diff --git a/indra/llcommon/metaclass.cpp b/indra/llcommon/metaclass.cpp
new file mode 100644
index 0000000000..29ad20e6b6
--- /dev/null
+++ b/indra/llcommon/metaclass.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file metaclass.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLMetaClass
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "metaclass.h"
+#include "metaproperty.h"
+#include "reflective.h"
+
+LLMetaClass::LLMetaClass()
+{
+}
+
+//virtual
+LLMetaClass::~LLMetaClass()
+{
+}
+
+const LLMetaProperty* LLMetaClass::findProperty(const std::string& name) const
+{
+ PropertyIterator iter = mProperties.find(name);
+ if(iter == mProperties.end())
+ {
+ return NULL;
+ }
+ return (*iter).second;
+}
+
+void LLMetaClass::addProperty(const LLMetaProperty* property)
+{
+ mProperties.insert(std::make_pair(property->getName(), property));
+}
+
+U32 LLMetaClass::getPropertyCount() const
+{
+ return mProperties.size();
+}
+
+LLMetaClass::PropertyIterator LLMetaClass::beginProperties() const
+{
+ return mProperties.begin();
+}
+
+LLMetaClass::PropertyIterator LLMetaClass::endProperties() const
+{
+ return mProperties.end();
+}
+
+bool LLMetaClass::isInstance(const LLReflective* object) const
+{
+ // TODO: Babbage: Search through super classes of objects MetaClass.
+ const LLMetaClass* object_meta_class = &(object->getMetaClass());
+ return (object_meta_class == this);
+}
+
diff --git a/indra/llcommon/metaclass.h b/indra/llcommon/metaclass.h
new file mode 100644
index 0000000000..3424cea087
--- /dev/null
+++ b/indra/llcommon/metaclass.h
@@ -0,0 +1,64 @@
+/**
+ * @file metaclass.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Reflective meta information describing a class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METACLASS_H
+#define LL_METACLASS_H
+
+#include <string>
+#include <map>
+
+#include "stdtypes.h"
+
+class LLReflective;
+class LLMetaProperty;
+class LLMetaMethod;
+class LLMetaClass
+{
+public:
+
+ LLMetaClass();
+ virtual ~LLMetaClass();
+
+ // Create instance of this MetaClass. NULL if class is abstract.
+ // Gives ownership of returned object.
+ // virtual LLReflective* create() const = 0;
+
+ // Returns named property or NULL.
+ const LLMetaProperty* findProperty(const std::string& name) const;
+
+ // Add property to metaclass. Takes ownership of given property.
+ void addProperty(const LLMetaProperty* property);
+
+ typedef std::map<std::string, const LLMetaProperty*>::const_iterator PropertyIterator;
+
+ U32 getPropertyCount() const;
+
+ PropertyIterator beginProperties() const;
+ PropertyIterator endProperties() const;
+
+ // Returns named property or NULL.
+ // const LLMetaMethod* findMethod(const std::string& name) const;
+
+ // Add method to metaclass. Takes ownership of given method.
+ // void addMethod(const LLMetaMethod* method);
+
+ // Find MetaClass by name. NULL if name is unknown.
+ // static LLMetaClass* findClass(const std::string& name);
+
+ // True if object is instance of this meta class.
+ bool isInstance(const LLReflective* object) const;
+
+private:
+
+ typedef std::map<std::string, const LLMetaProperty*> PropertyMap;
+ PropertyMap mProperties;
+};
+
+#endif // LL_METACLASS_H
diff --git a/indra/llcommon/metaclasst.h b/indra/llcommon/metaclasst.h
new file mode 100644
index 0000000000..8ae9016b57
--- /dev/null
+++ b/indra/llcommon/metaclasst.h
@@ -0,0 +1,42 @@
+/**
+ * @file metaclasst.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METACLASST_H
+#define LL_METACLASST_H
+
+#include "metaclass.h"
+
+template<class TObject>
+class LLMetaClassT : public LLMetaClass
+{
+ public:
+
+ virtual ~LLMetaClassT() {;}
+
+ static const LLMetaClassT& instance()
+ {
+ static const LLMetaClassT& instance = buildMetaClass();
+ return instance;
+ }
+
+ private:
+
+ static const LLMetaClassT& buildMetaClass()
+ {
+ LLMetaClassT& meta_class = *(new LLMetaClassT());
+ reflectProperties(meta_class);
+ return meta_class;
+ }
+
+ LLMetaClassT() {;}
+
+ static void reflectProperties(LLMetaClass&)
+ {
+ }
+};
+
+#endif // LL_METACLASST_H
diff --git a/indra/llcommon/metaproperty.cpp b/indra/llcommon/metaproperty.cpp
new file mode 100644
index 0000000000..befee61a8a
--- /dev/null
+++ b/indra/llcommon/metaproperty.cpp
@@ -0,0 +1,35 @@
+/**
+ * @file metaproperty.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLMetaProperty.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "metaproperty.h"
+#include "metaclass.h"
+
+LLMetaProperty::LLMetaProperty(const std::string& name, const LLMetaClass& object_class) :
+ mName(name), mObjectClass(object_class)
+{
+}
+
+//virtual
+LLMetaProperty::~LLMetaProperty()
+{
+}
+
+const LLMetaClass& LLMetaProperty::getObjectMetaClass() const
+{
+ return mObjectClass;
+}
+
+void LLMetaProperty::checkObjectClass(const LLReflective* object) const
+{
+ if(! mObjectClass.isInstance(object))
+ {
+ throw "class cast exception";
+ }
+}
diff --git a/indra/llcommon/metaproperty.h b/indra/llcommon/metaproperty.h
new file mode 100644
index 0000000000..be615f2c67
--- /dev/null
+++ b/indra/llcommon/metaproperty.h
@@ -0,0 +1,55 @@
+/**
+ * @file metaproperty.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Reflective meta information describing a property of a class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METAPROPERTY_H
+#define LL_METAPROPERTY_H
+
+#include "stdtypes.h"
+#include "llsd.h"
+#include "reflective.h"
+
+class LLMetaClass;
+class LLReflective;
+class LLMetaProperty
+{
+public:
+ LLMetaProperty(const std::string& name, const LLMetaClass& object_class);
+ virtual ~LLMetaProperty();
+
+ // Get property name.
+ const std::string& getName() const {return mName;}
+
+ // Get value of this property.
+ virtual const LLReflective* get(const LLReflective* object) const = 0;
+
+ // Set value of this property.
+ // virtual void set(LLReflective* object, const LLReflective* value) = 0;
+
+ // Get value of this property as LLSD. Default returns undefined LLSD.
+ virtual LLSD getLLSD(const LLReflective* object) const = 0;
+
+ // Get the MetaClass of legal values of this property.
+ // const LLMetaClass& getValueMetaClass();
+
+ // Get the meta class that this property is a member of.
+ const LLMetaClass& getObjectMetaClass() const;
+
+protected:
+
+ // Check object is instance of object class, throw exception if not.
+ void checkObjectClass(const LLReflective* object) const;
+
+private:
+
+ std::string mName;
+ const LLMetaClass& mObjectClass;
+};
+
+#endif // LL_METAPROPERTY_H
diff --git a/indra/llcommon/metapropertyt.h b/indra/llcommon/metapropertyt.h
new file mode 100644
index 0000000000..056e422221
--- /dev/null
+++ b/indra/llcommon/metapropertyt.h
@@ -0,0 +1,165 @@
+/**
+ * @file metapropertyt.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METAPROPERTYT_H
+#define LL_METAPROPERTYT_H
+
+#include "llsd.h"
+#include "llstring.h"
+#include "metaclasst.h"
+#include "metaproperty.h"
+#include "reflectivet.h"
+
+template<class TProperty>
+class LLMetaPropertyT : public LLMetaProperty
+{
+public:
+
+ virtual ~LLMetaPropertyT() {;}
+
+ // Get value of this property. Gives ownership of returned value.
+ virtual const LLReflective* get(const LLReflective* object) const
+ {
+ checkObjectClass(object);
+ return getProperty(object);
+ }
+
+ // Set value of this property.
+ /*virtual void set(LLReflective* object, const LLReflective* value)
+ {
+ // TODO: Babbage: Check types.
+ ref(object) = static_cast<const LLReflectiveT<TProperty>* >(value)->getValue();
+ }*/
+
+ // Get value of this property as LLSD.
+ virtual LLSD getLLSD(const LLReflective* object) const
+ {
+ return LLSD();
+ }
+
+protected:
+
+ LLMetaPropertyT(const std::string& name, const LLMetaClass& object_class) : LLMetaProperty(name, object_class) {;}
+
+ virtual const TProperty* getProperty(const LLReflective* object) const = 0;
+};
+
+template <>
+inline const LLReflective* LLMetaPropertyT<S32>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<std::string>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<LLString>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<LLUUID>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline LLSD LLMetaPropertyT<S32>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<std::string>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<LLString>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<LLUUID>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template<class TObject, class TProperty>
+class LLMetaPropertyTT : public LLMetaPropertyT<TProperty>
+{
+public:
+
+ LLMetaPropertyTT(const std::string& name, const LLMetaClass& object_class, TProperty (TObject::*property)) :
+ LLMetaPropertyT<TProperty>(name, object_class), mProperty(property) {;}
+
+protected:
+
+ // Get void* to property.
+ virtual const TProperty* getProperty(const LLReflective* object) const
+ {
+ const TObject* typed_object = static_cast<const TObject*>(object);
+ return &(typed_object->*mProperty);
+ };
+
+private:
+
+ TProperty (TObject::*mProperty);
+};
+
+template<class TObject, class TProperty>
+class LLMetaPropertyPtrTT : public LLMetaPropertyT<TProperty>
+{
+public:
+
+ LLMetaPropertyPtrTT(const std::string& name, const LLMetaClass& object_class, TProperty* (TObject::*property)) :
+ LLMetaPropertyT<TProperty>(name, object_class), mProperty(property) {;}
+
+protected:
+
+ // Get void* to property.
+ virtual const TProperty* getProperty(const LLReflective* object) const
+ {
+ const TObject* typed_object = static_cast<const TObject*>(object);
+ return typed_object->*mProperty;
+ };
+
+private:
+
+ TProperty* (TObject::*mProperty);
+};
+
+// Utility function to simplify the registration of members.
+template<class TObject, class TProperty>
+void reflectProperty(LLMetaClass& meta_class, const std::string& name, TProperty (TObject::*property))
+{
+ typedef LLMetaPropertyTT<TObject, TProperty> PropertyType;
+ const LLMetaProperty* meta_property = new PropertyType(name, meta_class, property);
+ meta_class.addProperty(meta_property);
+}
+
+// Utility function to simplify the registration of ptr properties.
+template<class TObject, class TProperty>
+void reflectPtrProperty(LLMetaClass& meta_class, const std::string& name, TProperty* (TObject::*property))
+{
+ typedef LLMetaPropertyPtrTT<TObject, TProperty> PropertyType;
+ const LLMetaProperty* meta_property = new PropertyType(name, meta_class, property);
+ meta_class.addProperty(meta_property);
+}
+
+#endif // LL_METAPROPERTYT_H
diff --git a/indra/llcommon/reflective.cpp b/indra/llcommon/reflective.cpp
new file mode 100644
index 0000000000..039a500575
--- /dev/null
+++ b/indra/llcommon/reflective.cpp
@@ -0,0 +1,20 @@
+/**
+ * @file reflective.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLReflective.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "reflective.h"
+
+LLReflective::LLReflective()
+{
+}
+
+//virtual
+LLReflective::~LLReflective()
+{
+}
diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h
new file mode 100644
index 0000000000..1e3501390e
--- /dev/null
+++ b/indra/llcommon/reflective.h
@@ -0,0 +1,24 @@
+/**
+ * @file reflective.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Interface that must be implemented by all reflective classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_REFLECTIVE_H
+#define LL_REFLECTIVE_H
+
+class LLMetaClass;
+class LLReflective
+{
+public:
+ LLReflective();
+ virtual ~LLReflective();
+
+ virtual const LLMetaClass& getMetaClass() const = 0;
+};
+
+#endif // LL_REFLECTIVE_H
diff --git a/indra/llcommon/reflectivet.h b/indra/llcommon/reflectivet.h
new file mode 100644
index 0000000000..e16b4386ed
--- /dev/null
+++ b/indra/llcommon/reflectivet.h
@@ -0,0 +1,30 @@
+/**
+ * @file reflectivet.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_REFLECTIVET_H
+#define LL_REFLECTIVET_H
+
+#include "reflective.h"
+
+template <class T>
+class LLReflectiveT : public LLReflective
+{
+public:
+
+ LLReflectiveT(const T& value) : mValue(value) {;}
+ virtual ~LLReflectiveT() {;}
+
+ virtual const LLMetaClass& getMetaClass() const {return LLMetaClassT<LLReflectiveT<T> >::instance();}
+
+ const T& getValue() const {return mValue;}
+
+private:
+
+ T mValue;
+};
+
+#endif
diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h
new file mode 100644
index 0000000000..a4771dfeb6
--- /dev/null
+++ b/indra/llcommon/roles_constants.h
@@ -0,0 +1,168 @@
+/**
+ * @file roles_constants.h
+ * @brief General Roles Constants
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_ROLES_CONSTANTS_H
+#define LL_ROLES_CONSTANTS_H
+
+// This value includes the everyone group.
+const S32 MAX_ROLES = 10;
+
+enum LLRoleMemberChangeType
+{
+ RMC_ADD,
+ RMC_REMOVE,
+ RMC_NONE
+};
+
+enum LLRoleChangeType
+{
+ RC_UPDATE_NONE,
+ RC_UPDATE_DATA,
+ RC_UPDATE_POWERS,
+ RC_UPDATE_ALL,
+ RC_CREATE,
+ RC_DELETE
+};
+
+//
+// Powers
+//
+
+// KNOWN HOLES:
+// bit 0x1 << 37 (GP_OBJECT_RETURN)
+
+// These powers were removed to make group roles simpler
+// bit 0x1 << 27 (GP_LAND_ALLOW_SCRIPTS)
+// bit 0x1 << 16 (GP_LAND_VIEW_OWNED)
+// bit 0x1 << 41 (GP_ACCOUNTING_VIEW)
+// bit 0x1 << 46 (GP_PROPOSAL_VIEW)
+
+const U64 GP_NO_POWERS = 0x0;
+const U64 GP_ALL_POWERS = 0xFFFFFFFFFFFFFFFFLL;
+
+// Membership
+const U64 GP_MEMBER_INVITE = 0x1 << 1; // Invite member
+const U64 GP_MEMBER_EJECT = 0x1 << 2; // Eject member from group
+const U64 GP_MEMBER_OPTIONS = 0x1 << 3; // Toggle "Open enrollment" and change "Signup Fee"
+const U64 GP_MEMBER_VISIBLE_IN_DIR = 0x1LL << 47;
+
+// Roles
+const U64 GP_ROLE_CREATE = 0x1 << 4; // Create new roles
+const U64 GP_ROLE_DELETE = 0x1 << 5; // Delete roles
+const U64 GP_ROLE_PROPERTIES = 0x1 << 6; // Change Role Names, Titles, and Descriptions (Of roles the user is in, only, or any role in group?)
+const U64 GP_ROLE_ASSIGN_MEMBER_LIMITED = 0x1 << 7; // Assign Member to a Role that the assigner is in
+const U64 GP_ROLE_ASSIGN_MEMBER = 0x1 << 8; // Assign Member to Role
+const U64 GP_ROLE_REMOVE_MEMBER = 0x1 << 9; // Remove Member from Role
+const U64 GP_ROLE_CHANGE_ACTIONS = 0x1 << 10; // Change actions a role can perform
+
+// Group Identity
+const U64 GP_GROUP_CHANGE_IDENTITY = 0x1 << 11; // Charter, insignia, 'Show In Group List', 'Publish on the web', 'Mature', all 'Show Member In Group Profile' checkboxes
+
+// Parcel Management
+const U64 GP_LAND_DEED = 0x1 << 12; // Deed Land and Buy Land for Group
+const U64 GP_LAND_RELEASE = 0x1 << 13; // Release Land (to Gov. Linden)
+const U64 GP_LAND_SET_SALE_INFO = 0x1 << 14; // Set for sale info (Toggle "For Sale", Set Price, Set Target, Toggle "Sell objects with the land")
+const U64 GP_LAND_DIVIDE_JOIN = 0x1 << 15; // Divide and Join Parcels
+
+// Parcel Identity
+const U64 GP_LAND_FIND_PLACES = 0x1 << 17; // Toggle "Show in Find Places" and Set Category.
+const U64 GP_LAND_CHANGE_IDENTITY = 0x1 << 18; // Change Parcel Identity: Parcel Name, Parcel Description, Snapshot, 'Publish on the web', and 'Mature' checkbox
+const U64 GP_LAND_SET_LANDING_POINT = 0x1 << 19; // Set Landing Point
+
+// Parcel Settings
+const U64 GP_LAND_CHANGE_MEDIA = 0x1 << 20; // Change Media Settings
+const U64 GP_LAND_EDIT = 0x1 << 21; // Toggle Edit Land
+const U64 GP_LAND_OPTIONS = 0x1 << 22; // Toggle Set Home Point, Fly, Outside Scripts, Create/Edit Objects, Landmark, and Damage checkboxes
+
+// Parcel Powers
+const U64 GP_LAND_ALLOW_EDIT_LAND = 0x1 << 23; // Bypass Edit Land Restriction
+const U64 GP_LAND_ALLOW_FLY = 0x1 << 24; // Bypass Fly Restriction
+const U64 GP_LAND_ALLOW_CREATE = 0x1 << 25; // Bypass Create/Edit Objects Restriction
+const U64 GP_LAND_ALLOW_LANDMARK = 0x1 << 26; // Bypass Landmark Restriction
+const U64 GP_LAND_ALLOW_SET_HOME = 0x1 << 28; // Bypass Set Home Point Restriction
+
+// Parcel Access
+const U64 GP_LAND_MANAGE_ALLOWED = 0x1 << 29; // Manage Allowed List
+const U64 GP_LAND_MANAGE_BANNED = 0x1 << 30; // Manage Banned List
+const U64 GP_LAND_MANAGE_PASSES = 0x1LL << 31; // Change Sell Pass Settings
+const U64 GP_LAND_ADMIN = 0x1LL << 32; // Eject and Freeze Users on the land
+
+// Parcel Content
+const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48; // Return objects on parcel that are owned by the group
+const U64 GP_LAND_RETURN_GROUP_SET = 0x1LL << 33; // Return objects on parcel that are set to group
+const U64 GP_LAND_RETURN_NON_GROUP = 0x1LL << 34; // Return objects on parcel that are not set to group
+// Select a power-bit based on an object's relationship to a parcel.
+const U64 GP_LAND_RETURN = GP_LAND_RETURN_GROUP_OWNED
+ | GP_LAND_RETURN_GROUP_SET
+ | GP_LAND_RETURN_NON_GROUP;
+const U64 GP_LAND_GARDENING = 0x1LL << 35; // Parcel Gardening - plant and move linden trees
+
+// Object Management
+const U64 GP_OBJECT_DEED = 0x1LL << 36; // Deed Object
+// HOLE -- 0x1LL << 37
+const U64 GP_OBJECT_MANIPULATE = 0x1LL << 38; // Manipulate Group Owned Objects (Move, Copy, Mod)
+const U64 GP_OBJECT_SET_SALE = 0x1LL << 39; // Set Group Owned Object for Sale
+
+// Accounting
+const U64 GP_ACCOUNTING_ACCOUNTABLE = 0x1LL << 40; // Pay Group Liabilities and Receive Group Dividends
+
+// Notices
+const U64 GP_NOTICES_SEND = 0x1LL << 42; // Send Notices
+const U64 GP_NOTICES_RECEIVE = 0x1LL << 43; // Receive Notices and View Notice History
+
+// Proposals
+const U64 GP_PROPOSAL_START = 0x1LL << 44; // Start Proposal
+const U64 GP_PROPOSAL_VOTE = 0x1LL << 45; // Vote on Proposal
+
+const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE
+ | GP_LAND_ALLOW_SET_HOME
+ | GP_NOTICES_RECEIVE
+ | GP_PROPOSAL_START
+ | GP_PROPOSAL_VOTE
+ ;
+
+const U64 GP_DEFAULT_OFFICER = GP_ACCOUNTING_ACCOUNTABLE
+ | GP_GROUP_CHANGE_IDENTITY
+ | GP_LAND_ADMIN
+ | GP_LAND_ALLOW_EDIT_LAND
+ | GP_LAND_ALLOW_FLY
+ | GP_LAND_ALLOW_CREATE
+ | GP_LAND_ALLOW_LANDMARK
+ | GP_LAND_ALLOW_SET_HOME
+ | GP_LAND_CHANGE_IDENTITY
+ | GP_LAND_CHANGE_MEDIA
+ | GP_LAND_DEED
+ | GP_LAND_DIVIDE_JOIN
+ | GP_LAND_EDIT
+ | GP_LAND_FIND_PLACES
+ | GP_LAND_GARDENING
+ | GP_LAND_MANAGE_ALLOWED
+ | GP_LAND_MANAGE_BANNED
+ | GP_LAND_MANAGE_PASSES
+ | GP_LAND_OPTIONS
+ | GP_LAND_RELEASE
+ | GP_LAND_RETURN_GROUP_OWNED
+ | GP_LAND_RETURN_GROUP_SET
+ | GP_LAND_RETURN_NON_GROUP
+ | GP_LAND_SET_LANDING_POINT
+ | GP_LAND_SET_SALE_INFO
+ | GP_MEMBER_EJECT
+ | GP_MEMBER_INVITE
+ | GP_MEMBER_OPTIONS
+ | GP_MEMBER_VISIBLE_IN_DIR
+ | GP_NOTICES_RECEIVE
+ | GP_NOTICES_SEND
+ | GP_OBJECT_DEED
+ | GP_OBJECT_MANIPULATE
+ | GP_OBJECT_SET_SALE
+ | GP_PROPOSAL_START
+ | GP_PROPOSAL_VOTE
+ | GP_ROLE_ASSIGN_MEMBER_LIMITED
+ | GP_ROLE_PROPERTIES
+ ;
+#endif
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
new file mode 100644
index 0000000000..12fb932a0a
--- /dev/null
+++ b/indra/llcommon/stdenums.h
@@ -0,0 +1,115 @@
+/**
+ * @file stdenums.h
+ * @brief Enumerations for indra.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STDENUMS_H
+#define LL_STDENUMS_H
+
+//----------------------------------------------------------------------------
+// DEPRECATED - create new, more specific files for shared enums/constants
+//----------------------------------------------------------------------------
+
+// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
+enum EDragAndDropType
+{
+ DAD_NONE = 0,
+ DAD_TEXTURE = 1,
+ DAD_SOUND = 2,
+ DAD_CALLINGCARD = 3,
+ DAD_LANDMARK = 4,
+ DAD_SCRIPT = 5,
+ DAD_CLOTHING = 6,
+ DAD_OBJECT = 7,
+ DAD_NOTECARD = 8,
+ DAD_CATEGORY = 9,
+ DAD_ROOT_CATEGORY = 10,
+ DAD_BODYPART = 11,
+ DAD_ANIMATION = 12,
+ DAD_GESTURE = 13,
+ DAD_COUNT = 14, // number of types in this enum
+};
+
+// Reasons for drags to be denied.
+// ordered by priority for multi-drag
+enum EAcceptance
+{
+ ACCEPT_POSTPONED, // we are asynchronously determining acceptance
+ ACCEPT_NO, // Uninformative, general purpose denial.
+ ACCEPT_NO_LOCKED, // Operation would be valid, but permissions are set to disallow it.
+ ACCEPT_YES_COPY_SINGLE, // We'll take a copy of a single item
+ ACCEPT_YES_SINGLE, // Accepted. OK to drag and drop single item here.
+ ACCEPT_YES_COPY_MULTI, // We'll take a copy of multiple items
+ ACCEPT_YES_MULTI // Accepted. OK to drag and drop multiple items here.
+};
+
+// This is used by the DeRezObject message to determine where to put
+// derezed tasks.
+enum EDeRezDestination
+{
+ DRD_SAVE_INTO_AGENT_INVENTORY = 0,
+ DRD_ACQUIRE_TO_AGENT_INVENTORY = 1, // try to leave copy in world
+ DRD_SAVE_INTO_TASK_INVENTORY = 2,
+ DRD_ATTACHMENT = 3,
+ DRD_TAKE_INTO_AGENT_INVENTORY = 4, // delete from world
+ DRD_FORCE_TO_GOD_INVENTORY = 5, // force take copy
+ DRD_TRASH = 6,
+ DRD_ATTACHMENT_TO_INV = 7,
+ DRD_ATTACHMENT_EXISTS = 8,
+ DRD_RETURN_TO_OWNER = 9, // back to owner's inventory
+ DRD_RETURN_TO_LAST_OWNER = 10, // deeded object back to last owner's inventory
+
+ DRD_COUNT = 11
+};
+
+
+// This is used by the return to owner code to determine the reason
+// that this object is being returned.
+enum EReturnReason
+{
+ RR_GENERIC = 0,
+ RR_SANDBOX = 1,
+ RR_PARCEL_OWNER = 2,
+ RR_PARCEL_AUTO = 3,
+ RR_PARCEL_FULL = 4,
+ RR_OFF_WORLD = 5,
+
+ RR_COUNT = 6
+};
+
+// This is used for filling in the first byte of the ExtraID field of
+// the ObjectProperties message.
+enum EObjectPropertiesExtraID
+{
+ OPEID_NONE = 0,
+ OPEID_ASSET_ID = 1,
+ OPEID_FROM_TASK_ID = 2,
+
+ OPEID_COUNT = 3
+};
+
+enum EAddPosition
+{
+ ADD_TOP,
+ ADD_SORTED,
+ ADD_BOTTOM
+};
+
+enum LLGroupChange
+{
+ GC_PROPERTIES,
+ GC_MEMBER_DATA,
+ GC_ROLE_DATA,
+ GC_ROLE_MEMBER_DATA,
+ GC_TITLES,
+ GC_ALL
+};
+
+//----------------------------------------------------------------------------
+// DEPRECATED - create new, more specific files for shared enums/constants
+//----------------------------------------------------------------------------
+
+#endif
diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h
new file mode 100644
index 0000000000..b898475683
--- /dev/null
+++ b/indra/llcommon/stdtypes.h
@@ -0,0 +1,89 @@
+/**
+ * @file stdtypes.h
+ * @brief Basic type declarations for cross platform compatibility.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_STDTYPES_H
+#define LL_STDTYPES_H
+
+#include <limits.h>
+#include <float.h>
+
+typedef signed char S8;
+typedef unsigned char U8;
+typedef signed short S16;
+typedef unsigned short U16;
+typedef signed int S32;
+typedef unsigned int U32;
+
+#if LL_WINDOWS
+// Windows wchar_t is 16-bit
+typedef U32 llwchar;
+#else
+typedef wchar_t llwchar;
+#endif
+
+#if LL_WINDOWS
+typedef signed __int64 S64;
+// probably should be 'hyper' or similiar
+#define S64L(a) (a)
+typedef unsigned __int64 U64;
+#define U64L(a) (a)
+#else
+typedef long long int S64;
+typedef long long unsigned int U64;
+#if LL_DARWIN || LL_LINUX
+#define S64L(a) (a##LL)
+#define U64L(a) (a##ULL)
+#endif
+#endif
+
+typedef float F32;
+typedef double F64;
+
+typedef S32 BOOL;
+typedef U8 KEY;
+typedef U32 MASK;
+typedef U32 TPACKETID;
+
+// Use #define instead of consts to avoid conversion headaches
+#define S8_MAX (SCHAR_MAX)
+#define U8_MAX (UCHAR_MAX)
+#define S16_MAX (SHRT_MAX)
+#define U16_MAX (USHRT_MAX)
+#define S32_MAX (INT_MAX)
+#define U32_MAX (UINT_MAX)
+#define F32_MAX (FLT_MAX)
+#define F64_MAX (DBL_MAX)
+
+#define S8_MIN (SCHAR_MIN)
+#define U8_MIN (0)
+#define S16_MIN (SHRT_MIN)
+#define U16_MIN (0)
+#define S32_MIN (INT_MIN)
+#define U32_MIN (0)
+#define F32_MIN (FLT_MIN)
+#define F64_MIN (DBL_MIN)
+
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+typedef U8 LLPCode;
+
+#if LL_LINUX && __GNUC__ <= 2
+typedef int intptr_t;
+#endif
+
+#endif
diff --git a/indra/llcommon/string_table.h b/indra/llcommon/string_table.h
new file mode 100644
index 0000000000..02ea4c34aa
--- /dev/null
+++ b/indra/llcommon/string_table.h
@@ -0,0 +1,8 @@
+/**
+ * @file string_table.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+#include "llstringtable.h"
diff --git a/indra/llcommon/timer.h b/indra/llcommon/timer.h
new file mode 100644
index 0000000000..e4b799ee30
--- /dev/null
+++ b/indra/llcommon/timer.h
@@ -0,0 +1,8 @@
+/**
+ * @file timer.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+#include "lltimer.h"
diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp
new file mode 100644
index 0000000000..2c8a214b01
--- /dev/null
+++ b/indra/llcommon/timing.cpp
@@ -0,0 +1,7 @@
+/**
+ * @file timing.cpp
+ * @brief This file will be deprecated in the future.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h
new file mode 100644
index 0000000000..f09fb0f92b
--- /dev/null
+++ b/indra/llcommon/timing.h
@@ -0,0 +1,26 @@
+/**
+ * @file timing.h
+ * @brief Cross-platform routines for doing timing.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TIMING_H
+#define LL_TIMING_H
+
+#include <time.h>
+
+#if LL_LINUX || LL_DARWIN
+# include <sys/time.h>
+#endif
+
+
+const F32 SEC_TO_MICROSEC = 1000000.f;
+const U64 SEC_TO_MICROSEC_U64 = 1000000;
+const U32 SEC_PER_DAY = 86400;
+
+// This is just a stub, implementation in lltimer.cpp. This file will be deprecated in the future.
+U64 totalTime(); // Returns current system time in microseconds
+
+#endif
diff --git a/indra/llcommon/u64.cpp b/indra/llcommon/u64.cpp
new file mode 100644
index 0000000000..c8b8bc4a28
--- /dev/null
+++ b/indra/llcommon/u64.cpp
@@ -0,0 +1,91 @@
+/**
+ * @file u64.cpp
+ * @brief Utilities to deal with U64s.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "u64.h"
+
+
+U64 str_to_U64(const char *str)
+{
+ U64 result = 0;
+ char *aptr = strpbrk(str,"0123456789");
+
+ if (!aptr)
+ {
+ llwarns << "str_to_U64: Bad string to U64 conversion attempt: format\n" << llendl;
+ }
+ else
+ {
+ while ((*aptr >= '0') && (*aptr <= '9'))
+ {
+ result = result*10 + (*aptr++ - '0');
+ }
+ }
+ return (result);
+}
+
+
+char* U64_to_str(U64 value, char* result, S32 result_size)
+{
+ U32 part1,part2,part3;
+
+ part3 = (U32)(value % (U64)10000000);
+
+ value /= 10000000;
+ part2 = (U32)(value % (U64)10000000);
+
+ value /= 10000000;
+ part1 = (U32)(value % (U64)10000000);
+
+ // three cases to avoid leading zeroes unless necessary
+
+ if (part1)
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u%07u%07u",
+ part1,part2,part3); /* Flawfinder: ignore */
+ }
+ else if (part2)
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u%07u",
+ part2,part3); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u",
+ part3); /* Flawfinder: ignore */
+ }
+ return (result);
+}
+
+F64 U64_to_F64(const U64 value)
+{
+ S64 top_bits = (S64)(value >> 1);
+ F64 result = (F64)top_bits;
+ result *= 2.f;
+ result += (U32)(value & 0x01);
+ return result;
+}
+
+U64 llstrtou64(const char* str, char** end, S32 base)
+{
+#ifdef LL_WINDOWS
+ return _strtoui64(str,end,base);
+#else
+ return strtoull(str,end,base);
+#endif
+}
diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h
new file mode 100644
index 0000000000..e659be189f
--- /dev/null
+++ b/indra/llcommon/u64.h
@@ -0,0 +1,19 @@
+/**
+ * @file u64.h
+ * @brief Utilities to deal with U64s.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_U64_H
+#define LL_U64_H
+
+U64 str_to_U64(const char* str);
+char* U64_to_str(U64 value, char* result, S32 result_size);
+
+F64 U64_to_F64(const U64 value);
+
+U64 llstrtou64(const char* str, char** end, S32 base);
+
+#endif