diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcommon |
Print done when done.
Diffstat (limited to 'indra/llcommon')
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 << "<"; + break; + case '>': + out << ">"; + break; + case '&': + out << "&"; + break; + case '\'': + out << "'"; + break; + case '"': + out << """; + 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 |